@@ -7035,5 +7035,63 @@ describe('actions/Policy', () => {
70357035 apiWriteSpy . mockRestore ( ) ;
70367036 isIOUReportUsingReportSpy . mockRestore ( ) ;
70377037 } ) ;
7038+
7039+ it ( 'should clear iouReportID on the old DM chat optimistically and restore it on failure' , async ( ) => {
7040+ await Onyx . set ( ONYXKEYS . SESSION , { email : ESH_EMAIL , accountID : ESH_ACCOUNT_ID } ) ;
7041+ await waitForBatchedUpdates ( ) ;
7042+
7043+ const employeeAccountID = 400 ;
7044+ const iouReportOwnerEmail = 'employee@example.com' ;
7045+ const oldChatReportID = '901' ;
7046+ const movedIouReportID = '900' ;
7047+
7048+ const iouReport : Report = {
7049+ ...createRandomReport ( 1 , undefined ) ,
7050+ reportID : movedIouReportID ,
7051+ type : CONST . REPORT . TYPE . IOU ,
7052+ ownerAccountID : employeeAccountID ,
7053+ chatReportID : oldChatReportID ,
7054+ policyID : 'oldPolicyID' ,
7055+ currency : CONST . CURRENCY . USD ,
7056+ total : 1500 ,
7057+ } ;
7058+
7059+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . REPORT } ${ iouReport . reportID } ` , iouReport ) ;
7060+ await waitForBatchedUpdates ( ) ;
7061+
7062+ const isIOUReportUsingReportSpy = jest . spyOn ( ReportUtils , 'isIOUReportUsingReport' ) . mockReturnValue ( true ) ;
7063+ const apiWriteSpy = jest . spyOn ( require ( '@libs/API' ) , 'write' ) . mockImplementation ( ( ) => Promise . resolve ( ) ) ;
7064+
7065+ const mockTranslate = ( ( key : string ) => key ) as unknown as Parameters < typeof Policy . createWorkspaceFromIOUPayment > [ 8 ] ;
7066+ Policy . createWorkspaceFromIOUPayment ( iouReport , undefined , ESH_ACCOUNT_ID , ESH_EMAIL , iouReportOwnerEmail , undefined , CONST . CURRENCY . USD , undefined , mockTranslate , { } ) ;
7067+ await waitForBatchedUpdates ( ) ;
7068+
7069+ const writeOptions = apiWriteSpy . mock . calls . at ( 0 ) ?. at ( 2 ) as {
7070+ optimisticData ?: Array < { onyxMethod ?: string ; key ?: string ; value ?: Record < string , unknown > | null } > ;
7071+ failureData ?: Array < { onyxMethod ?: string ; key ?: string ; value ?: Record < string , unknown > | null } > ;
7072+ } ;
7073+
7074+ const oldChatKey = `${ ONYXKEYS . COLLECTION . REPORT } ${ oldChatReportID } ` ;
7075+ const optimisticOldChatUpdate = ( writeOptions ?. optimisticData ?? [ ] ) . find (
7076+ ( update ) => update . key === oldChatKey && ( update . value as { iouReportID ?: string | null } | null ) ?. iouReportID !== undefined ,
7077+ ) ;
7078+ const failureOldChatUpdate = ( writeOptions ?. failureData ?? [ ] ) . find (
7079+ ( update ) => update . key === oldChatKey && ( update . value as { iouReportID ?: string | null } | null ) ?. iouReportID !== undefined ,
7080+ ) ;
7081+
7082+ // Optimistic update should clear the dangling pointer so a fresh IOU report is built
7083+ // for the next expense in this DM (the moved report otherwise gets reused via getMoneyRequestInformation).
7084+ expect ( optimisticOldChatUpdate ) . toBeDefined ( ) ;
7085+ expect ( ( optimisticOldChatUpdate ?. value as { iouReportID ?: string | null } ) ?. iouReportID ) . toBeNull ( ) ;
7086+ expect ( ( optimisticOldChatUpdate ?. value as { hasOutstandingChildRequest ?: boolean } ) ?. hasOutstandingChildRequest ) . toBe ( false ) ;
7087+
7088+ // Failure rollback must restore the previous iouReportID so the DM chat is back to its prior state.
7089+ expect ( failureOldChatUpdate ) . toBeDefined ( ) ;
7090+ expect ( ( failureOldChatUpdate ?. value as { iouReportID ?: string | null } ) ?. iouReportID ) . toBe ( movedIouReportID ) ;
7091+ expect ( ( failureOldChatUpdate ?. value as { hasOutstandingChildRequest ?: boolean } ) ?. hasOutstandingChildRequest ) . toBe ( true ) ;
7092+
7093+ apiWriteSpy . mockRestore ( ) ;
7094+ isIOUReportUsingReportSpy . mockRestore ( ) ;
7095+ } ) ;
70387096 } ) ;
70397097} ) ;
0 commit comments