godcrm/backend/services/verification/__tests__/validateOverride.test.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

274 lines
9.3 KiB
JavaScript

// ADR-0011 · Phase E2 · Unit tests for tighten-only override validator (C-8).
//
// Covers all 7 enforcement rules per validateOverride.js:
// 1. cooldown_seconds — RAISE only
// 2. required_methods — RAISE only
// 3. guards — SUPERSET only (no removal)
// 4. available_methods — SUBSET only (no addition)
// 5. ttl_seconds — SHORTEN only (null base allows any positive)
// 6. rate_limit — fewer attempts / longer window
// 7. policy — 'all' > 'any_n' (no downgrade)
//
// Plus edge cases: null override, infeasible required_methods, pass-through.
import { describe, it, expect } from 'vitest';
import { validateVerificationOverride } from '../validateOverride.js';
const BASE = Object.freeze({
available_methods: ['totp', 'captcha'],
required_methods: 1,
cooldown_seconds: 300,
cooldown_ms: 300000,
ttl_seconds: 3600,
ttl_ms: 3600000,
locks_on_statuses: [],
unlocks_on_statuses: [],
guards: ['row_update_guard'],
policy: 'any_n',
rate_limit: { window_seconds: 60, max_attempts: 5 },
method_config: {},
});
describe('validateVerificationOverride — null/empty', () => {
it('null override → effective = base copy', () => {
const r = validateVerificationOverride(BASE, null);
expect(r.ok).toBe(true);
expect(r.effective).toEqual(BASE);
});
it('undefined override → ok', () => {
const r = validateVerificationOverride(BASE, undefined);
expect(r.ok).toBe(true);
});
it('non-object override → reject', () => {
expect(validateVerificationOverride(BASE, 'string').ok).toBe(false);
expect(validateVerificationOverride(BASE, []).ok).toBe(false);
});
it('missing base → reject', () => {
expect(validateVerificationOverride(null, {}).ok).toBe(false);
});
});
describe('Rule 1 — cooldown_seconds (RAISE only)', () => {
it('raise → accept', () => {
const r = validateVerificationOverride(BASE, { cooldown_seconds: 600 });
expect(r.ok).toBe(true);
expect(r.effective.cooldown_seconds).toBe(600);
expect(r.effective.cooldown_ms).toBe(600000);
});
it('equal → accept', () => {
const r = validateVerificationOverride(BASE, { cooldown_seconds: 300 });
expect(r.ok).toBe(true);
});
it('lower → reject', () => {
const r = validateVerificationOverride(BASE, { cooldown_seconds: 60 });
expect(r.ok).toBe(false);
expect(r.field).toBe('cooldown_seconds');
});
it('legacy cooldown_ms is converted', () => {
const r = validateVerificationOverride(BASE, { cooldown_ms: 600000 });
expect(r.ok).toBe(true);
expect(r.effective.cooldown_seconds).toBe(600);
});
});
describe('Rule 2 — required_methods (RAISE only)', () => {
it('raise within available → accept', () => {
const r = validateVerificationOverride(BASE, { required_methods: 2 });
expect(r.ok).toBe(true);
expect(r.effective.required_methods).toBe(2);
});
it('equal → accept', () => {
const r = validateVerificationOverride(BASE, { required_methods: 1 });
expect(r.ok).toBe(true);
});
it('lower → reject', () => {
const base = { ...BASE, required_methods: 2 };
const r = validateVerificationOverride(base, { required_methods: 1 });
expect(r.ok).toBe(false);
expect(r.field).toBe('required_methods');
});
it('exceeds available_methods → reject (infeasible)', () => {
const r = validateVerificationOverride(BASE, { required_methods: 5 });
expect(r.ok).toBe(false);
expect(r.field).toBe('required_methods');
});
it('non-integer → reject', () => {
const r = validateVerificationOverride(BASE, { required_methods: 1.5 });
expect(r.ok).toBe(false);
});
});
describe('Rule 3 — guards (SUPERSET only)', () => {
it('superset → accept', () => {
const r = validateVerificationOverride(BASE, { guards: ['row_update_guard', 'extra_guard'] });
expect(r.ok).toBe(true);
expect(r.effective.guards).toEqual(['row_update_guard', 'extra_guard']);
});
it('equal set → accept', () => {
const r = validateVerificationOverride(BASE, { guards: ['row_update_guard'] });
expect(r.ok).toBe(true);
});
it('drops base guard → reject', () => {
const r = validateVerificationOverride(BASE, { guards: ['extra_guard'] });
expect(r.ok).toBe(false);
expect(r.field).toBe('guards');
});
it('empty guards drops base → reject', () => {
const r = validateVerificationOverride(BASE, { guards: [] });
expect(r.ok).toBe(false);
});
});
describe('Rule 4 — available_methods (SUBSET only)', () => {
it('subset → accept', () => {
const r = validateVerificationOverride(BASE, { available_methods: ['totp'] });
expect(r.ok).toBe(true);
expect(r.effective.available_methods).toEqual(['totp']);
});
it('adds new method not in base → reject', () => {
const r = validateVerificationOverride(BASE, { available_methods: ['totp', 'sms'] });
expect(r.ok).toBe(false);
expect(r.field).toBe('available_methods');
});
it('empty array → reject', () => {
const r = validateVerificationOverride(BASE, { available_methods: [] });
expect(r.ok).toBe(false);
});
it('shrinks below base required_methods → reject', () => {
const base = { ...BASE, required_methods: 2 };
const r = validateVerificationOverride(base, { available_methods: ['totp'] });
expect(r.ok).toBe(false);
expect(r.field).toBe('required_methods');
});
});
describe('Rule 5 — ttl_seconds (SHORTEN only)', () => {
it('shorten → accept', () => {
const r = validateVerificationOverride(BASE, { ttl_seconds: 1800 });
expect(r.ok).toBe(true);
expect(r.effective.ttl_seconds).toBe(1800);
});
it('lengthen → reject', () => {
const r = validateVerificationOverride(BASE, { ttl_seconds: 7200 });
expect(r.ok).toBe(false);
expect(r.field).toBe('ttl_seconds');
});
it('clear ttl when base set → reject', () => {
const r = validateVerificationOverride(BASE, { ttl_seconds: null });
expect(r.ok).toBe(false);
});
it('set ttl when base null → accept', () => {
const base = { ...BASE, ttl_seconds: null, ttl_ms: null };
const r = validateVerificationOverride(base, { ttl_seconds: 600 });
expect(r.ok).toBe(true);
});
it('negative ttl → reject', () => {
const r = validateVerificationOverride(BASE, { ttl_seconds: -1 });
expect(r.ok).toBe(false);
});
});
describe('Rule 6 — rate_limit (tighter only)', () => {
it('fewer attempts → accept', () => {
const r = validateVerificationOverride(BASE, { rate_limit: { window_seconds: 60, max_attempts: 3 } });
expect(r.ok).toBe(true);
});
it('longer window → accept', () => {
const r = validateVerificationOverride(BASE, { rate_limit: { window_seconds: 120, max_attempts: 5 } });
expect(r.ok).toBe(true);
});
it('more attempts → reject', () => {
const r = validateVerificationOverride(BASE, { rate_limit: { window_seconds: 60, max_attempts: 10 } });
expect(r.ok).toBe(false);
expect(r.field).toBe('rate_limit.max_attempts');
});
it('shorter window → reject', () => {
const r = validateVerificationOverride(BASE, { rate_limit: { window_seconds: 30, max_attempts: 5 } });
expect(r.ok).toBe(false);
expect(r.field).toBe('rate_limit.window_seconds');
});
it('clear rate_limit when base set → reject', () => {
const r = validateVerificationOverride(BASE, { rate_limit: null });
expect(r.ok).toBe(false);
});
it('set rate_limit when base null → accept', () => {
const base = { ...BASE, rate_limit: null };
const r = validateVerificationOverride(base, { rate_limit: { window_seconds: 60, max_attempts: 3 } });
expect(r.ok).toBe(true);
});
});
describe('Rule 7 — policy (any_n → all only, never reverse)', () => {
it('any_n → all → accept', () => {
const r = validateVerificationOverride(BASE, { policy: 'all' });
expect(r.ok).toBe(true);
expect(r.effective.policy).toBe('all');
});
it('all → any_n → reject', () => {
const base = { ...BASE, policy: 'all' };
const r = validateVerificationOverride(base, { policy: 'any_n' });
expect(r.ok).toBe(false);
expect(r.field).toBe('policy');
});
it('invalid policy → reject', () => {
const r = validateVerificationOverride(BASE, { policy: 'maybe' });
expect(r.ok).toBe(false);
});
});
describe('Pass-through fields', () => {
it('locks_on_statuses passes through', () => {
const r = validateVerificationOverride(BASE, { locks_on_statuses: ['done'] });
expect(r.ok).toBe(true);
expect(r.effective.locks_on_statuses).toEqual(['done']);
});
it('ADR-0003 extras pass through (required_reviewer_ids, screenshot_atom, diff_hash_match)', () => {
const r = validateVerificationOverride(BASE, {
required_reviewer_ids: [42, 99],
screenshot_atom: 'atoms/abc',
diff_hash_match: true,
});
expect(r.ok).toBe(true);
expect(r.effective.required_reviewer_ids).toEqual([42, 99]);
expect(r.effective.screenshot_atom).toBe('atoms/abc');
expect(r.effective.diff_hash_match).toBe(true);
});
it('method_config merged with base', () => {
const base = { ...BASE, method_config: { totp: { window: 1 } } };
const r = validateVerificationOverride(base, { method_config: { captcha: { provider: 'h' } } });
expect(r.ok).toBe(true);
expect(r.effective.method_config).toEqual({
totp: { window: 1 },
captcha: { provider: 'h' },
});
});
});