import { inject, injectable, type WorkflowId } from "@codemation/core"; import type { TestAssertionRepository } from "../../domain/runs/TestAssertionRepository"; import type { TestSuiteRunRepository } from "../../domain/runs/TestSuiteRunRepository"; import type { AssertionMetricTrendDto, AssertionMetricTrendPointDto } from "../contracts/TestingContracts"; import { TestAssertionRepositoryToken, TestSuiteRunRepositoryToken } from "./TestSuiteRunTrackerFactory"; @injectable() export class TestAssertionAggregator { constructor( @inject(TestAssertionRepositoryToken) private readonly assertionRepo: TestAssertionRepository, @inject(TestSuiteRunRepositoryToken) private readonly suiteRepo: TestSuiteRunRepository, ) {} async getAssertionMetricTrends(args: { readonly workflowId: WorkflowId; readonly names?: ReadonlyArray; }): Promise> { const filterNames = args.names && args.names.length > 0 ? args.names.filter((n) => n.trim().length > 0) : undefined; const [aggregations, suiteRuns, distinctNames] = await Promise.all([ this.assertionRepo.aggregateMeanScoreByNameAndSuiteRun({ workflowId: args.workflowId, ...(filterNames ? { names: filterNames } : {}), }), this.suiteRepo.listByWorkflow({ workflowId: args.workflowId }), filterNames ? Promise.resolve(filterNames) : this.assertionRepo.listDistinctNamesByWorkflow(args.workflowId), ]); const startedAtById = new Map(); for (const suite of suiteRuns) { startedAtById.set(suite.id, suite.startedAt); } const pointsByName = new Map(); for (const agg of aggregations) { const startedAt = startedAtById.get(agg.testSuiteRunId); if (startedAt === undefined) continue; const list = pointsByName.get(agg.name); const point: AssertionMetricTrendPointDto = { testSuiteRunId: agg.testSuiteRunId, startedAt, meanScore: agg.meanScore, sampleCount: agg.sampleCount, }; if (list) { list.push(point); } else { pointsByName.set(agg.name, [point]); } } for (const points of pointsByName.values()) { points.sort((a, b) => (a.startedAt < b.startedAt ? -1 : a.startedAt > b.startedAt ? 1 : 0)); } const orderedNames = filterNames ? filterNames : distinctNames; const seen = new Set(); const result: AssertionMetricTrendDto[] = []; for (const name of orderedNames) { if (seen.has(name)) continue; seen.add(name); result.push({ name, perSuiteRun: pointsByName.get(name) ?? [], }); } return result; } }