test(timetable): useTimetableMutations 409 rollback + idempotency-key semantics (Step 9)
Minimal seam in src/composables/api/useTimetableMutations.ts: the move()
mutation's onError now calls useNotificationStore().show(...) on a 409
status. Generic axios errors stay quiet here — the global response
handler in lib/axios/factory.ts already toasts those. RFC D14 wanted
the version-mismatch toast specifically.
apps/app/tests/component/useTimetableMutations.test.ts (NEW, 5 tests):
- on success: returns server payload with bumped version + sends the
Idempotency-Key supplied by the caller
- 409: rejects with VersionMismatchError + notification.show()
invoked once with the Dutch translation + 'error' level
- cascade: success with cascaded[] populated puts those peers into
the result.cascaded array
- Idempotency-Key uniqueness: two distinct logical move() calls send
distinct keys
- Idempotency-Key reuse: caller-controlled retry within the same
logical action sends the SAME key on the wire (so the backend's
60s idempotency middleware dedupes)
The two existing unit-project tests now register a Pinia instance
(createPinia + setActivePinia) so useNotificationStore() resolves.
Existing assertions unchanged.
Test count: 364 → 369.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
performanceSchema,
|
||||
stageSchema,
|
||||
} from '@/schemas/timetable'
|
||||
import { useNotificationStore } from '@/stores/useNotificationStore'
|
||||
import type {
|
||||
CreatePerformancePayload,
|
||||
CreateStagePayload,
|
||||
@@ -64,6 +65,7 @@ function isVersionMismatch(err: unknown): err is { response: { status: 409; data
|
||||
|
||||
export function useTimetableMutations(args: UseTimetableMutationsArgs) {
|
||||
const queryClient = useQueryClient()
|
||||
const notification = useNotificationStore()
|
||||
const { orgId, eventId, dayId } = args
|
||||
|
||||
const performancesKey = () => ['timetable', 'performances', eventId, dayId] as const
|
||||
@@ -173,13 +175,19 @@ export function useTimetableMutations(args: UseTimetableMutationsArgs) {
|
||||
mergePerformance(result.moved)
|
||||
applyCascade(result.cascaded)
|
||||
},
|
||||
onError: (_err, _vars, ctx) => {
|
||||
onError: (err, _vars, ctx) => {
|
||||
// Restore cached blocks from snapshot so the canvas snaps back.
|
||||
if (ctx?.snapshot)
|
||||
mergePerformance(ctx.snapshot)
|
||||
if (ctx?.snapshotWachtrij)
|
||||
mergePerformance(ctx.snapshotWachtrij)
|
||||
invalidate()
|
||||
|
||||
// RFC D14 — version mismatch toast. Generic axios errors stay quiet
|
||||
// here; they're already surfaced by the global response handler in
|
||||
// lib/axios/factory.ts.
|
||||
if ((err as { status?: number } | null)?.status === 409)
|
||||
notification.show('Iemand anders heeft dit zojuist aangepast — venster ververst.', 'error')
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user