fix(ssp): route cancel/delete through runSubmit, surface the error (RB-20)
ApplicationsStore.cancel and AdminCasesStore.delete rolled an optimistic write back on failure but showed no message — a bare catch with no Result and no error channel (CQ-002). Both now call runSubmit and set a lastError signal on failure, mirroring createSubmitChangeRequest in the same folder. Each page renders the error with the existing app-alert atom, the same pattern brief.page.ts already uses for lastError. Added a spec file for ApplicationsStore (none existed) and extended AdminCasesStore's spec, each asserting the rollback AND the surfaced error. Verified both new assertions fail without the fix (an Edit undo/redo of the store method, not git checkout). Regenerated libs/shared/docs/behaviour-spec.mdx (gen:behaviour-spec) to pick up the new/renamed test names. Marked RB-20 done in 99-backlog.md and recorded the change in implementation/rb-20.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { SUBMIT_FAILED } from '@shared/application/submit';
|
||||
import { ApplicationsAdapter } from '@registratie/infrastructure/applications.adapter';
|
||||
import { AdminCasesStore } from './admin-cases.store';
|
||||
|
||||
@@ -43,7 +44,10 @@ describe('AdminCasesStore', () => {
|
||||
expect(s.tag === 'Success' && s.value.map((c) => c.id)).toEqual(['b']);
|
||||
});
|
||||
|
||||
it('rolls back the removal when the delete fails', async () => {
|
||||
// RB-20: a failed delete must not be silent — the row rolls back AND the store
|
||||
// surfaces the error the page renders. Before RB-20 this only rolled back
|
||||
// (bare `catch { this.state.set(before) }`), so `lastError()` stayed null forever.
|
||||
it('rolls back the removal and surfaces the error when the delete fails', async () => {
|
||||
const deleteAny = vi.fn().mockRejectedValue(new Error('boom'));
|
||||
const store = setup({ listAll: () => Promise.resolve([summary('a')]), deleteAny });
|
||||
await store.load();
|
||||
@@ -51,5 +55,24 @@ describe('AdminCasesStore', () => {
|
||||
await store.delete('a');
|
||||
const s = store.cases();
|
||||
expect(s.tag === 'Success' && s.value.map((c) => c.id)).toEqual(['a']); // reappears
|
||||
expect(store.lastError()).toBe(SUBMIT_FAILED);
|
||||
});
|
||||
|
||||
it('clears a stale error on the next delete attempt', async () => {
|
||||
const deleteAny = vi
|
||||
.fn()
|
||||
.mockRejectedValueOnce(new Error('boom'))
|
||||
.mockResolvedValueOnce(undefined);
|
||||
const store = setup({
|
||||
listAll: () => Promise.resolve([summary('a'), summary('b')]),
|
||||
deleteAny,
|
||||
});
|
||||
await store.load();
|
||||
|
||||
await store.delete('a');
|
||||
expect(store.lastError()).toBe(SUBMIT_FAILED);
|
||||
|
||||
await store.delete('b');
|
||||
expect(store.lastError()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user