/** * Match-candidates API — integration test. * * Exercises the GET /api/v1/clients/match-candidates handler against a * real port + clients pool. Verifies the dedup library's at-create * suggestion path returns the right candidates and confidence tiers * for the "use existing client?" form interruption. */ import { describe, expect, it } from 'vitest'; import { db } from '@/lib/db'; import { clientContacts } from '@/lib/db/schema/clients'; import { getMatchCandidatesHandler } from '@/app/api/v1/clients/match-candidates/handlers'; import { makeMockCtx, makeMockRequest } from '../../helpers/route-tester'; import { makeClient, makePort } from '../../helpers/factories'; interface MatchData { clientId: string; fullName: string; score: number; confidence: 'high' | 'medium' | 'low'; reasons: string[]; interestCount: number; } async function callHandler( ctx: ReturnType, query: Record, ): Promise { const url = new URL('http://localhost/api/v1/clients/match-candidates'); for (const [k, v] of Object.entries(query)) url.searchParams.set(k, v); const req = makeMockRequest('GET', url.toString()); const res = await getMatchCandidatesHandler(req, ctx); expect(res.status).toBe(200); const body = await res.json(); return body.data as MatchData[]; } describe('GET /api/v1/clients/match-candidates', () => { it('returns empty when nothing actionable was provided', async () => { const port = await makePort(); const ctx = makeMockCtx({ portId: port.id }); const data = await callHandler(ctx, {}); expect(data).toEqual([]); }); it('finds an existing client by exact email match (high confidence)', async () => { const port = await makePort(); const ctx = makeMockCtx({ portId: port.id }); const existing = await makeClient({ portId: port.id, overrides: { fullName: 'Marcus Laurent' }, }); await db.insert(clientContacts).values({ clientId: existing.id, channel: 'email', value: 'marcus@example.com', isPrimary: true, }); await db.insert(clientContacts).values({ clientId: existing.id, channel: 'phone', value: '+15551234567', valueE164: '+15551234567', isPrimary: true, }); const data = await callHandler(ctx, { email: 'Marcus@example.com', phone: '+15551234567', name: 'Marcus Laurent', }); expect(data).toHaveLength(1); expect(data[0]!.clientId).toBe(existing.id); expect(data[0]!.confidence).toBe('high'); expect(data[0]!.reasons).toEqual(expect.arrayContaining(['email match', 'phone match'])); }); it('does not surface unrelated clients in the same port', async () => { const port = await makePort(); const ctx = makeMockCtx({ portId: port.id }); const target = await makeClient({ portId: port.id, overrides: { fullName: 'Marcus Laurent' }, }); await db.insert(clientContacts).values({ clientId: target.id, channel: 'email', value: 'marcus@example.com', isPrimary: true, }); // An unrelated client. const unrelated = await makeClient({ portId: port.id, overrides: { fullName: 'Bob Smith' }, }); await db.insert(clientContacts).values({ clientId: unrelated.id, channel: 'email', value: 'bob@example.org', isPrimary: true, }); const data = await callHandler(ctx, { email: 'marcus@example.com' }); expect(data.map((d) => d.clientId)).toEqual([target.id]); }); it('returns medium-confidence partial matches', async () => { // Same name, different contact info — Pattern F territory. const port = await makePort(); const ctx = makeMockCtx({ portId: port.id }); const existing = await makeClient({ portId: port.id, overrides: { fullName: 'Etiennette Clamouze' }, }); await db.insert(clientContacts).values({ clientId: existing.id, channel: 'email', value: 'clamouze.etiennette@gmail.com', isPrimary: true, }); const data = await callHandler(ctx, { // Different email + phone, same name. email: 'etiennette@the-manoah.com', name: 'Etiennette Clamouze', }); // Either no match (low confidence filtered out) or a medium one — // either is fine. Critically, NOT high. if (data.length > 0) { expect(data[0]!.confidence).not.toBe('high'); } }); it('does not leak across ports', async () => { const portA = await makePort(); const portB = await makePort(); const ctxA = makeMockCtx({ portId: portA.id }); const inB = await makeClient({ portId: portB.id, overrides: { fullName: 'In Port B' }, }); await db.insert(clientContacts).values({ clientId: inB.id, channel: 'email', value: 'b@example.com', isPrimary: true, }); // Caller is in port A, asking for an email that lives in port B. const data = await callHandler(ctxA, { email: 'b@example.com' }); expect(data).toEqual([]); }); });