fix(sync): skip the local cache eviction on delete when IndexedDB is missing
The API delete is authoritative and must always run; only the best-effort local cache eviction is routed through withDb so it no-ops when IndexedDB is unavailable, instead of throwing after a successful server delete. axios stays outside the guard so no mutation is ever swallowed.
This commit is contained in:
parent
80f99a9f75
commit
6cbcd2a202
|
|
@ -0,0 +1,55 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { transactionSyncService } from './transaction-sync';
|
||||
|
||||
const dbMock = vi.hoisted(() => ({
|
||||
transactions: {
|
||||
delete: vi.fn(async () => undefined),
|
||||
},
|
||||
sync_metadata: { delete: vi.fn(), get: vi.fn(), put: vi.fn() },
|
||||
}));
|
||||
|
||||
const axiosMock = vi.hoisted(() => ({
|
||||
delete: vi.fn(async () => ({ data: {} })),
|
||||
}));
|
||||
|
||||
// Keep the real withDb (reads globalThis live); swap only the Dexie-backed db.
|
||||
vi.mock('@/lib/dexie-db', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/lib/dexie-db')>();
|
||||
return { ...actual, db: dbMock };
|
||||
});
|
||||
|
||||
vi.mock('axios', () => ({ default: axiosMock }));
|
||||
|
||||
describe('transactionSyncService.delete', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('deletes via the API but skips the cache eviction when IndexedDB is missing', async () => {
|
||||
vi.stubGlobal('indexedDB', undefined);
|
||||
|
||||
await expect(
|
||||
transactionSyncService.delete('txn-1'),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(axiosMock.delete).toHaveBeenCalledWith('/transactions/txn-1', {
|
||||
data: undefined,
|
||||
});
|
||||
expect(dbMock.transactions.delete).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('deletes via the API and evicts the cache when IndexedDB is available', async () => {
|
||||
vi.stubGlobal('indexedDB', {} as IDBFactory);
|
||||
|
||||
await transactionSyncService.delete('txn-1');
|
||||
|
||||
expect(axiosMock.delete).toHaveBeenCalledWith('/transactions/txn-1', {
|
||||
data: undefined,
|
||||
});
|
||||
expect(dbMock.transactions.delete).toHaveBeenCalledWith('txn-1');
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { db } from '@/lib/dexie-db';
|
||||
import { db, withDb } from '@/lib/dexie-db';
|
||||
import { TransactionSyncManager } from '@/lib/sync-manager';
|
||||
import type { LearnedRuleNotice } from '@/types/automation-rule';
|
||||
import type { Transaction } from '@/types/transaction';
|
||||
|
|
@ -192,7 +192,11 @@ class TransactionSyncService {
|
|||
await axios.delete(`/transactions/${id}`, {
|
||||
data: options?.updateBalance ? { update_balance: true } : undefined,
|
||||
});
|
||||
await db.transactions.delete(id);
|
||||
// The API delete above is authoritative; the local cache eviction is
|
||||
// best-effort and skipped when IndexedDB is unavailable (PHP-LARAVEL-43).
|
||||
await withDb<void>(async () => {
|
||||
await db.transactions.delete(id);
|
||||
}, undefined);
|
||||
}
|
||||
|
||||
async updateManyIndividual(
|
||||
|
|
|
|||
Loading…
Reference in New Issue