Completes User Story 4's eleven named metrics: first-response-time (messages.service.ts's post(), guarded against double-counting a ticket's second agent message), SLA compliance (sla.service.ts's complete()/runBreachDetectionSweep(), with a guard so a run already breached by the sweep is never also counted "met" when it later resolves), most-common-errors (error-codes.service.ts, counted only once a code is confirmed real), and tool-failure-rate/knowledge- effectiveness (tools.service.ts's single executeTool call site). Verified end-to-end against real Postgres/Redis by scraping the real /metrics endpoint before and after driving each metric's actual underlying event through the real service layer — including a genuine tool-execution failure (a nonexistent ticket ID) rather than a simulated one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { SlaService } from '@/modules/orchestration/sla/service/sla.service';
|
|
import * as observability from '@/infrastructure/observability';
|
|
|
|
function fakeRun(overrides: Partial<Record<string, unknown>> = {}) {
|
|
return {
|
|
id: 'run-1',
|
|
ticketId: 'ticket-1',
|
|
status: 'running',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('SLA compliance metric (014-full-observability data-model.md #6)', () => {
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('counts a run that completes while still running as met', async () => {
|
|
const incSpy = vi.spyOn(observability.slaRunOutcomesCounter, 'inc');
|
|
const runs = {
|
|
findByTicketId: vi.fn().mockResolvedValue(fakeRun({ status: 'running' })),
|
|
update: vi.fn().mockResolvedValue(undefined),
|
|
} as never;
|
|
const service = new SlaService(undefined, runs);
|
|
|
|
await service.complete('ticket-1');
|
|
|
|
expect(incSpy).toHaveBeenCalledWith({ outcome: 'met' });
|
|
});
|
|
|
|
it('does not double-count a run that was already breached before it resolved', async () => {
|
|
const incSpy = vi.spyOn(observability.slaRunOutcomesCounter, 'inc');
|
|
const runs = {
|
|
findByTicketId: vi.fn().mockResolvedValue(fakeRun({ status: 'breached' })),
|
|
update: vi.fn().mockResolvedValue(undefined),
|
|
} as never;
|
|
const service = new SlaService(undefined, runs);
|
|
|
|
await service.complete('ticket-1');
|
|
|
|
expect(incSpy).not.toHaveBeenCalledWith({ outcome: 'met' });
|
|
});
|
|
|
|
it('does not count anything for a run already completed', async () => {
|
|
const incSpy = vi.spyOn(observability.slaRunOutcomesCounter, 'inc');
|
|
const runs = {
|
|
findByTicketId: vi.fn().mockResolvedValue(fakeRun({ status: 'completed' })),
|
|
update: vi.fn().mockResolvedValue(undefined),
|
|
} as never;
|
|
const service = new SlaService(undefined, runs);
|
|
|
|
await service.complete('ticket-1');
|
|
|
|
expect(incSpy).not.toHaveBeenCalled();
|
|
expect((runs as unknown as { update: ReturnType<typeof vi.fn> }).update).not.toHaveBeenCalled();
|
|
});
|
|
});
|