Files
pn-new-crm/tests/integration/addresses.test.ts

195 lines
5.7 KiB
TypeScript
Raw Permalink Normal View History

import { describe, it, expect } from 'vitest';
import { db } from '@/lib/db';
import { clientAddresses, companyAddresses } from '@/lib/db/schema';
import { eq } from 'drizzle-orm';
import {
listClientAddresses,
addClientAddress,
updateClientAddress,
removeClientAddress,
} from '@/lib/services/clients.service';
import {
listCompanyAddresses,
addCompanyAddress,
updateCompanyAddress,
removeCompanyAddress,
} from '@/lib/services/companies.service';
import { NotFoundError } from '@/lib/errors';
import { makePort, makeClient, makeCompany } from '../helpers/factories';
const META = (portId: string) => ({
userId: 'test-user',
portId,
ipAddress: '127.0.0.1',
userAgent: 'vitest',
});
describe('client addresses service', () => {
it('adds, lists, updates, and removes a client address', async () => {
const port = await makePort();
const client = await makeClient({ portId: port.id });
// Initially empty.
const empty = await listClientAddresses(client.id, port.id);
expect(empty).toHaveLength(0);
const added = await addClientAddress(
client.id,
port.id,
{
label: 'Home',
streetAddress: '1 Pier Rd',
city: 'Marbella',
countryIso: 'ES',
subdivisionIso: 'ES-MA',
postalCode: '29602',
isPrimary: true,
},
META(port.id),
);
expect(added.label).toBe('Home');
expect(added.countryIso).toBe('ES');
expect(added.isPrimary).toBe(true);
const list = await listClientAddresses(client.id, port.id);
expect(list).toHaveLength(1);
const updated = await updateClientAddress(
added.id,
client.id,
port.id,
{ city: 'Málaga' },
META(port.id),
);
expect(updated.city).toBe('Málaga');
await removeClientAddress(added.id, client.id, port.id, META(port.id));
const after = await listClientAddresses(client.id, port.id);
expect(after).toHaveLength(0);
});
it('demotes an existing primary when adding a new primary', async () => {
const port = await makePort();
const client = await makeClient({ portId: port.id });
const first = await addClientAddress(
client.id,
port.id,
{ label: 'Home', isPrimary: true },
META(port.id),
);
const second = await addClientAddress(
client.id,
port.id,
{ label: 'Office', isPrimary: true },
META(port.id),
);
const rows = await db.query.clientAddresses.findMany({
where: eq(clientAddresses.clientId, client.id),
});
const primaries = rows.filter((r) => r.isPrimary);
expect(primaries).toHaveLength(1);
expect(primaries[0]!.id).toBe(second.id);
// The previously-primary row is now demoted, not deleted.
const firstAfter = rows.find((r) => r.id === first.id);
expect(firstAfter?.isPrimary).toBe(false);
});
it('demotes other primaries when patching to primary=true', async () => {
const port = await makePort();
const client = await makeClient({ portId: port.id });
const first = await addClientAddress(
client.id,
port.id,
{ label: 'Home', isPrimary: true },
META(port.id),
);
const second = await addClientAddress(
client.id,
port.id,
{ label: 'Office', isPrimary: false },
META(port.id),
);
await updateClientAddress(second.id, client.id, port.id, { isPrimary: true }, META(port.id));
const rows = await db.query.clientAddresses.findMany({
where: eq(clientAddresses.clientId, client.id),
});
const primary = rows.find((r) => r.isPrimary);
expect(primary?.id).toBe(second.id);
expect(rows.find((r) => r.id === first.id)?.isPrimary).toBe(false);
});
it('is tenant-scoped (cross-port access throws NotFoundError)', async () => {
const portA = await makePort();
const portB = await makePort();
const client = await makeClient({ portId: portA.id });
await expect(listClientAddresses(client.id, portB.id)).rejects.toThrow(NotFoundError);
await expect(
addClientAddress(client.id, portB.id, { label: 'X' }, META(portB.id)),
).rejects.toThrow(NotFoundError);
});
});
describe('company addresses service', () => {
it('adds, lists, updates, and removes a company address', async () => {
const port = await makePort();
const company = await makeCompany({ portId: port.id });
const added = await addCompanyAddress(
company.id,
port.id,
{ label: 'HQ', countryIso: 'GB', isPrimary: true },
META(port.id),
);
expect(added.countryIso).toBe('GB');
const list = await listCompanyAddresses(company.id, port.id);
expect(list).toHaveLength(1);
const updated = await updateCompanyAddress(
added.id,
company.id,
port.id,
{ city: 'London' },
META(port.id),
);
expect(updated.city).toBe('London');
await removeCompanyAddress(added.id, company.id, port.id, META(port.id));
const after = await db.query.companyAddresses.findMany({
where: eq(companyAddresses.companyId, company.id),
});
expect(after).toHaveLength(0);
});
it('demotes an existing primary when adding a new primary', async () => {
const port = await makePort();
const company = await makeCompany({ portId: port.id });
await addCompanyAddress(company.id, port.id, { label: 'HQ', isPrimary: true }, META(port.id));
const second = await addCompanyAddress(
company.id,
port.id,
{ label: 'Branch', isPrimary: true },
META(port.id),
);
const rows = await db.query.companyAddresses.findMany({
where: eq(companyAddresses.companyId, company.id),
});
const primaries = rows.filter((r) => r.isPrimary);
expect(primaries).toHaveLength(1);
expect(primaries[0]!.id).toBe(second.id);
});
});