import { describe, expect, it } from 'vitest'; import { BulkPartsRequest, CreatePartRequest, PartListQuery, UpdatePartRequest } from './parts.js'; const mfgId = '11111111-1111-4111-8111-111111111111'; const binId = '22222222-2222-4222-8222-222222222222'; const modelId = '44444444-4444-4444-8444-444444444444'; describe('CreatePartRequest', () => { it('accepts a partModelId-based payload', () => { const r = CreatePartRequest.parse({ serialNumber: 'SN-1', partModelId: modelId, }); expect(r.serialNumber).toBe('SN-1'); }); it('accepts a manufacturerId + mpn payload (auto-upsert path)', () => { const r = CreatePartRequest.parse({ serialNumber: 'SN-1', manufacturerId: mfgId, mpn: 'MPN-1', }); expect(r.mpn).toBe('MPN-1'); }); it('rejects when neither partModelId nor (manufacturerId + mpn) is provided', () => { expect( CreatePartRequest.safeParse({ serialNumber: 'SN-1', manufacturerId: mfgId }).success, ).toBe(false); expect(CreatePartRequest.safeParse({ serialNumber: 'SN-1' }).success).toBe(false); }); it('rejects empty serial', () => { expect( CreatePartRequest.safeParse({ serialNumber: '', partModelId: modelId }).success, ).toBe(false); }); it('rejects negative price', () => { const res = CreatePartRequest.safeParse({ serialNumber: 'X', partModelId: modelId, price: -1, }); expect(res.success).toBe(false); }); it('caps tagIds at 32', () => { const tagIds = Array.from({ length: 33 }, () => '33333333-3333-4333-8333-333333333333'); expect( CreatePartRequest.safeParse({ serialNumber: 'X', partModelId: modelId, tagIds, }).success, ).toBe(false); }); }); describe('UpdatePartRequest', () => { it('requires at least one field', () => { expect(UpdatePartRequest.safeParse({}).success).toBe(false); }); it('accepts a single field', () => { expect(UpdatePartRequest.safeParse({ notes: 'hi' }).success).toBe(true); }); it('permits nullable binId to clear location', () => { const r = UpdatePartRequest.parse({ binId: null }); expect(r.binId).toBeNull(); }); it('permits nullable hostId to clear host assignment', () => { const r = UpdatePartRequest.parse({ hostId: null }); expect(r.hostId).toBeNull(); }); }); describe('PartListQuery', () => { it('defaults page=1, pageSize=20', () => { const r = PartListQuery.parse({}); expect(r.page).toBe(1); expect(r.pageSize).toBe(20); }); it('coerces string numbers from query strings', () => { const r = PartListQuery.parse({ page: '3', pageSize: '50' }); expect(r.page).toBe(3); expect(r.pageSize).toBe(50); }); it('clamps pageSize to the 100 max', () => { expect(PartListQuery.safeParse({ pageSize: '500' }).success).toBe(false); }); it('parses eolOnly from string and boolean', () => { expect(PartListQuery.parse({ eolOnly: 'true' }).eolOnly).toBe(true); expect(PartListQuery.parse({ eolOnly: 'false' }).eolOnly).toBe(false); expect(PartListQuery.parse({ eolOnly: true }).eolOnly).toBe(true); }); }); describe('BulkPartsRequest', () => { it('requires at least one mutation field', () => { expect(BulkPartsRequest.safeParse({ ids: [mfgId] }).success).toBe(false); }); it('accepts state mutation', () => { expect(BulkPartsRequest.safeParse({ ids: [mfgId], state: 'SPARE' }).success).toBe(true); }); it('accepts binId=null to unassign', () => { const r = BulkPartsRequest.parse({ ids: [mfgId], binId: null }); expect(r.binId).toBeNull(); }); it('accepts hostId=null to unassign', () => { const r = BulkPartsRequest.parse({ ids: [mfgId], hostId: null }); expect(r.hostId).toBeNull(); }); it('caps ids at 500', () => { const ids = Array.from({ length: 501 }, () => binId); expect(BulkPartsRequest.safeParse({ ids, state: 'SPARE' }).success).toBe(false); }); });