139 lines
4.3 KiB
TypeScript
139 lines
4.3 KiB
TypeScript
|
|
/**
|
|||
|
|
* i18n PR9–10 — public residential inquiry endpoint.
|
|||
|
|
*
|
|||
|
|
* Validates the server-side phone normalization that the public inquiry
|
|||
|
|
* route runs when the website posts a raw international format (older
|
|||
|
|
* Nuxt builds), and that pre-normalized payloads pass through unchanged.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { describe, it, expect, vi, beforeAll } from 'vitest';
|
|||
|
|
import { eq } from 'drizzle-orm';
|
|||
|
|
|
|||
|
|
import { db } from '@/lib/db';
|
|||
|
|
import { residentialClients } from '@/lib/db/schema/residential';
|
|||
|
|
import { makePort } from '../helpers/factories';
|
|||
|
|
import { makeMockRequest } from '../helpers/route-tester';
|
|||
|
|
|
|||
|
|
vi.mock('@/lib/socket/server', () => ({ emitToRoom: vi.fn() }));
|
|||
|
|
vi.mock('@/lib/email', () => ({ sendEmail: vi.fn().mockResolvedValue(undefined) }));
|
|||
|
|
|
|||
|
|
let ipCounter = 1;
|
|||
|
|
function uniqueIp(): string {
|
|||
|
|
ipCounter += 1;
|
|||
|
|
return `10.50.${Math.floor(ipCounter / 255) % 255}.${ipCounter % 255}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
describe('POST /api/public/residential-inquiries', () => {
|
|||
|
|
let POST: typeof import('@/app/api/public/residential-inquiries/route').POST;
|
|||
|
|
|
|||
|
|
beforeAll(async () => {
|
|||
|
|
const mod = await import('@/app/api/public/residential-inquiries/route');
|
|||
|
|
POST = mod.POST;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('parses a raw international phone string into E.164 + country', async () => {
|
|||
|
|
const port = await makePort();
|
|||
|
|
const email = `res-${Math.random().toString(36).slice(2, 8)}@test.local`;
|
|||
|
|
|
|||
|
|
const req = makeMockRequest(
|
|||
|
|
'POST',
|
|||
|
|
`http://localhost/api/public/residential-inquiries?portId=${port.id}`,
|
|||
|
|
{
|
|||
|
|
headers: { 'x-forwarded-for': uniqueIp() },
|
|||
|
|
body: {
|
|||
|
|
firstName: 'Anna',
|
|||
|
|
lastName: 'Nowak',
|
|||
|
|
email,
|
|||
|
|
// Raw international format — server should normalize.
|
|||
|
|
phone: '+44 20 7946 0958',
|
|||
|
|
placeOfResidence: 'Warsaw',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const res = await POST(req);
|
|||
|
|
expect(res.status).toBe(201);
|
|||
|
|
|
|||
|
|
const [row] = await db
|
|||
|
|
.select()
|
|||
|
|
.from(residentialClients)
|
|||
|
|
.where(eq(residentialClients.email, email));
|
|||
|
|
|
|||
|
|
expect(row).toBeDefined();
|
|||
|
|
expect(row?.phoneE164).toBe('+442079460958');
|
|||
|
|
expect(row?.phoneCountry).toBe('GB');
|
|||
|
|
// Free-text legacy column preserved verbatim for backfill.
|
|||
|
|
expect(row?.phone).toBe('+44 20 7946 0958');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('passes pre-normalized E.164 + country through unchanged', async () => {
|
|||
|
|
const port = await makePort();
|
|||
|
|
const email = `res-${Math.random().toString(36).slice(2, 8)}@test.local`;
|
|||
|
|
|
|||
|
|
const req = makeMockRequest(
|
|||
|
|
'POST',
|
|||
|
|
`http://localhost/api/public/residential-inquiries?portId=${port.id}`,
|
|||
|
|
{
|
|||
|
|
headers: { 'x-forwarded-for': uniqueIp() },
|
|||
|
|
body: {
|
|||
|
|
firstName: 'Jan',
|
|||
|
|
lastName: 'Kowalski',
|
|||
|
|
email,
|
|||
|
|
phone: '+48 22 555 0100',
|
|||
|
|
phoneE164: '+48225550100',
|
|||
|
|
phoneCountry: 'PL',
|
|||
|
|
nationalityIso: 'PL',
|
|||
|
|
timezone: 'Europe/Warsaw',
|
|||
|
|
placeOfResidence: 'Warsaw',
|
|||
|
|
placeOfResidenceCountryIso: 'PL',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const res = await POST(req);
|
|||
|
|
expect(res.status).toBe(201);
|
|||
|
|
|
|||
|
|
const [row] = await db
|
|||
|
|
.select()
|
|||
|
|
.from(residentialClients)
|
|||
|
|
.where(eq(residentialClients.email, email));
|
|||
|
|
|
|||
|
|
expect(row?.phoneE164).toBe('+48225550100');
|
|||
|
|
expect(row?.phoneCountry).toBe('PL');
|
|||
|
|
expect(row?.nationalityIso).toBe('PL');
|
|||
|
|
expect(row?.timezone).toBe('Europe/Warsaw');
|
|||
|
|
expect(row?.placeOfResidenceCountryIso).toBe('PL');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('persists a national-format phone when the website only sends a country hint', async () => {
|
|||
|
|
const port = await makePort();
|
|||
|
|
const email = `res-${Math.random().toString(36).slice(2, 8)}@test.local`;
|
|||
|
|
|
|||
|
|
const req = makeMockRequest(
|
|||
|
|
'POST',
|
|||
|
|
`http://localhost/api/public/residential-inquiries?portId=${port.id}`,
|
|||
|
|
{
|
|||
|
|
headers: { 'x-forwarded-for': uniqueIp() },
|
|||
|
|
body: {
|
|||
|
|
firstName: 'Marta',
|
|||
|
|
lastName: 'Lewandowska',
|
|||
|
|
email,
|
|||
|
|
phone: '22 555 0200', // National-format
|
|||
|
|
phoneCountry: 'PL', // Hint only — no E.164 yet.
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const res = await POST(req);
|
|||
|
|
expect(res.status).toBe(201);
|
|||
|
|
|
|||
|
|
const [row] = await db
|
|||
|
|
.select()
|
|||
|
|
.from(residentialClients)
|
|||
|
|
.where(eq(residentialClients.email, email));
|
|||
|
|
|
|||
|
|
expect(row?.phoneE164).toBe('+48225550200');
|
|||
|
|
expect(row?.phoneCountry).toBe('PL');
|
|||
|
|
});
|
|||
|
|
});
|