mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-09-16 18:42:03 +00:00
fix(backup): don't let a stale export pause a reconnected backup
- An export now pauses a revoked integration only if its row is unchanged since the export read it, using the trigger-maintained updatedAt column as the row version. If the user reconnected in the meantime, the stale failure no longer disables the fresh credentials or asks the user to reconnect again. updateExportStatus now reports whether a row was written. - The backup status store ignores a response overtaken by a newer refresh, or by the status being flagged, cleared or set directly, so a slow response can't overwrite newer state. - Unit tests cover the whole integration repository, the guarded pause and stale status responses. Coverage thresholds are raised to the new baseline.
This commit is contained in:
@@ -104,6 +104,64 @@ describe('refreshBackupStatus', () => {
|
||||
expect(get(backupsNeedingReconnect)).toEqual(['google_drive']);
|
||||
});
|
||||
|
||||
function deferredResponse() {
|
||||
let resolve!: (response: Response) => void;
|
||||
const promise = new Promise<Response>((r) => (resolve = r));
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
const json = (body: unknown) => new Response(JSON.stringify(body));
|
||||
|
||||
it('ignores a response overtaken by a newer refresh', async () => {
|
||||
const slow = deferredResponse();
|
||||
fetchMock
|
||||
.mockReturnValueOnce(slow.promise)
|
||||
.mockResolvedValueOnce(json([{ provider: 'google_drive', enabled: true }]));
|
||||
|
||||
const first = refreshBackupStatus();
|
||||
await refreshBackupStatus();
|
||||
slow.resolve(json([{ provider: 'google_drive', enabled: false }]));
|
||||
await first;
|
||||
|
||||
expect(get(backupsNeedingReconnect)).toEqual([]);
|
||||
});
|
||||
|
||||
it('ignores a response that arrives after the status was flagged directly', async () => {
|
||||
const slow = deferredResponse();
|
||||
fetchMock.mockReturnValueOnce(slow.promise);
|
||||
|
||||
const pending = refreshBackupStatus();
|
||||
markReconnectNeeded(['dropbox']);
|
||||
slow.resolve(json([{ provider: 'dropbox', enabled: true }]));
|
||||
await pending;
|
||||
|
||||
expect(get(backupsNeedingReconnect)).toEqual(['dropbox']);
|
||||
});
|
||||
|
||||
it('ignores a response that arrives after the status was cleared', async () => {
|
||||
const slow = deferredResponse();
|
||||
fetchMock.mockReturnValueOnce(slow.promise);
|
||||
|
||||
const pending = refreshBackupStatus();
|
||||
clearBackupStatus();
|
||||
slow.resolve(json([{ provider: 'google_drive', enabled: false }]));
|
||||
await pending;
|
||||
|
||||
expect(get(backupsNeedingReconnect)).toEqual([]);
|
||||
});
|
||||
|
||||
it('ignores a response that arrives after the status was set from another source', async () => {
|
||||
const slow = deferredResponse();
|
||||
fetchMock.mockReturnValueOnce(slow.promise);
|
||||
|
||||
const pending = refreshBackupStatus();
|
||||
setBackupStatus([{ provider: 'google_drive', enabled: true }]);
|
||||
slow.resolve(json([{ provider: 'google_drive', enabled: false }]));
|
||||
await pending;
|
||||
|
||||
expect(get(backupsNeedingReconnect)).toEqual([]);
|
||||
});
|
||||
|
||||
it('keeps the last known status when the request fails', async () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
markReconnectNeeded(['dropbox']);
|
||||
|
||||
Reference in New Issue
Block a user