fix(sync): show error instead of falsely claiming IN_SYNC when upload fails

When SYNC_IMPORT upload is rejected (e.g., payload too complex/large),
the sync status was incorrectly set to IN_SYNC, misleading users into
thinking their data was synced when it wasn't.

Changes:
- Check uploadResult.rejectedCount before setting IN_SYNC
- Set status to ERROR when upload has rejected operations
- Show alert dialog for payload size/complexity errors
- Add ERROR_PAYLOAD_TOO_LARGE translation key
- Add tests for rejected ops handling
This commit is contained in:
Johannes Millan 2026-01-10 18:38:49 +01:00
parent f9620d4f37
commit 4780f08333
4 changed files with 41 additions and 6 deletions

View file

@ -392,6 +392,41 @@ describe('SyncWrapperService', () => {
expect(mockProviderManager.setSyncStatus).not.toHaveBeenCalled();
});
it('should set ERROR and return HANDLED_ERROR when upload has rejected ops', async () => {
mockSyncService.uploadPendingOps.and.returnValue(
Promise.resolve({
uploadedCount: 0,
rejectedCount: 1,
piggybackedOps: [],
rejectedOps: [{ opId: 'test-op', error: 'Payload too complex (max depth 50)' }],
localWinOpsCreated: 0,
}),
);
const result = await service.sync();
expect(result).toBe('HANDLED_ERROR');
expect(mockProviderManager.setSyncStatus).toHaveBeenCalledWith('ERROR');
expect(mockProviderManager.setSyncStatus).not.toHaveBeenCalledWith('IN_SYNC');
});
it('should set ERROR for non-payload rejected ops', async () => {
mockSyncService.uploadPendingOps.and.returnValue(
Promise.resolve({
uploadedCount: 0,
rejectedCount: 1,
piggybackedOps: [],
rejectedOps: [{ opId: 'test-op', error: 'Some other rejection' }],
localWinOpsCreated: 0,
}),
);
const result = await service.sync();
expect(result).toBe('HANDLED_ERROR');
expect(mockProviderManager.setSyncStatus).toHaveBeenCalledWith('ERROR');
});
});
describe('Error handling', () => {

View file

@ -238,11 +238,9 @@ export class SyncWrapperService {
'SyncWrapperService: Upload rejected - payload too large/complex',
uploadResult.rejectedOps,
);
this._providerManager.setSyncStatus('SYNC_ERROR');
this._snackService.open({
msg: T.F.SYNC.S.ERROR_PAYLOAD_TOO_LARGE,
type: 'ERROR',
});
this._providerManager.setSyncStatus('ERROR');
// Use alert for maximum visibility - this is a critical error
alert(this._translateService.instant(T.F.SYNC.S.ERROR_PAYLOAD_TOO_LARGE));
return 'HANDLED_ERROR';
}
@ -251,7 +249,7 @@ export class SyncWrapperService {
'SyncWrapperService: Upload had rejected operations, not marking as IN_SYNC',
uploadResult.rejectedOps,
);
this._providerManager.setSyncStatus('SYNC_ERROR');
this._providerManager.setSyncStatus('ERROR');
return 'HANDLED_ERROR';
}

View file

@ -1099,6 +1099,7 @@ const T = {
REMOTE_DATA_TOO_OLD: 'F.SYNC.S.REMOTE_DATA_TOO_OLD',
STORAGE_QUOTA_EXCEEDED: 'F.SYNC.S.STORAGE_QUOTA_EXCEEDED',
STORAGE_RECOVERED_AFTER_COMPACTION: 'F.SYNC.S.STORAGE_RECOVERED_AFTER_COMPACTION',
ERROR_PAYLOAD_TOO_LARGE: 'F.SYNC.S.ERROR_PAYLOAD_TOO_LARGE',
SUCCESS_DOWNLOAD: 'F.SYNC.S.SUCCESS_DOWNLOAD',
SUCCESS_VIA_BUTTON: 'F.SYNC.S.SUCCESS_VIA_BUTTON',
UNKNOWN_ERROR: 'F.SYNC.S.UNKNOWN_ERROR',

View file

@ -1076,6 +1076,7 @@
"REMOTE_DATA_TOO_OLD": "Remote data is too old to be processed. Please update your remote app.",
"STORAGE_QUOTA_EXCEEDED": "Sync server storage is full. Your changes are NOT syncing. Please archive old tasks or upgrade your plan.",
"STORAGE_RECOVERED_AFTER_COMPACTION": "Storage was running low. Old data cleaned up successfully.",
"ERROR_PAYLOAD_TOO_LARGE": "Sync Error: Your data is too large to upload.\n\nThis happens when you have a lot of archived tasks. Your data was NOT synced.\n\nPlease contact support for assistance.",
"SUCCESS_DOWNLOAD": "Synced data from remote",
"SUCCESS_VIA_BUTTON": "Data successfully synced",
"UNKNOWN_ERROR": "Unknown Sync Error: {{err}}",