import { describe, it, expect, vi, afterEach } from "vitest"; import { EventBuilder } from "../src/event-builder.js"; import { ResolvedConfig } from "../src/config.js"; import { TaskType, Complexity, Harness, Provider, Outcome, QualityGate, RepoSizeCategory, } from "../src/types/events.js"; function makeConfig(): ResolvedConfig { return { serverUrl: "https://tel.example.com", apiKey: "a".repeat(64), instanceId: "my-instance-uuid", enabled: true, submitIntervalMs: 300_000, maxQueueSize: 1000, batchSize: 100, requestTimeoutMs: 10_000, predictionCacheTtlMs: 21_600_000, dryRun: false, maxRetries: 3, onError: () => {}, }; } describe("EventBuilder", () => { afterEach(() => { vi.restoreAllMocks(); }); it("should build a complete TaskCompletionEvent", () => { const builder = new EventBuilder(makeConfig()); const event = builder.build({ task_duration_ms: 15000, task_type: TaskType.IMPLEMENTATION, complexity: Complexity.HIGH, harness: Harness.CLAUDE_CODE, model: "claude-3-opus", provider: Provider.ANTHROPIC, estimated_input_tokens: 2000, estimated_output_tokens: 1000, actual_input_tokens: 2200, actual_output_tokens: 1100, estimated_cost_usd_micros: 100000, actual_cost_usd_micros: 110000, quality_gate_passed: true, quality_gates_run: [ QualityGate.BUILD, QualityGate.TEST, QualityGate.LINT, ], quality_gates_failed: [], context_compactions: 2, context_rotations: 1, context_utilization_final: 0.75, outcome: Outcome.SUCCESS, retry_count: 0, language: "typescript", repo_size_category: RepoSizeCategory.MEDIUM, }); expect(event.task_type).toBe(TaskType.IMPLEMENTATION); expect(event.complexity).toBe(Complexity.HIGH); expect(event.model).toBe("claude-3-opus"); expect(event.quality_gates_run).toEqual([ QualityGate.BUILD, QualityGate.TEST, QualityGate.LINT, ]); expect(event.language).toBe("typescript"); expect(event.repo_size_category).toBe(RepoSizeCategory.MEDIUM); }); it("should auto-generate event_id as UUID", () => { const builder = new EventBuilder(makeConfig()); const event = builder.build({ task_duration_ms: 1000, task_type: TaskType.TESTING, complexity: Complexity.LOW, harness: Harness.AIDER, model: "gpt-4", provider: Provider.OPENAI, estimated_input_tokens: 100, estimated_output_tokens: 50, actual_input_tokens: 100, actual_output_tokens: 50, estimated_cost_usd_micros: 1000, actual_cost_usd_micros: 1000, quality_gate_passed: true, quality_gates_run: [], quality_gates_failed: [], context_compactions: 0, context_rotations: 0, context_utilization_final: 0.3, outcome: Outcome.SUCCESS, retry_count: 0, }); // UUID format: 8-4-4-4-12 hex chars expect(event.event_id).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, ); // Each event should get a unique ID const event2 = builder.build({ task_duration_ms: 1000, task_type: TaskType.TESTING, complexity: Complexity.LOW, harness: Harness.AIDER, model: "gpt-4", provider: Provider.OPENAI, estimated_input_tokens: 100, estimated_output_tokens: 50, actual_input_tokens: 100, actual_output_tokens: 50, estimated_cost_usd_micros: 1000, actual_cost_usd_micros: 1000, quality_gate_passed: true, quality_gates_run: [], quality_gates_failed: [], context_compactions: 0, context_rotations: 0, context_utilization_final: 0.3, outcome: Outcome.SUCCESS, retry_count: 0, }); expect(event.event_id).not.toBe(event2.event_id); }); it("should auto-set timestamp to ISO 8601", () => { const now = new Date("2026-02-07T10:00:00.000Z"); vi.setSystemTime(now); const builder = new EventBuilder(makeConfig()); const event = builder.build({ task_duration_ms: 1000, task_type: TaskType.DEBUGGING, complexity: Complexity.MEDIUM, harness: Harness.OPENCODE, model: "claude-3-sonnet", provider: Provider.ANTHROPIC, estimated_input_tokens: 500, estimated_output_tokens: 200, actual_input_tokens: 500, actual_output_tokens: 200, estimated_cost_usd_micros: 5000, actual_cost_usd_micros: 5000, quality_gate_passed: false, quality_gates_run: [QualityGate.TEST], quality_gates_failed: [QualityGate.TEST], context_compactions: 0, context_rotations: 0, context_utilization_final: 0.4, outcome: Outcome.FAILURE, retry_count: 1, }); expect(event.timestamp).toBe("2026-02-07T10:00:00.000Z"); }); it("should set instance_id from config", () => { const config = makeConfig(); const builder = new EventBuilder(config); const event = builder.build({ task_duration_ms: 1000, task_type: TaskType.PLANNING, complexity: Complexity.LOW, harness: Harness.UNKNOWN, model: "test-model", provider: Provider.UNKNOWN, estimated_input_tokens: 0, estimated_output_tokens: 0, actual_input_tokens: 0, actual_output_tokens: 0, estimated_cost_usd_micros: 0, actual_cost_usd_micros: 0, quality_gate_passed: true, quality_gates_run: [], quality_gates_failed: [], context_compactions: 0, context_rotations: 0, context_utilization_final: 0, outcome: Outcome.SUCCESS, retry_count: 0, }); expect(event.instance_id).toBe("my-instance-uuid"); }); it("should set schema_version to 1.0", () => { const builder = new EventBuilder(makeConfig()); const event = builder.build({ task_duration_ms: 1000, task_type: TaskType.REFACTORING, complexity: Complexity.CRITICAL, harness: Harness.KILO_CODE, model: "gemini-pro", provider: Provider.GOOGLE, estimated_input_tokens: 3000, estimated_output_tokens: 2000, actual_input_tokens: 3000, actual_output_tokens: 2000, estimated_cost_usd_micros: 80000, actual_cost_usd_micros: 80000, quality_gate_passed: true, quality_gates_run: [QualityGate.TYPECHECK], quality_gates_failed: [], context_compactions: 5, context_rotations: 2, context_utilization_final: 0.95, outcome: Outcome.SUCCESS, retry_count: 0, }); expect(event.schema_version).toBe("1.0"); }); });