From 27d438929b5eed5bf4ced19207e5e6d16ed9324e Mon Sep 17 00:00:00 2001 From: Matt Ciaccio Date: Thu, 23 Apr 2026 23:35:30 +0200 Subject: [PATCH] refactor(yachts): rename schema + consolidate tests per project conventions --- .claude/scheduled_tasks.lock | 1 + client-portal | 2 +- src/lib/validators/yachts.ts | 4 +- tests/unit/validators.test.ts | 86 +++++++++++++++++++++++++--- tests/unit/validators/yachts.test.ts | 49 ---------------- 5 files changed, 83 insertions(+), 59 deletions(-) create mode 100644 .claude/scheduled_tasks.lock delete mode 100644 tests/unit/validators/yachts.test.ts diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 0000000..5c4aca1 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"fd05cbd7-d695-4a70-9223-4b25f3369829","pid":88534,"acquiredAt":1776866083076} \ No newline at end of file diff --git a/client-portal b/client-portal index e2d3181..84f89f9 160000 --- a/client-portal +++ b/client-portal @@ -1 +1 @@ -Subproject commit e2d31815cf45fdcca9d36a5eb83c9038ba7d6057 +Subproject commit 84f89f9409c9510d628585b69c3a1acf3c9d8e75 diff --git a/src/lib/validators/yachts.ts b/src/lib/validators/yachts.ts index 6ea2970..cb4d1a0 100644 --- a/src/lib/validators/yachts.ts +++ b/src/lib/validators/yachts.ts @@ -39,7 +39,7 @@ export const transferOwnershipSchema = z.object({ transferNotes: z.string().optional(), }); -export const listYachtsQuery = baseListQuerySchema.extend({ +export const listYachtsSchema = baseListQuerySchema.extend({ ownerType: z.enum(['client', 'company']).optional(), ownerId: z.string().optional(), status: z.enum(['active', 'retired', 'sold_away']).optional(), @@ -49,4 +49,4 @@ export const listYachtsQuery = baseListQuerySchema.extend({ export type CreateYachtInput = z.infer; export type UpdateYachtInput = z.infer; export type TransferOwnershipInput = z.infer; -export type ListYachtsInput = z.infer; +export type ListYachtsInput = z.infer; diff --git a/tests/unit/validators.test.ts b/tests/unit/validators.test.ts index de2a3f6..8c4caea 100644 --- a/tests/unit/validators.test.ts +++ b/tests/unit/validators.test.ts @@ -1,10 +1,11 @@ import { describe, it, expect } from 'vitest'; import { createClientSchema, updateClientSchema } from '@/lib/validators/clients'; -import { createInterestSchema, updateInterestSchema, changeStageSchema } from '@/lib/validators/interests'; +import { createInterestSchema, changeStageSchema } from '@/lib/validators/interests'; import { updateBerthSchema, updateBerthStatusSchema } from '@/lib/validators/berths'; import { createInvoiceSchema } from '@/lib/validators/invoices'; import { createWebhookSchema, updateWebhookSchema } from '@/lib/validators/webhooks'; import { createFieldSchema, updateFieldSchema } from '@/lib/validators/custom-fields'; +import { createYachtSchema, transferOwnershipSchema } from '@/lib/validators/yachts'; // ─── Client schemas ─────────────────────────────────────────────────────────── @@ -92,12 +93,24 @@ describe('createInterestSchema', () => { }); it('rejects invalid pipelineStage', () => { - const result = createInterestSchema.safeParse({ clientId: 'c1', pipelineStage: 'unknown_stage' }); + const result = createInterestSchema.safeParse({ + clientId: 'c1', + pipelineStage: 'unknown_stage', + }); expect(result.success).toBe(false); }); it('accepts all valid pipeline stages', () => { - const stages = ['open', 'details_sent', 'in_communication', 'visited', 'signed_eoi_nda', 'deposit_10pct', 'contract', 'completed']; + const stages = [ + 'open', + 'details_sent', + 'in_communication', + 'visited', + 'signed_eoi_nda', + 'deposit_10pct', + 'contract', + 'completed', + ]; for (const stage of stages) { const result = createInterestSchema.safeParse({ clientId: 'c1', pipelineStage: stage }); expect(result.success, `stage "${stage}" should be valid`).toBe(true); @@ -138,11 +151,15 @@ describe('updateBerthSchema', () => { describe('updateBerthStatusSchema', () => { it('accepts valid status with reason', () => { - expect(updateBerthStatusSchema.safeParse({ status: 'available', reason: 'Freed up' }).success).toBe(true); + expect( + updateBerthStatusSchema.safeParse({ status: 'available', reason: 'Freed up' }).success, + ).toBe(true); }); it('rejects invalid status', () => { - expect(updateBerthStatusSchema.safeParse({ status: 'occupied', reason: 'reason' }).success).toBe(false); + expect( + updateBerthStatusSchema.safeParse({ status: 'occupied', reason: 'reason' }).success, + ).toBe(false); }); it('rejects missing reason', () => { @@ -220,7 +237,10 @@ describe('createWebhookSchema', () => { }); it('rejects http URL (must be HTTPS)', () => { - const result = createWebhookSchema.safeParse({ ...validWebhook, url: 'http://example.com/hook' }); + const result = createWebhookSchema.safeParse({ + ...validWebhook, + url: 'http://example.com/hook', + }); expect(result.success).toBe(false); if (!result.success) { const messages = result.error.issues.map((i) => i.message); @@ -288,7 +308,10 @@ describe('createFieldSchema', () => { }); it('rejects fieldName with spaces', () => { - const result = createFieldSchema.safeParse({ ...validTextField, fieldName: 'preferred marina' }); + const result = createFieldSchema.safeParse({ + ...validTextField, + fieldName: 'preferred marina', + }); expect(result.success).toBe(false); }); @@ -343,3 +366,52 @@ describe('updateFieldSchema', () => { // it cannot be used to mutate fieldType. }); }); + +// ─── Yacht schemas ──────────────────────────────────────────────────────────── + +describe('createYachtSchema', () => { + it('rejects empty name', () => { + const result = createYachtSchema.safeParse({ + name: '', + owner: { type: 'client', id: 'c1' }, + }); + expect(result.success).toBe(false); + }); + + it('requires owner', () => { + const result = createYachtSchema.safeParse({ name: 'Sea Breeze' }); + expect(result.success).toBe(false); + }); + + it('rejects invalid yearBuilt', () => { + const result = createYachtSchema.safeParse({ + name: 'Sea Breeze', + owner: { type: 'client', id: 'c1' }, + yearBuilt: 1700, + }); + expect(result.success).toBe(false); + }); + + it('accepts minimal valid input', () => { + const result = createYachtSchema.safeParse({ + name: 'Sea Breeze', + owner: { type: 'client', id: 'c1' }, + }); + expect(result.success).toBe(true); + }); +}); + +describe('transferOwnershipSchema', () => { + it('requires newOwner + effectiveDate', () => { + expect(transferOwnershipSchema.safeParse({}).success).toBe(false); + }); + + it('accepts valid input', () => { + const result = transferOwnershipSchema.safeParse({ + newOwner: { type: 'company', id: 'co1' }, + effectiveDate: new Date(), + transferReason: 'sale', + }); + expect(result.success).toBe(true); + }); +}); diff --git a/tests/unit/validators/yachts.test.ts b/tests/unit/validators/yachts.test.ts deleted file mode 100644 index 81424d7..0000000 --- a/tests/unit/validators/yachts.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { createYachtSchema, transferOwnershipSchema } from '@/lib/validators/yachts'; - -describe('createYachtSchema', () => { - it('rejects empty name', () => { - const result = createYachtSchema.safeParse({ - name: '', - owner: { type: 'client', id: 'c1' }, - }); - expect(result.success).toBe(false); - }); - - it('requires owner', () => { - const result = createYachtSchema.safeParse({ name: 'Sea Breeze' }); - expect(result.success).toBe(false); - }); - - it('rejects invalid yearBuilt', () => { - const result = createYachtSchema.safeParse({ - name: 'Sea Breeze', - owner: { type: 'client', id: 'c1' }, - yearBuilt: 1700, - }); - expect(result.success).toBe(false); - }); - - it('accepts minimal valid input', () => { - const result = createYachtSchema.safeParse({ - name: 'Sea Breeze', - owner: { type: 'client', id: 'c1' }, - }); - expect(result.success).toBe(true); - }); -}); - -describe('transferOwnershipSchema', () => { - it('requires newOwner + effectiveDate', () => { - expect(transferOwnershipSchema.safeParse({}).success).toBe(false); - }); - - it('accepts valid input', () => { - const result = transferOwnershipSchema.safeParse({ - newOwner: { type: 'company', id: 'co1' }, - effectiveDate: new Date(), - transferReason: 'sale', - }); - expect(result.success).toBe(true); - }); -});