PSA/shared/services/__tests__/emailService.contactLookup.test.ts
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

128 lines
3.5 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
const testState = vi.hoisted(() => ({
getContactByEmailMock: vi.fn(),
createContactMock: vi.fn(),
}));
vi.mock('../../models/contactModel', () => ({
ContactModel: {
getContactByEmail: testState.getContactByEmailMock,
createContact: testState.createContactMock,
},
}));
import { EmailService } from '../emailService';
type FakeKnex = {
(table: string): {
select: (..._args: any[]) => {
where: (..._args: any[]) => {
first: () => Promise<any>;
};
};
};
};
function makeKnex(clientRow?: any): FakeKnex {
return ((table: string) => {
if (table === 'clients') {
return {
select: () => ({
where: () => ({
first: async () => clientRow,
}),
}),
};
}
throw new Error(`Unexpected table ${table}`);
}) as FakeKnex;
}
describe('EmailService contact lookup', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('T033: findContactByEmail preserves both matched sender email and the contact primary email when an additional row matched', async () => {
testState.getContactByEmailMock.mockResolvedValue({
contact_name_id: 'contact-1',
full_name: 'Alice Contact',
email: 'owner@acme.com',
client_id: 'client-1',
role: 'Coordinator',
default_phone_number: null,
phone_numbers: [
{
is_default: true,
phone_number: '555-1111',
},
],
});
const service = new EmailService(makeKnex({ client_name: 'Acme Client' }) as any, 'tenant-1');
const result = await service.findContactByEmail('billing@acme.com');
expect(testState.getContactByEmailMock).toHaveBeenCalledWith('billing@acme.com', 'tenant-1', expect.any(Function));
expect(result).toEqual({
contact_id: 'contact-1',
name: 'Alice Contact',
email: 'owner@acme.com',
matched_email: 'billing@acme.com',
client_id: 'client-1',
client_name: 'Acme Client',
phone: '555-1111',
title: 'Coordinator',
});
});
it('T034: createOrFindContact creates a new contact with only the primary email in contacts.email when no match exists', async () => {
testState.getContactByEmailMock.mockResolvedValue(null);
testState.createContactMock.mockResolvedValue({
contact_name_id: 'contact-created',
full_name: 'New Contact',
email: 'new.contact@acme.com',
client_id: 'client-1',
role: 'Coordinator',
created_at: '2026-03-15T00:00:00.000Z',
default_phone_number: null,
phone_numbers: [],
});
const service = new EmailService(makeKnex() as any, 'tenant-1');
const result = await service.createOrFindContact({
email: 'New.Contact@Acme.com',
name: 'New Contact',
client_id: 'client-1',
phone: '',
title: 'Coordinator',
});
expect(testState.createContactMock).toHaveBeenCalledWith(
expect.objectContaining({
full_name: 'New Contact',
email: 'new.contact@acme.com',
client_id: 'client-1',
phone_numbers: [],
role: 'Coordinator',
}),
'tenant-1',
expect.any(Function)
);
expect(testState.createContactMock.mock.calls[0]?.[0]).not.toHaveProperty('additional_email_addresses');
expect(result).toEqual({
id: 'contact-created',
name: 'New Contact',
email: 'new.contact@acme.com',
client_id: 'client-1',
phone: undefined,
title: 'Coordinator',
created_at: '2026-03-15T00:00:00.000Z',
is_new: true,
});
});
});