test(timetable): add real-API contract fixtures as schema regression test (3 shape variants) (B6)

apps/app/tests/unit/schemas/timetableContractShape.test.ts (NEW, 5 tests):
  - base shape: one performance with stage assigned + full engagement
    (Bert's browser-tested sample, field-for-field). Asserts decimal-as-
    string contract on fee_amount/buma_percentage/vat_percentage AND
    enum-label wrapper on booking_status AND nested computed object.
  - parked shape: stage_id=null, stage=null (Wachtrij case)
  - multi-perf shape: two performances sharing engagement_id
    (RFC §D17 "Friday + Saturday under one combined deal")
  - sanity: individual performanceSchema parses each fixture element
  - regression guard: a payload with NUMBER fee_amount throws (locks
    out the pre-B5 bug class)

Every fixture spells out explicit `null` for the schema's nullable-but-
required fields (timestamps, notes, deal_breakdown) so the
nullable() vs optional() distinction is exercised, not glossed over.

Schema surface change to support the test:
  apps/app/src/schemas/timetable.ts now EXPORTS performanceArraySchema
  (previously a private const inside useTimetable.ts).
  apps/app/src/composables/api/useTimetable.ts imports the shared one
  instead of redeclaring it locally — single source of truth for the
  array shape consumers and tests share.

Test count: 397 → 402 (+5). Typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-09 22:47:24 +02:00
parent 1eee1f9415
commit bce3081cb2
3 changed files with 270 additions and 2 deletions

View File

@@ -5,7 +5,7 @@ import { z } from 'zod'
import { apiClient } from '@/lib/axios'
import {
artistEngagementSchema,
performanceSchema,
performanceArraySchema,
stageSchema,
} from '@/schemas/timetable'
import type {
@@ -15,7 +15,6 @@ import type {
} from '@/types/timetable'
const stageArraySchema = z.array(stageSchema)
const performanceArraySchema = z.array(performanceSchema)
/**
* RFC v0.2 §6.2 — read-side composables for the timetable canvas.

View File

@@ -190,6 +190,12 @@ export const performanceSchema = z.object({
deleted_at: z.string().nullable(),
})
/**
* Wire-shape of `GET …/performances` and the `cascaded[]` payload from
* `POST …/timetable/move`. Useful for contract regression tests.
*/
export const performanceArraySchema = z.array(performanceSchema)
export const moveTimetableSuccessSchema = z.object({
moved: performanceSchema,
cascaded: z.array(performanceSchema),