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:
Víctor Falcón 2026-07-07 06:51:04 +02:00
parent 80f99a9f75
commit 6cbcd2a202
2 changed files with 61 additions and 2 deletions

View File

@ -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');
});
});

View File

@ -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(