From 93d6fe8b944a87ff3119392fb7569449666be300 Mon Sep 17 00:00:00 2001 From: saqib mir Date: Thu, 10 Sep 2026 11:13:46 +0530 Subject: [PATCH] =?UTF-8?q?fix(016-load-concurrency-testing):=20US3=20?= =?UTF-8?q?=E2=80=94=20finish=20escalation=20idempotency=20plumbing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the escalation-event repository/service changes from the prior commit: normalizes the exactOptionalPropertyTypes mismatch in the findFirst fallback lookup, and updates every existing unit test (sla-pause-resume, sla-breach-detection, sla-compliance-metric, escalation-rule-match) to the new SlaRunRepository.updateWithVersion and EscalationEventRepository.create({event, wasNewlyCreated}) signatures. Full typecheck/lint/architecture-check clean. Co-Authored-By: Claude Sonnet 5 --- .../repository/escalation-event.repository.ts | 5 +++- .../sla-compliance-metric.test.ts | 9 ++++--- .../escalation-rule-match.test.ts | 4 ++- .../sla-breach-detection.test.ts | 9 ++++--- .../orchestration/sla-pause-resume.test.ts | 27 ++++++++++--------- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/modules/orchestration/escalation/repository/escalation-event.repository.ts b/src/modules/orchestration/escalation/repository/escalation-event.repository.ts index 6d5ee39..bc0eccb 100644 --- a/src/modules/orchestration/escalation/repository/escalation-event.repository.ts +++ b/src/modules/orchestration/escalation/repository/escalation-event.repository.ts @@ -50,7 +50,10 @@ export class EscalationEventRepository { if (!isDuplicateRuleEscalationConflict(error)) throw error; const existing = await this.prisma.escalationEvent.findFirst({ - where: { ticketId: data.ticketId, ruleId: data.ruleId }, + where: { + ticketId: data.ticketId, + ...(data.ruleId !== undefined ? { ruleId: data.ruleId } : {}), + }, }); if (!existing) throw error; // conflict raced with a delete — surface the original error. return { event: existing, wasNewlyCreated: false }; diff --git a/tests/unit/observability/sla-compliance-metric.test.ts b/tests/unit/observability/sla-compliance-metric.test.ts index 7f782d0..d8d6db7 100644 --- a/tests/unit/observability/sla-compliance-metric.test.ts +++ b/tests/unit/observability/sla-compliance-metric.test.ts @@ -7,6 +7,7 @@ function fakeRun(overrides: Partial> = {}) { id: 'run-1', ticketId: 'ticket-1', status: 'running', + version: 1, ...overrides, }; } @@ -20,7 +21,7 @@ describe('SLA compliance metric (014-full-observability data-model.md #6)', () = const incSpy = vi.spyOn(observability.slaRunOutcomesCounter, 'inc'); const runs = { findByTicketId: vi.fn().mockResolvedValue(fakeRun({ status: 'running' })), - update: vi.fn().mockResolvedValue(undefined), + updateWithVersion: vi.fn().mockResolvedValue(fakeRun({ status: 'completed' })), } as never; const service = new SlaService(undefined, runs); @@ -33,7 +34,7 @@ describe('SLA compliance metric (014-full-observability data-model.md #6)', () = const incSpy = vi.spyOn(observability.slaRunOutcomesCounter, 'inc'); const runs = { findByTicketId: vi.fn().mockResolvedValue(fakeRun({ status: 'breached' })), - update: vi.fn().mockResolvedValue(undefined), + updateWithVersion: vi.fn().mockResolvedValue(fakeRun({ status: 'completed' })), } as never; const service = new SlaService(undefined, runs); @@ -46,13 +47,13 @@ describe('SLA compliance metric (014-full-observability data-model.md #6)', () = const incSpy = vi.spyOn(observability.slaRunOutcomesCounter, 'inc'); const runs = { findByTicketId: vi.fn().mockResolvedValue(fakeRun({ status: 'completed' })), - update: vi.fn().mockResolvedValue(undefined), + updateWithVersion: 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 }).update).not.toHaveBeenCalled(); + expect((runs as unknown as { updateWithVersion: ReturnType }).updateWithVersion).not.toHaveBeenCalled(); }); }); diff --git a/tests/unit/orchestration/escalation-rule-match.test.ts b/tests/unit/orchestration/escalation-rule-match.test.ts index cfa1c08..41bb816 100644 --- a/tests/unit/orchestration/escalation-rule-match.test.ts +++ b/tests/unit/orchestration/escalation-rule-match.test.ts @@ -19,7 +19,9 @@ describe('EscalationService.handleBreach', () => { findApplicable: vi.fn().mockResolvedValue({ id: 'policy-1', productId: 'prod-1' }), } as never; const rules = { findActiveRules: vi.fn().mockResolvedValue([rule]) } as never; - const events = { create: vi.fn().mockResolvedValue({ id: 'event-1' }) } as never; + const events = { + create: vi.fn().mockResolvedValue({ event: { id: 'event-1' }, wasNewlyCreated: true }), + } as never; const assignmentEngine = { assignToSpecificNode: vi.fn().mockResolvedValue(undefined), } as never; diff --git a/tests/unit/orchestration/sla-breach-detection.test.ts b/tests/unit/orchestration/sla-breach-detection.test.ts index 57e64d6..a8e159e 100644 --- a/tests/unit/orchestration/sla-breach-detection.test.ts +++ b/tests/unit/orchestration/sla-breach-detection.test.ts @@ -10,6 +10,7 @@ function run(overrides: Partial): SLARun { firstResponseDueAt: null, resolutionDueAt: null, status: 'running', + version: 1, pausedAt: null, resumedAt: null, breachedAt: null, @@ -22,11 +23,11 @@ function run(overrides: Partial): SLARun { describe('SlaService.runBreachDetectionSweep', () => { it('marks every running run past its resolution due date as breached and fires escalation', async () => { const overdue = run({ id: 'r1', ticketId: 't1' }); - const update = vi.fn().mockResolvedValue(overdue); + const updateWithVersion = vi.fn().mockResolvedValue(overdue); const runsRepo = { findRunningPastResolutionDueAt: vi.fn().mockResolvedValue([overdue]), findRunningPastFirstResponseDueAt: vi.fn().mockResolvedValue([]), - update, + updateWithVersion, } as never; const handleBreach = vi.fn().mockResolvedValue(undefined); const escalation = { handleBreach } as never; @@ -34,7 +35,7 @@ describe('SlaService.runBreachDetectionSweep', () => { const service = new SlaService(undefined, runsRepo, undefined, undefined, escalation); await service.runBreachDetectionSweep(); - expect(update).toHaveBeenCalledWith('r1', expect.objectContaining({ status: 'breached' })); + expect(updateWithVersion).toHaveBeenCalledWith('r1', 1, expect.objectContaining({ status: 'breached' })); expect(handleBreach).toHaveBeenCalledWith('t1', 'resolution_breach'); }); @@ -42,7 +43,7 @@ describe('SlaService.runBreachDetectionSweep', () => { const runsRepo = { findRunningPastResolutionDueAt: vi.fn().mockResolvedValue([]), findRunningPastFirstResponseDueAt: vi.fn().mockResolvedValue([]), - update: vi.fn(), + updateWithVersion: vi.fn(), } as never; const handleBreach = vi.fn(); const service = new SlaService(undefined, runsRepo, undefined, undefined, { diff --git a/tests/unit/orchestration/sla-pause-resume.test.ts b/tests/unit/orchestration/sla-pause-resume.test.ts index bcd9d3b..0e23baf 100644 --- a/tests/unit/orchestration/sla-pause-resume.test.ts +++ b/tests/unit/orchestration/sla-pause-resume.test.ts @@ -10,6 +10,7 @@ function run(overrides: Partial): SLARun { firstResponseDueAt: new Date('2026-01-05T12:00:00.000Z'), resolutionDueAt: new Date('2026-01-05T17:00:00.000Z'), status: 'running', + version: 1, pausedAt: null, resumedAt: null, breachedAt: null, @@ -22,13 +23,13 @@ function run(overrides: Partial): SLARun { describe('SlaService pause/resume', () => { it('pause records pausedAt and flips status to paused', async () => { const found = run({}); - const update = vi.fn().mockResolvedValue(found); - const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(found), update } as never; + const updateWithVersion = vi.fn().mockResolvedValue(found); + const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(found), updateWithVersion } as never; const service = new SlaService(undefined, runsRepo); await service.pause('ticket-1'); - expect(update).toHaveBeenCalledWith('run-1', expect.objectContaining({ status: 'paused' })); + expect(updateWithVersion).toHaveBeenCalledWith('run-1', 1, expect.objectContaining({ status: 'paused' })); }); it('resume shifts both due dates forward by exactly the paused wall-clock duration', async () => { @@ -39,16 +40,16 @@ describe('SlaService pause/resume', () => { firstResponseDueAt: new Date('2026-01-05T12:00:00.000Z'), resolutionDueAt: new Date('2026-01-05T17:00:00.000Z'), }); - const update = vi.fn().mockResolvedValue(paused); - const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(paused), update } as never; + const updateWithVersion = vi.fn().mockResolvedValue(paused); + const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(paused), updateWithVersion } as never; const service = new SlaService(undefined, runsRepo); const before = Date.now(); await service.resume('ticket-1'); const after = Date.now(); - expect(update).toHaveBeenCalledTimes(1); - const [, patch] = update.mock.calls[0] as [string, Record]; + expect(updateWithVersion).toHaveBeenCalledTimes(1); + const [, , patch] = updateWithVersion.mock.calls[0] as [string, number, Record]; expect(patch.status).toBe('running'); expect(patch.pausedAt).toBeNull(); @@ -74,13 +75,13 @@ describe('SlaService pause/resume', () => { status: 'paused', pausedAt: secondPausedAt, } as SLARun; - const update = vi.fn().mockResolvedValue(pausedAgain); - const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(pausedAgain), update } as never; + const updateWithVersion = vi.fn().mockResolvedValue(pausedAgain); + const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(pausedAgain), updateWithVersion } as never; const service = new SlaService(undefined, runsRepo); await service.resume('ticket-1'); - const [, patch] = update.mock.calls[0] as [string, Record]; + const [, , patch] = updateWithVersion.mock.calls[0] as [string, number, Record]; const shifted = (patch.resolutionDueAt as Date).getTime(); // Must be shifted from the ALREADY-shifted 18:00 baseline, not the original 17:00 baseline. expect(shifted).toBeGreaterThan(new Date('2026-01-05T18:00:00.000Z').getTime()); @@ -88,11 +89,11 @@ describe('SlaService pause/resume', () => { it('never resumes a run that is not currently paused', async () => { const runningRun = run({ status: 'running', pausedAt: null }); - const update = vi.fn(); - const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(runningRun), update } as never; + const updateWithVersion = vi.fn(); + const runsRepo = { findByTicketId: vi.fn().mockResolvedValue(runningRun), updateWithVersion } as never; const service = new SlaService(undefined, runsRepo); await service.resume('ticket-1'); - expect(update).not.toHaveBeenCalled(); + expect(updateWithVersion).not.toHaveBeenCalled(); }); });