UNPKG

@accounter/server

Version:
169 lines 7.89 kB
import { describe, expect, it, vi } from 'vitest'; import { emailIngestionModule } from '../index.js'; import { gmailListenerModule } from '../../gmail-listener/index.js'; import { emailIngestionResolvers } from '../resolvers/email-ingestion.resolver.js'; import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js'; import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js'; import { DocumentsProvider } from '../../documents/providers/documents.provider.js'; import { ChargesProvider } from '../../charges/providers/charges.provider.js'; import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js'; function createMockInjector(providers = {}) { const businessesProvider = { getBusinessByEmail: vi.fn().mockResolvedValue(null), ...providers.businessesProvider, }; const adminContextProvider = { getVerifiedAdminContext: vi.fn().mockResolvedValue({ ownerId: 'owner-1' }), ...providers.adminContextProvider, }; const documentsProvider = { getDocumentByHash: { load: vi.fn().mockResolvedValue(null) }, insertDocuments: vi.fn().mockResolvedValue([]), ...providers.documentsProvider, }; const chargesProvider = { generateCharge: vi.fn().mockResolvedValue({ id: 'charge-1' }), ...providers.chargesProvider, }; const dbClient = { transaction: vi.fn().mockImplementation(async (fn) => fn({})), ...providers.dbClient, }; return { get: (token) => { if (token === BusinessesProvider) return businessesProvider; if (token === AdminContextProvider) return adminContextProvider; if (token === DocumentsProvider) return documentsProvider; if (token === ChargesProvider) return chargesProvider; if (token === TenantAwareDBClient) return dbClient; throw new Error(`Unexpected provider: ${String(token)}`); }, }; } // --------------------------------------------------------------------------- // Module structure and S02 naming guards // --------------------------------------------------------------------------- describe('emailIngestionModule', () => { it('has the correct module id', () => { expect(emailIngestionModule.id).toBe('email-ingestion'); }); it('exports CommonTypes namespace', async () => { const { CommonTypes } = await import('../index.js'); expect(CommonTypes).toBeDefined(); }); // S02 guard: emailIngestionModule must be exported directly from // email-ingestion, not only via the gmail-listener shim. // This test fails if the export is removed or renamed back. it('is directly exported from email-ingestion (not only via gmail-listener shim)', () => { expect(emailIngestionModule).toBeDefined(); expect(emailIngestionModule.id).toBe('email-ingestion'); }); }); // S02 guard: resolver identifiers use email-ingestion naming. // These tests fail if the canonical exports are removed or reverted to old names. describe('emailIngestionResolvers (S02 naming guard)', () => { it('is exported from the email-ingestion resolver module', () => { expect(emailIngestionResolvers).toBeDefined(); }); it('provides Query.businessEmailConfig resolver', () => { expect(emailIngestionResolvers.Query?.businessEmailConfig).toBeDefined(); }); it('provides Mutation.insertEmailDocuments resolver', () => { expect(emailIngestionResolvers.Mutation?.insertEmailDocuments).toBeDefined(); }); }); // --------------------------------------------------------------------------- // Backward-compat shim (explicit adapter — not new code) // --------------------------------------------------------------------------- describe('gmailListenerModule (backward-compat shim)', () => { it('is the same module instance as emailIngestionModule', () => { expect(gmailListenerModule).toBe(emailIngestionModule); }); }); // --------------------------------------------------------------------------- // businessEmailConfig resolver // --------------------------------------------------------------------------- describe('businessEmailConfig resolver', () => { const resolver = emailIngestionResolvers.Query.businessEmailConfig; it('returns null when no business matches the email', async () => { const injector = createMockInjector({ businessesProvider: { getBusinessByEmail: vi.fn().mockResolvedValue(null) }, }); const result = await resolver({}, { email: 'unknown@example.com' }, { injector }, {}); expect(result).toBeNull(); }); it('returns businessId and empty config when business has no suggestion_data', async () => { const injector = createMockInjector({ businessesProvider: { getBusinessByEmail: vi.fn().mockResolvedValue({ id: 'biz-1', name: 'Test Business', suggestion_data: null, }), }, }); const result = await resolver({}, { email: 'test@example.com' }, { injector }, {}); expect(result).toMatchObject({ businessId: 'biz-1' }); expect(result?.internalEmailLinks).toBeUndefined(); expect(result?.emailBody).toBeUndefined(); }); it('returns emailListener config from valid suggestion_data', async () => { const suggestionData = { emailListener: { internalEmailLinks: ['partner@example.com'], emailBody: true, attachments: [], }, }; const injector = createMockInjector({ businessesProvider: { getBusinessByEmail: vi.fn().mockResolvedValue({ id: 'biz-2', name: 'Test Business', suggestion_data: suggestionData, }), }, }); const result = await resolver({}, { email: 'test@example.com' }, { injector }, {}); expect(result).toMatchObject({ businessId: 'biz-2', internalEmailLinks: ['partner@example.com'], emailBody: true, attachments: [], }); }); }); // --------------------------------------------------------------------------- // insertEmailDocuments resolver // --------------------------------------------------------------------------- describe('insertEmailDocuments resolver', () => { const resolver = emailIngestionResolvers.Mutation.insertEmailDocuments; it('returns true immediately when documents array is empty', async () => { const injector = createMockInjector(); const result = await resolver({}, { documents: [], userDescription: 'test', messageId: null, businessId: null }, { injector }, {}); expect(result).toBe(true); }); it('skips a document that already exists in the database (dedup by hash)', async () => { const existingDoc = { id: 'doc-existing' }; const mockDoc = { text: vi.fn().mockResolvedValue('document-content'), name: 'invoice.pdf', }; const insertDocuments = vi.fn().mockResolvedValue([]); const injector = createMockInjector({ documentsProvider: { getDocumentByHash: { load: vi.fn().mockResolvedValue(existingDoc) }, insertDocuments, }, }); const result = await resolver({}, { documents: [mockDoc], userDescription: 'test', messageId: 'msg-1', businessId: null }, { injector }, {}); expect(result).toBe(true); expect(insertDocuments).not.toHaveBeenCalled(); }); }); //# sourceMappingURL=email-ingestion.module.test.js.map