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
73 lines
3.0 KiB
TypeScript
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);
|
|
});
|
|
});
|