import { StaticAudienceExecutor } from '../executors/StaticAudienceExecutor' import { AudienceCriteria } from '../types' const validCriteria: AudienceCriteria = { groups: [{ operator: 'AND', rules: [{ kind: 'property', field: 'email', op: 'contains', value: '@' }] }] } function mockRepo(ids: string[] = ['c1', 'c2', 'c3']) { return { getContactIdsByAudienceCriteriaV2: jest.fn().mockResolvedValue(new Set(ids)), findByIds: jest.fn().mockResolvedValue({ data: ids.map(id => ({ id, email: `${id}@test.com` })), error: null, }), } } describe('StaticAudienceExecutor', () => { describe('execute', () => { it('returns contactIds and count', async () => { const executor = new StaticAudienceExecutor(mockRepo()) const result = await executor.execute(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', }) expect(result.contactIds).toBeInstanceOf(Set) expect(result.contactIds.size).toBe(3) expect(result.count).toBe(3) expect(result.metadata?.criteriaType).toBe('static') expect(result.metadata?.executionTime).toBeGreaterThanOrEqual(0) }) it('fetches contact details when pagination is set', async () => { const repo = mockRepo(['c1', 'c2', 'c3', 'c4', 'c5']) const executor = new StaticAudienceExecutor(repo) await executor.execute(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', pagination: { page: 1, limit: 2 }, }) expect(repo.findByIds).toHaveBeenCalled() // Should request only first 2 ids const calledIds = repo.findByIds.mock.calls[0][2] expect(calledIds).toHaveLength(2) }) it('fetches contact details when includeCount is true', async () => { const repo = mockRepo() const executor = new StaticAudienceExecutor(repo) const result = await executor.execute(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', includeCount: true, }) expect(repo.findByIds).toHaveBeenCalled() expect(result.contacts).toBeDefined() }) it('does not fetch contact details without pagination or includeCount', async () => { const repo = mockRepo() const executor = new StaticAudienceExecutor(repo) const result = await executor.execute(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', }) expect(repo.findByIds).not.toHaveBeenCalled() expect(result.contacts).toBeUndefined() }) it('returns empty result for invalid criteria', async () => { const executor = new StaticAudienceExecutor(mockRepo()) const result = await executor.execute({ type: 'static' }, { organizationId: 'org-1', projectId: 'proj-1', }) expect(result.contactIds.size).toBe(0) expect(result.count).toBe(0) }) it('throws when contactRepository not set', async () => { const executor = new StaticAudienceExecutor() await expect(executor.execute(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', })).rejects.toThrow('ContactRepository não foi configurado') }) it('paginates correctly for page 2', async () => { const repo = mockRepo(['c1', 'c2', 'c3', 'c4', 'c5']) const executor = new StaticAudienceExecutor(repo) await executor.execute(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', pagination: { page: 2, limit: 2 }, }) const calledIds = repo.findByIds.mock.calls[0][2] expect(calledIds).toEqual(['c3', 'c4']) }) }) describe('executeCount', () => { it('returns count only', async () => { const executor = new StaticAudienceExecutor(mockRepo(['c1', 'c2'])) const count = await executor.executeCount(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', }) expect(count).toBe(2) }) }) describe('setContactRepository', () => { it('allows setting repository after construction', async () => { const executor = new StaticAudienceExecutor() executor.setContactRepository(mockRepo()) const result = await executor.execute(validCriteria, { organizationId: 'org-1', projectId: 'proj-1', }) expect(result.count).toBe(3) }) }) })