import { injectable } from "@codemation/core"; import type { RecordTestAssertionArgs, TestAssertionMeanScoreAggregation, TestAssertionRecord, TestAssertionRepository, } from "../../domain/runs/TestAssertionRepository"; @injectable() export class InMemoryTestAssertionRepository implements TestAssertionRepository { private readonly recordsById = new Map(); async record(args: RecordTestAssertionArgs): Promise { const record: TestAssertionRecord = { id: args.id, runId: args.runId, testSuiteRunId: args.testSuiteRunId, workflowId: args.workflowId, nodeId: args.nodeId, ...(args.iterationId !== undefined ? { iterationId: args.iterationId } : {}), ...(args.itemIndex !== undefined ? { itemIndex: args.itemIndex } : {}), name: args.name, score: args.score, ...(args.passThreshold !== undefined ? { passThreshold: args.passThreshold } : {}), ...(args.errored === true ? { errored: true as const } : {}), ...(args.expected !== undefined ? { expected: args.expected } : {}), ...(args.actual !== undefined ? { actual: args.actual } : {}), ...(args.message !== undefined ? { message: args.message } : {}), ...(args.details !== undefined ? { details: args.details } : {}), createdAt: args.createdAt, }; this.recordsById.set(args.id, record); } async listByRun(runId: string): Promise> { return [...this.recordsById.values()] .filter((r) => r.runId === runId) .sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1)); } async listByTestSuiteRun(testSuiteRunId: string): Promise> { return [...this.recordsById.values()] .filter((r) => r.testSuiteRunId === testSuiteRunId) .sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1)); } async deleteByTestSuiteRun(testSuiteRunId: string): Promise { for (const [id, record] of this.recordsById) { if (record.testSuiteRunId === testSuiteRunId) { this.recordsById.delete(id); } } } async listDistinctNamesByWorkflow(workflowId: string): Promise> { const names = new Set(); for (const record of this.recordsById.values()) { if (record.workflowId === workflowId) { names.add(record.name); } } return [...names].sort(); } async aggregateMeanScoreByNameAndSuiteRun(args: { readonly workflowId: string; readonly names?: ReadonlyArray; }): Promise> { const filterNames = args.names ? new Set(args.names) : undefined; interface Bucket { readonly testSuiteRunId: string; readonly name: string; sum: number; count: number; } const buckets = new Map(); for (const record of this.recordsById.values()) { if (record.workflowId !== args.workflowId) continue; if (filterNames && !filterNames.has(record.name)) continue; const key = `${record.testSuiteRunId}${record.name}`; const existing = buckets.get(key); if (existing) { existing.sum += record.score; existing.count += 1; } else { buckets.set(key, { testSuiteRunId: record.testSuiteRunId, name: record.name, sum: record.score, count: 1, }); } } return [...buckets.values()].map((b) => ({ testSuiteRunId: b.testSuiteRunId, name: b.name, meanScore: b.count > 0 ? b.sum / b.count : 0, sampleCount: b.count, })); } }