import { FirestoreSimple , Collection } from '../src' ; import { AdminFirestoreTestUtil , createRandomCollectionName } from './util' ; const util = new AdminFirestoreTestUtil ( ) ; const firestore = util . adminFirestore ; const collectionPath = util . collectionPath ; type TestDoc = { id : string ; title : string ; } ; describe ( 'transaction' , ( ) => { let firestoreSimple ; let txFirestoreSimple ; let dao ; let txDao ; beforeEach ( async ( ) => { firestoreSimple = new FirestoreSimple ( firestore ) ; txFirestoreSimple = new FirestoreSimple ( firestore ) ; dao = firestoreSimple . collection < TestDoc > ( { path : collectionPath } ) ; txDao = txFirestoreSimple . collection < TestDoc > ( { path : collectionPath } ) ; } ) ; afterAll ( async ( ) => { await util . deleteApps ( ) ; } ) ; afterEach ( async ( ) => { await util . deleteCollection ( ) ; } ) ; describe ( 'context.tx' , ( ) => { it ( 'should be undefined before transaction' , async ( ) => { expect ( txFirestoreSimple . context . tx ) . toBeUndefined ( ) ; } ) ; it ( 'should be assigned in transaction' , async ( ) => { await txFirestoreSimple . runTransaction ( async ( tx ) => { expect ( txFirestoreSimple . context . tx ) . toBe ( tx ) ; } ) ; } ) ; it ( 'should be undefined after transaction' , async ( ) => { await txFirestoreSimple . runTransaction ( async ( _tx ) => { expect ( txFirestoreSimple . context . tx ) . not . toBeUndefined ( ) ; } ) ; expect ( txFirestoreSimple . context . tx ) . toBeUndefined ( ) ; } ) ; it ( 'should be error nesting transaction' , async ( ) => { await txFirestoreSimple . runTransaction ( async ( _tx ) => { expect ( txFirestoreSimple . runTransaction ( async ( _tx ) => { dao . add ( { title : 'test' } ) ; } ) ) . rejects . toThrow ( ) ; } ) ; } ) ; it ( 'should be error runBatch in transaction' , async ( ) => { await txFirestoreSimple . runTransaction ( async ( _tx ) => { expect ( txFirestoreSimple . runBatch ( async ( _batch ) => { dao . add ( { title : 'test' } ) ; } ) ) . rejects . toThrow ( ) ; } ) ; } ) ; it ( 'should be undefined when throw some error in transaction' , async ( ) => { try { await txFirestoreSimple . runTransaction ( async ( _tx ) => { await txDao . add ( { title : undefined } as any ) ; } ) ; } catch { } expect ( txFirestoreSimple . context . tx ) . toBeUndefined ( ) ; } ) ; } ) ; describe ( 'Collection' , ( ) => { describe ( 'write method' , ( ) => { it ( 'set' , async ( ) => { const doc = { id : 'test1' , title : 'aaa' } ; const updatedDoc = { id : 'test1' , title : 'bbb' } ; await dao . set ( doc ) ; await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . set ( updatedDoc ) ; const outTxFetched = await dao . fetch ( doc . id ) ; expect ( outTxFetched ) . toEqual ( doc ) ; } ) ; const fetched = await dao . fetch ( doc . id ) ; expect ( fetched ) . toEqual ( updatedDoc ) ; } ) ; it ( 'delete' , async ( ) => { const doc = { id : 'test1' , title : 'aaa' } ; await dao . set ( doc ) ; await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . delete ( doc . id ) ; const outTxFetched = await dao . fetch ( doc . id ) ; expect ( outTxFetched ) . toEqual ( doc ) ; } ) ; const fetched = await dao . fetch ( doc . id ) ; expect ( fetched ) . toBeUndefined ( ) ; } ) ; it ( 'add' , async ( ) => { let newId ; const doc = { title : 'aaa' } ; await txFirestoreSimple . runTransaction ( async ( ) => { newId = await txDao . add ( doc ) ; const outTxFetched = await dao . fetch ( newId ) ; expect ( outTxFetched ) . toBeUndefined ( ) ; } ) ; if ( ! newId ) return ; const fetched = await dao . fetch ( newId ) ; expect ( fetched ) . not . toBeUndefined ( ) ; } ) ; it ( 'update' , async ( ) => { const updatedTitle = 'update' ; const doc = { id : 'test2' , title : 'aaa' } ; await dao . set ( doc ) ; await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . update ( { id : doc . id , title : updatedTitle } ) ; const outTxFetched = await dao . fetch ( doc . id ) ; expect ( outTxFetched ! . title ) . toEqual ( doc . title ) ; } ) ; const fetched = await dao . fetch ( doc . id ) ; expect ( fetched ! . title ) . toEqual ( updatedTitle ) ; } ) ; it ( 'should not be commited when throw error in runTransaction' , async ( ) => { const doc = { id : 'test1' , title : 'aaa' } ; const updatedDoc = { id : 'test1' , title : 'bbb' } ; await dao . set ( doc ) ; try { await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . set ( updatedDoc ) ; await txDao . set ( { id : 'test1' , title : undefined } as any ) ; } ) ; } catch { } const fetched = await dao . fetch ( doc . id ) ; expect ( fetched ) . toEqual ( doc ) ; } ) ; } ) ; describe ( 'read method' , ( ) => { it ( 'fetch after set in transaction should be error' , async ( ) => { const doc = { id : 'test1' , title : 'aaa' } ; const updatedDoc = { id : 'test1' , title : 'bbb' } ; await dao . set ( doc ) ; await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . set ( updatedDoc ) ; await expect ( txDao . fetch ( doc . id ) ) . rejects . toThrow ( 'Firestore transactions require all reads to be executed before all writes.' ) ; } ) ; } ) ; it ( 'fetchAll after set in transaction should be error' , async ( ) => { const doc = { id : 'test1' , title : 'aaa' } ; const updatedDoc = { id : 'test1' , title : 'bbb' } ; await dao . set ( doc ) ; await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . set ( updatedDoc ) ; await expect ( txDao . fetchAll ( ) ) . rejects . toThrow ( 'Firestore transactions require all reads to be executed before all writes.' ) ; } ) ; } ) ; it ( 'query after set in transaction should be error' , async ( ) => { const doc = { id : 'test1' , title : 'aaa' } ; const updatedDoc = { id : 'test1' , title : 'bbb' } ; await dao . set ( doc ) ; await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . set ( updatedDoc ) ; await expect ( txDao . where ( 'title' , '==' , 'bbb' ) . fetch ( ) ) . rejects . toThrow ( 'Firestore transactions require all reads to be executed before all writes.' ) ; } ) ; } ) ; } ) ; } ) ; describe ( 'Collection.context.tx' , ( ) => { const anotherCollectionPath = createRandomCollectionName ( ) ; let txAnotherDao ; beforeEach ( async ( ) => { txAnotherDao = txFirestoreSimple . collection < TestDoc > ( { path : anotherCollectionPath } ) ; } ) ; it ( 'each collections share same transaction context' , async ( ) => { await txFirestoreSimple . runTransaction ( async ( tx ) => { expect ( txDao . context . tx ) . toBe ( tx ) ; expect ( txAnotherDao . context . tx ) . toBe ( tx ) ; } ) ; } ) ; it ( 'transaction enables across each collections' , async ( ) => { const anotherDao = firestoreSimple . collection < TestDoc > ( { path : anotherCollectionPath } ) ; const doc = { id : 'test1' , title : 'aaa' } ; await dao . set ( doc ) ; const anotherDoc = { id : 'test1' , title : 'another' } ; await anotherDao . set ( anotherDoc ) ; const updatedDoc = { id : 'test1' , title : 'bbb' } ; const updatedAnotherDoc = { id : 'test1' , title : 'another_bbb' } ; await txFirestoreSimple . runTransaction ( async ( ) => { await txDao . set ( updatedDoc ) ; await txAnotherDao . set ( updatedAnotherDoc ) ; const outTxFetched = await dao . fetch ( doc . id ) ; expect ( outTxFetched ) . toEqual ( doc ) ; const outTxAnotherFetched = await anotherDao . fetch ( anotherDoc . id ) ; expect ( outTxAnotherFetched ) . toEqual ( anotherDoc ) ; } ) ; const fetched = await dao . fetch ( doc . id ) ; expect ( fetched ) . toEqual ( updatedDoc ) ; const anotherFetched = await anotherDao . fetch ( anotherDoc . id ) ; expect ( anotherFetched ) . toEqual ( updatedAnotherDoc ) ; } ) ; } ) ; } ) ;