From 56543d5e565b6be48793a43d3f7633f97243d158 Mon Sep 17 00:00:00 2001 From: Nabi Ebrahimi Date: Tue, 22 Jul 2025 21:34:32 +0430 Subject: [PATCH 1/3] fix: unhold expense failure data --- src/libs/actions/IOU.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d880d5ff49af..4593316b3cba 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -11067,6 +11067,9 @@ function unholdRequest(transactionID: string, reportID: string) { value: { pendingAction: null, errors: getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericUnholdExpenseFailureMessage'), + comment: { + hold: transaction?.comment?.hold, + }, }, }, { From 2c9ee6f699d1a5447d4891e94e4b6ff3679c372a Mon Sep 17 00:00:00 2001 From: Nabi Ebrahimi Date: Thu, 24 Jul 2025 14:55:38 +0430 Subject: [PATCH 2/3] Added unit test for unholdRequest failure case --- tests/actions/IOUTest.ts | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 38baa1950a52..f458c0623728 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -42,6 +42,7 @@ import {clearAllRelatedReportActionErrors} from '@libs/actions/ReportActions'; import {subscribeToUserEvents} from '@libs/actions/User'; import {WRITE_COMMANDS} from '@libs/API/types'; import type {ApiCommand} from '@libs/API/types'; +import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils'; import {translateLocal} from '@libs/Localize'; import {rand64} from '@libs/NumberUtils'; import {getLoginsByAccountIDs} from '@libs/PersonalDetailsUtils'; @@ -4644,6 +4645,72 @@ describe('actions/IOU', () => { }); }); }); + + test('should rollback unhold request on API failure', () => { + const iouReport = buildOptimisticIOUReport(1, 2, 100, '1', 'USD'); + const transaction = buildOptimisticTransaction({ + transactionParams: { + amount: 100, + currency: 'USD', + reportID: iouReport.reportID, + }, + }); + + const transactionCollectionDataSet: TransactionCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: transaction, + }; + const iouAction: ReportAction = buildOptimisticIOUReportAction({ + type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, + amount: transaction.amount, + currency: transaction.currency, + comment: '', + participants: [], + transactionID: transaction.transactionID, + }); + const transactionThread = buildTransactionThread(iouAction, iouReport); + + const actions: OnyxInputValue = {[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction.reportActionID}`]: iouAction}; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${transactionThread.reportID}`]: transactionThread, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, + }; + const actionCollectionDataSet: ReportActionsCollectionDataSet = {[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`]: actions}; + const comment = 'hold reason'; + + return waitForBatchedUpdates() + .then(() => Onyx.multiSet({...reportCollectionDataSet, ...transactionCollectionDataSet, ...actionCollectionDataSet})) + .then(() => { + putOnHold(transaction.transactionID, comment, transactionThread.reportID); + return waitForBatchedUpdates(); + }) + .then(() => { + mockFetch.fail(); + mockFetch?.resume?.(); + unholdRequest(transaction.transactionID, transactionThread.reportID); + return waitForBatchedUpdates(); + }) + .then(() => { + return new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, + callback: (updatedTransaction) => { + if (!updatedTransaction) { + return; + } + + Onyx.disconnect(connection); + expect(updatedTransaction?.pendingAction).toBeFalsy(); + expect(updatedTransaction?.comment?.hold).not.toBeNull(); + expect(Object.values(updatedTransaction.errors ?? {})).toEqual( + Object.values(getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericUnholdExpenseFailureMessage') ?? {}), + ); + + resolve(); + }, + }); + }); + }); + }); }); describe('sendInvoice', () => { From dce13cacdac81f66132ae6d9cf7bd52dc521e888 Mon Sep 17 00:00:00 2001 From: Nabi Ebrahimi Date: Fri, 25 Jul 2025 10:05:42 +0430 Subject: [PATCH 3/3] applied requested changes --- tests/actions/IOUTest.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index f458c0623728..b2cd16e71ac4 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -4694,14 +4694,10 @@ describe('actions/IOU', () => { const connection = Onyx.connect({ key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, callback: (updatedTransaction) => { - if (!updatedTransaction) { - return; - } - Onyx.disconnect(connection); expect(updatedTransaction?.pendingAction).toBeFalsy(); - expect(updatedTransaction?.comment?.hold).not.toBeNull(); - expect(Object.values(updatedTransaction.errors ?? {})).toEqual( + expect(updatedTransaction?.comment?.hold).toBeTruthy(); + expect(Object.values(updatedTransaction?.errors ?? {})).toEqual( Object.values(getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericUnholdExpenseFailureMessage') ?? {}), );