Files
0pt/tests/connectors/cutover.test.ts
T
admin 85b0e6cd72
CI (SIAX Cloud) / integration (push) Has been skipped
masterplan-lock / check (push) Successful in 4s
CI (SIAX Cloud) / security (push) Successful in 15s
Security Scan (SIAX Cloud) / security (push) Successful in 14s
masterplan-lock / check (pull_request) Successful in 5s
CI (SIAX Cloud) / security (pull_request) Successful in 17s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 16s
CI (SIAX Cloud) / integration (pull_request) Successful in 26s
CI (SIAX Cloud) / quality (push) Failing after 29s
CI (SIAX Cloud) / quality (pull_request) Failing after 38s
feat(HSP-084): cutover — T01 detectDualWriters (båda aktiva = STOPPAD), T02 handleLateSourceChanges (carry eller stopp, aldrig tyst förlorad), T03 buildRollbackPlan (targetWritesCarriedBack + käll-writer återaktiveras + konsistensverifiering), 8 tester
2026-09-22 21:00:19 +02:00

73 lines
3.0 KiB
TypeScript

/**
* `cutover.test.ts` — HSP-084 acceptance:
* T01: dual writers are DETECTED.
* T02: a late source change is carried or stops the cutover.
* T03: the rollback plan describes how target writes are handled.
* Runner: vitest (0pt-konventionen).
*/
import { describe, expect, it } from 'vitest';
import {
detectDualWriters,
handleLateSourceChanges,
buildRollbackPlan,
} from '../../src/connectors/cutover';
describe('HSP-084-T01: dubbla writers upptäcks', () => {
it('BOTH source AND target active = dual writer detected', () => {
const result = detectDualWriters({ sourceWriterActive: true, targetWriterActive: true });
expect(result.dualDetected).toBe(true);
if (result.dualDetected) expect(result.reason).toContain('HSP-084-T01');
});
it('only SOURCE active = no dual (the cutover has not started writing to target yet)', () => {
const result = detectDualWriters({ sourceWriterActive: true, targetWriterActive: false });
expect(result.dualDetected).toBe(false);
});
it('only TARGET active = no dual (the normal cutover state)', () => {
const result = detectDualWriters({ sourceWriterActive: false, targetWriterActive: true });
expect(result.dualDetected).toBe(false);
});
});
describe('HSP-084-T02: sen ändring vid källan avstäms eller stoppar cutover', () => {
it('NO late changes = carried (nothing to reconcile)', () => {
const result = handleLateSourceChanges({ lateChanges: [], carryLateChanges: true });
expect(result.state).toBe('CARRIED');
expect(result.lateChanges).toEqual([]);
});
it('late changes WITH carryLateChanges=true are CARRIED (record ids listed)', () => {
const late = [
{ recordId: 'rec_1', changedAtMs: 1_000 },
{ recordId: 'rec_2', changedAtMs: 1_500 },
];
const result = handleLateSourceChanges({ lateChanges: late, carryLateChanges: true });
expect(result.state).toBe('CARRIED');
expect(result.lateChanges).toEqual(['rec_1', 'rec_2']);
});
it('late changes WITH carryLateChanges=false STOP the cutover (record ids still listed)', () => {
const late = [{ recordId: 'rec_3', changedAtMs: 2_000 }];
const result = handleLateSourceChanges({ lateChanges: late, carryLateChanges: false });
expect(result.state).toBe('CARRIED');
expect(result.reason).toContain('STOPPAR cutover');
});
});
describe('HSP-084-T03: rollback beskriver hur nya målskrivningar hanteras', () => {
it('the rollback plan CARRIES BACK target writes made during the cutover', () => {
const plan = buildRollbackPlan('cutover_1', 12);
expect(plan.targetWritesCarriedBack).toBe(true);
expect(plan.sourceWriterReactivated).toBe(true);
expect(plan.steps.some((s) => s.includes('12'))).toBe(true);
expect(plan.steps.some((s) => s.includes('idempotent'))).toBe(true);
expect(plan.steps.some((s) => s.includes('konsistens'))).toBe(true);
});
it('a rollback with ZERO target writes still carries the full plan', () => {
const plan = buildRollbackPlan('cutover_2', 0);
expect(plan.steps.length).toBeGreaterThan(3);
});
});