import { describe, expect, it, vi } from 'vitest'; import { TeamRuntimeTurnSettledTargetResolver } from '@features/member-work-sync/main/adapters/output/TeamRuntimeTurnSettledTargetResolver'; import type { TeamConfig } from '@shared/types'; describe('TeamRuntimeTurnSettledTargetResolver', () => { it('resolves a Claude Stop transcript path to an active Anthropic teammate', async () => { const resolver = new TeamRuntimeTurnSettledTargetResolver({ teamSource: { listTeams: vi.fn(async () => [{ teamName: 'team-a', displayName: 'team-a' } as never]), getConfig: vi.fn(async () => ({ name: 'team-a', members: [{ name: 'Alice', providerId: 'anthropic' }], }) satisfies TeamConfig), }, membersMetaStore: { getMembers: vi.fn(async () => []) } as never, memberLogsFinder: { listAttributedMemberFiles: vi.fn(async () => [ { memberName: 'Alice', sessionId: 'ses-1', filePath: '/tmp/ses-1.jsonl', mtimeMs: 1, }, ]), }, }); await expect( resolver.resolve({ schemaVersion: 1, provider: 'claude', hookEventName: 'Stop', sourceId: 'source-1', payloadHash: 'hash', recordedAt: '2026-04-29T12:00:00.000Z', sessionId: 'ses-1', transcriptPath: '/tmp/ses-1.jsonl', }) ).resolves.toEqual({ ok: true, teamName: 'team-a', memberName: 'alice' }); }); it('rejects matches for removed or non-Anthropic teammates', async () => { const resolver = new TeamRuntimeTurnSettledTargetResolver({ teamSource: { listTeams: vi.fn(async () => [{ teamName: 'team-a', displayName: 'team-a' } as never]), getConfig: vi.fn(async () => ({ name: 'team-a', members: [{ name: 'bob', providerId: 'opencode' }], }) satisfies TeamConfig), }, membersMetaStore: { getMembers: vi.fn(async () => []) } as never, memberLogsFinder: { listAttributedMemberFiles: vi.fn(async () => [ { memberName: 'bob', sessionId: 'ses-1', filePath: '/tmp/ses-1.jsonl', mtimeMs: 1, }, ]), }, }); await expect( resolver.resolve({ schemaVersion: 1, provider: 'claude', hookEventName: 'Stop', sourceId: 'source-1', payloadHash: 'hash', recordedAt: '2026-04-29T12:00:00.000Z', sessionId: 'ses-1', }) ).resolves.toEqual({ ok: false, reason: 'provider_mismatch' }); }); it('resolves Codex native turn-settled payloads from durable team/member identity', async () => { const resolver = new TeamRuntimeTurnSettledTargetResolver({ teamSource: { listTeams: vi.fn(async () => { throw new Error('codex path should not scan attributed files'); }), getConfig: vi.fn(async () => ({ name: 'team-a', members: [{ name: 'Jack', providerId: 'codex' }], }) satisfies TeamConfig), }, membersMetaStore: { getMembers: vi.fn(async () => []) } as never, memberLogsFinder: { listAttributedMemberFiles: vi.fn(async () => []), }, }); await expect( resolver.resolve({ schemaVersion: 1, provider: 'codex', hookEventName: 'Stop', sourceId: 'source-1', payloadHash: 'hash', recordedAt: '2026-04-29T12:00:00.000Z', sessionId: 'ses-1', teamName: 'team-a', memberName: 'jack', }) ).resolves.toEqual({ ok: true, teamName: 'team-a', memberName: 'jack' }); }); it('rejects Codex native events for non-Codex teammates', async () => { const resolver = new TeamRuntimeTurnSettledTargetResolver({ teamSource: { listTeams: vi.fn(async () => []), getConfig: vi.fn(async () => ({ name: 'team-a', members: [{ name: 'Jack', providerId: 'anthropic' }], }) satisfies TeamConfig), }, membersMetaStore: { getMembers: vi.fn(async () => []) } as never, }); await expect( resolver.resolve({ schemaVersion: 1, provider: 'codex', hookEventName: 'Stop', sourceId: 'source-1', payloadHash: 'hash', recordedAt: '2026-04-29T12:00:00.000Z', sessionId: 'ses-1', teamName: 'team-a', memberName: 'jack', }) ).resolves.toEqual({ ok: false, reason: 'provider_mismatch' }); }); it('resolves OpenCode turn-settled payloads from durable team/member identity', async () => { const resolver = new TeamRuntimeTurnSettledTargetResolver({ teamSource: { listTeams: vi.fn(async () => { throw new Error('opencode path should not scan attributed files'); }), getConfig: vi.fn(async () => ({ name: 'team-a', members: [{ name: 'Jack', providerId: 'opencode' }], }) satisfies TeamConfig), }, membersMetaStore: { getMembers: vi.fn(async () => []) } as never, memberLogsFinder: { listAttributedMemberFiles: vi.fn(async () => []), }, }); await expect( resolver.resolve({ schemaVersion: 1, provider: 'opencode', hookEventName: 'Stop', sourceId: 'source-1', payloadHash: 'hash', recordedAt: '2026-04-29T12:00:00.000Z', sessionId: 'ses-1', teamName: 'team-a', memberName: 'jack', }) ).resolves.toEqual({ ok: true, teamName: 'team-a', memberName: 'jack' }); }); it('rejects OpenCode events for non-OpenCode teammates', async () => { const resolver = new TeamRuntimeTurnSettledTargetResolver({ teamSource: { listTeams: vi.fn(async () => []), getConfig: vi.fn(async () => ({ name: 'team-a', members: [{ name: 'Jack', providerId: 'codex' }], }) satisfies TeamConfig), }, membersMetaStore: { getMembers: vi.fn(async () => []) } as never, }); await expect( resolver.resolve({ schemaVersion: 1, provider: 'opencode', hookEventName: 'Stop', sourceId: 'source-1', payloadHash: 'hash', recordedAt: '2026-04-29T12:00:00.000Z', sessionId: 'ses-1', teamName: 'team-a', memberName: 'jack', }) ).resolves.toEqual({ ok: false, reason: 'provider_mismatch' }); }); });