// Domain-layer form schemas: derived from the shared contract schemas, with // string-typed numeric inputs and toXxxRequest() mappers to the wire types. // Also serves as the resolution smoke test for the // '@proposal-system/api-contracts/schemas' subpath entrypoint. import { describe, it, expect } from 'vitest'; import { createProposalFormSchema, toCreateProposalRequest, MIN_SCOPE_LENGTH, } from '../proposals/schemas'; import { customerFormSchema, toCustomerRequest } from '../customers/schemas'; import { pricingLibraryItemFormSchema, toPricingLibraryItemRequest, } from '../pricingLibrary/schemas'; describe('createProposalFormSchema', () => { const valid = { workOrderNumber: 'WO-1001', poNumber: '', customerName: 'Acme Corp', customerAddress: '1 Main St, Islip, NY 11751', scopeOfWork: 'Replace rooftop HVAC unit and rebalance airflow', serviceCategory: 'HVAC' as const, priority: 'Standard' as const, notes: '', customCategory: '', }; it('accepts a complete form', () => { expect(createProposalFormSchema.safeParse(valid).success).toBe(true); }); it('rejects scope of work shorter than the minimum', () => { const result = createProposalFormSchema.safeParse({ ...valid, scopeOfWork: 'x'.repeat(MIN_SCOPE_LENGTH - 1), }); expect(result.success).toBe(false); }); it('requires customCategory when serviceCategory is Other', () => { expect(createProposalFormSchema.safeParse({ ...valid, serviceCategory: 'Other' }).success).toBe( false, ); expect( createProposalFormSchema.safeParse({ ...valid, serviceCategory: 'Other', customCategory: 'Roofing', }).success, ).toBe(true); }); it('maps to the wire request, prefixing notes with the custom category', () => { const request = toCreateProposalRequest({ ...valid, serviceCategory: 'Other', customCategory: 'Roofing', notes: 'existing note', }); expect(request.notes).toBe('[Category: Roofing]\nexisting note'); expect(request.poNumber).toBeUndefined(); expect(toCreateProposalRequest(valid).notes).toBeUndefined(); }); }); describe('customerFormSchema', () => { const valid = { name: 'Acme Corp', address: '1 Main St', contactEmail: '' }; it('accepts a form with empty email', () => { expect(customerFormSchema.safeParse(valid).success).toBe(true); }); it('rejects a malformed email but accepts a valid one', () => { expect(customerFormSchema.safeParse({ ...valid, contactEmail: 'nope' }).success).toBe(false); expect(customerFormSchema.safeParse({ ...valid, contactEmail: 'a@b.com' }).success).toBe(true); }); it('maps to addresses[] preserving extra existing addresses', () => { const request = toCustomerRequest({ name: ' Acme ', address: ' 2 New St ', contactEmail: '' }, [ '1 Old St', '3 Extra Ave', ]); // contactEmail is ALWAYS sent — empty string clears the stored email // (code-review finding, Phase 4: omission made emails impossible to clear). expect(request).toEqual({ name: 'Acme', addresses: ['2 New St', '3 Extra Ave'], contactEmail: '', }); }); }); describe('pricingLibraryItemFormSchema', () => { const valid = { title: 'Condenser coil cleaning', serviceCategory: 'HVAC' as const, unit: '', unitPrice: '', keywords: '', description: '', }; it('accepts empty unitPrice and rejects negatives/non-numbers', () => { expect(pricingLibraryItemFormSchema.safeParse(valid).success).toBe(true); expect(pricingLibraryItemFormSchema.safeParse({ ...valid, unitPrice: '-4' }).success).toBe( false, ); expect(pricingLibraryItemFormSchema.safeParse({ ...valid, unitPrice: 'abc' }).success).toBe( false, ); }); it('maps empty unitPrice to null and drops empty optional strings', () => { const request = toPricingLibraryItemRequest(valid); expect(request).toEqual({ title: 'Condenser coil cleaning', serviceCategory: 'HVAC', unitPrice: null, }); const priced = toPricingLibraryItemRequest({ ...valid, unitPrice: '12.50', unit: 'each' }); expect(priced.unitPrice).toBe(12.5); expect(priced.unit).toBe('each'); }); }); describe('shared numeric form fields', () => { it('rejects trailing garbage that parseFloat would silently coerce', () => { // Code-review finding (Phase 4): '12abc' passed the old parseFloat-based // pricing refine as 12; the shared Number-based field must reject it. expect( pricingLibraryItemFormSchema.safeParse({ title: 'Coil cleaning', serviceCategory: 'HVAC', unit: '', unitPrice: '12abc', keywords: '', description: '', }).success, ).toBe(false); }); });