import { toVoiceIntent, VOICE_INTENT_JSON_SCHEMA, type WireVoiceIntent, } from './extraction.wire'; const emptyDue = { kind: 'none' as const, weekday: null, which: null, unit: null, amount: null, jy: null, jm: null, jd: null, y: null, m: null, d: null, }; const wire = (overrides: Partial = {}): WireVoiceIntent => ({ treatmentType: null, teeth: [], connectedSpans: [], comment: null, prosthesisDefaultType: null, prosthesisOverrides: [], labId: null, labMatchExact: false, due: emptyDue, ...overrides, }); const positionalTooth = { spoken: 'شش بالا راست', fdi: null, arch: 'upper' as const, side: 'patient_right' as const, position: 6, }; describe('VOICE_INTENT_JSON_SCHEMA', () => { it('is strict — every property required, no extras', () => { expect(VOICE_INTENT_JSON_SCHEMA.additionalProperties).toBe(false); expect([...VOICE_INTENT_JSON_SCHEMA.required].sort()).toEqual( Object.keys(VOICE_INTENT_JSON_SCHEMA.properties).sort(), ); }); it('requires every field of the due object, since strict mode allows no optionals', () => { const due = VOICE_INTENT_JSON_SCHEMA.properties.due; expect([...due.required].sort()).toEqual( Object.keys(due.properties).sort(), ); }); }); describe('toVoiceIntent', () => { it('narrows a positional tooth', () => { const result = toVoiceIntent(wire({ teeth: [positionalTooth] })); expect(result.teeth[0]).toEqual({ kind: 'positional', arch: 'upper', side: 'patient_right', position: 6, spoken: 'شش بالا راست', }); }); it('narrows an explicit FDI tooth, which wins over positional fields', () => { const result = toVoiceIntent( wire({ teeth: [{ ...positionalTooth, fdi: '14', spoken: 'یک چهار' }] }), ); expect(result.teeth[0]).toEqual({ kind: 'explicit', fdi: '14', spoken: 'یک چهار', }); }); it('maps due.kind "none" to no deadline', () => { expect(toVoiceIntent(wire()).due).toBeNull(); }); it('narrows each due kind', () => { expect( toVoiceIntent( wire({ due: { ...emptyDue, kind: 'weekday', weekday: 'thursday', which: 'next', }, }), ).due, ).toEqual({ kind: 'weekday', weekday: 'thursday', which: 'next' }); expect( toVoiceIntent( wire({ due: { ...emptyDue, kind: 'offset', unit: 'week', amount: 2 } }), ).due, ).toEqual({ kind: 'offset', unit: 'week', amount: 2 }); expect( toVoiceIntent( wire({ due: { ...emptyDue, kind: 'jalali', jy: 1404, jm: 7, jd: 25 } }), ).due, ).toEqual({ kind: 'jalali', jy: 1404, jm: 7, jd: 25 }); expect( toVoiceIntent( wire({ due: { ...emptyDue, kind: 'gregorian', y: 2025, m: 10, d: 17 }, }), ).due, ).toEqual({ kind: 'gregorian', y: 2025, m: 10, d: 17 }); }); it('ignores a non-FDI-shaped fdi and keeps the positional fields', () => { // A model emitting fdi:"6" alongside correct arch/side/position must still yield 16, // not lose the tooth to the explicit branch. const result = toVoiceIntent( wire({ teeth: [{ ...positionalTooth, fdi: '6' }] }), ); expect(result.teeth[0]).toEqual({ kind: 'positional', arch: 'upper', side: 'patient_right', position: 6, spoken: 'شش بالا راست', }); }); it('rejects impossible FDI shapes from the explicit branch', () => { for (const fdi of ['99', '0', '140', '9', 'ab']) { expect( toVoiceIntent(wire({ teeth: [{ ...positionalTooth, fdi }] })).teeth[0] .kind, ).toBe('positional'); } }); it('passes an unrecognised due kind through so it can be flagged', () => { // Collapsing it to null would make a misunderstood deadline indistinguishable from // no deadline at all, and the resolver's flagging path unreachable. const result = toVoiceIntent( wire({ due: { ...emptyDue, kind: 'lunar_month' as never } }), ); expect(result.due).toEqual({ kind: 'lunar_month' }); }); it('reports no prosthesis when neither a default nor an override was given', () => { expect(toVoiceIntent(wire()).prosthesis).toBeNull(); }); it('builds a prosthesis intent from a default alone', () => { const result = toVoiceIntent(wire({ prosthesisDefaultType: 'pfm_crown' })); expect(result.prosthesis).toEqual({ defaultType: 'pfm_crown', overrides: [], }); }); it('builds a prosthesis intent from overrides alone', () => { const result = toVoiceIntent( wire({ prosthesisOverrides: [{ tooth: positionalTooth, type: 'pfm_crown' }], }), ); expect(result.prosthesis?.defaultType).toBeNull(); expect(result.prosthesis?.overrides).toHaveLength(1); }); it('narrows connected spans', () => { const result = toVoiceIntent( wire({ connectedSpans: [ { from: { ...positionalTooth, fdi: '14' }, to: { ...positionalTooth, fdi: '16' }, }, ], }), ); expect(result.connectedSpans[0].from).toEqual({ kind: 'explicit', fdi: '14', spoken: 'شش بالا راست', }); }); it('is total — a missing or malformed payload yields a resolvable shape, not a throw', () => { // Whatever survives here is reported as unresolved downstream rather than crashing. for (const bad of [undefined, null, {}, { teeth: 'nope', due: 5 }]) { expect(() => toVoiceIntent(bad as never)).not.toThrow(); const result = toVoiceIntent(bad as never); expect(result.teeth).toEqual([]); expect(result.due).toBeNull(); expect(result.labMatchExact).toBe(false); } }); it('coerces a non-boolean labMatchExact to false', () => { expect( toVoiceIntent(wire({ labMatchExact: 'yes' as never })).labMatchExact, ).toBe(false); }); });