/**
 * RED-GREEN tests for the trust-score time-series snapshotter.
 *
 * @connexum/ai-governance  trust-score-snapshot (dashboard support)
 */
import { describe, it } from 'node:test';
import * as assert from 'node:assert/strict';
import { TrustScoreSnapshotStore } from './trust-score-snapshot.js';
import { composeThreeDimensionalScore } from './trust-score-three-dim.js';
const dim = (c) => ({
    scope_adherence: c,
    policy_compliance: c,
    anomaly_rate: c,
    incident_history: c,
    behavioral_consistency: c,
    composite: c,
});
const opDim = (c) => ({
    out_of_scope_ingestion: c,
    scope_violation_by_team: c,
    scope_violation_by_data_class: c,
    human_override_frequency: c,
    composite: c,
});
const bDim = (c) => ({
    owasp_agentic_score: c,
    hook_coverage_count: 3,
    declared_capability_class_count: 4,
    has_human_in_loop: true,
    plugin_integrity_verified: true,
    proxy_routed: true,
    composite: c,
});
function makeSnapshot(agentId, at, composite) {
    return {
        agentId,
        at,
        score: composeThreeDimensionalScore({
            agent_activity: dim(composite),
            operator_activity: opDim(composite),
            agent_build_structure: bDim(composite),
        }),
    };
}
describe('trust-score time-series snapshotter', () => {
    it('records and retrieves snapshots in time-ascending order', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        store.record(makeSnapshot('a', '2026-05-12T00:00:00.000Z', 80));
        store.record(makeSnapshot('a', '2026-05-11T00:00:00.000Z', 70));
        const series = store.getTimeSeries('a');
        assert.equal(series.length, 3);
        assert.deepEqual(series.map((s) => s.at), [
            '2026-05-10T00:00:00.000Z',
            '2026-05-11T00:00:00.000Z',
            '2026-05-12T00:00:00.000Z',
        ]);
    });
    it('range filter applies inclusive bounds', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        store.record(makeSnapshot('a', '2026-05-11T00:00:00.000Z', 70));
        store.record(makeSnapshot('a', '2026-05-12T00:00:00.000Z', 80));
        const series = store.getTimeSeries('a', {
            from: '2026-05-11T00:00:00.000Z',
            to: '2026-05-12T00:00:00.000Z',
        });
        assert.equal(series.length, 2);
        assert.equal(series[0].at, '2026-05-11T00:00:00.000Z');
    });
    it('computeDelta returns null for <2 snapshots', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        assert.equal(store.computeDelta('a'), null);
    });
    it('computeDelta returns score change first->last with sampleCount', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        store.record(makeSnapshot('a', '2026-05-11T00:00:00.000Z', 70));
        store.record(makeSnapshot('a', '2026-05-12T00:00:00.000Z', 85));
        const d = store.computeDelta('a');
        assert.ok(d);
        assert.equal(d.delta.composite, 25); // 85 - 60
        assert.equal(d.delta.agent_activity, 25);
        assert.equal(d.delta.operator_activity, 25);
        assert.equal(d.delta.agent_build_structure, 25);
        assert.equal(d.sampleCount, 3);
        assert.equal(d.fromIso, '2026-05-10T00:00:00.000Z');
        assert.equal(d.toIso, '2026-05-12T00:00:00.000Z');
    });
    it('computeDelta with range filter only uses snapshots in window', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-08T00:00:00.000Z', 40));
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        store.record(makeSnapshot('a', '2026-05-12T00:00:00.000Z', 80));
        store.record(makeSnapshot('a', '2026-05-14T00:00:00.000Z', 90));
        const d = store.computeDelta('a', {
            from: '2026-05-10T00:00:00.000Z',
            to: '2026-05-12T00:00:00.000Z',
        });
        assert.ok(d);
        assert.equal(d.delta.composite, 20); // 80 - 60
        assert.equal(d.sampleCount, 2);
    });
    it('per-agent isolation: snapshots for agent A do not bleed into agent B', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        store.record(makeSnapshot('b', '2026-05-10T00:00:00.000Z', 90));
        assert.equal(store.getTimeSeries('a').length, 1);
        assert.equal(store.getTimeSeries('b').length, 1);
        assert.deepEqual(store.listAgents(), ['a', 'b']);
    });
    it('getLatest returns most recent snapshot', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        store.record(makeSnapshot('a', '2026-05-12T00:00:00.000Z', 80));
        assert.equal(store.getLatest('a')?.at, '2026-05-12T00:00:00.000Z');
        assert.equal(store.getLatest('does-not-exist'), undefined);
    });
    it('downsample to N points keeps first + last and evenly samples in between', () => {
        const store = new TrustScoreSnapshotStore();
        for (let i = 0; i < 100; i++) {
            const iso = new Date(2026, 4, 12, 0, 0, i).toISOString();
            store.record(makeSnapshot('a', iso, 50 + i));
        }
        const down = store.downsample('a', 10);
        assert.equal(down.length, 10);
        assert.equal(down[0].at, store.getTimeSeries('a')[0].at);
        assert.equal(down[down.length - 1].at, store.getLatest('a').at);
    });
    it('downsample with maxPoints >= series length returns the full series', () => {
        const store = new TrustScoreSnapshotStore();
        store.record(makeSnapshot('a', '2026-05-10T00:00:00.000Z', 60));
        store.record(makeSnapshot('a', '2026-05-11T00:00:00.000Z', 70));
        const down = store.downsample('a', 100);
        assert.equal(down.length, 2);
    });
    it('downsample with maxPoints <= 0 throws', () => {
        const store = new TrustScoreSnapshotStore();
        assert.throws(() => store.downsample('a', 0), /maxPoints must be > 0/);
        assert.throws(() => store.downsample('a', -5), /maxPoints must be > 0/);
    });
    it('record with empty agentId throws (fail-closed)', () => {
        const store = new TrustScoreSnapshotStore();
        assert.throws(() => store.record(makeSnapshot('', '2026-05-10T00:00:00.000Z', 60)), /agentId is required/);
    });
    it('record with invalid timestamp throws (fail-closed)', () => {
        const store = new TrustScoreSnapshotStore();
        assert.throws(() => store.record(makeSnapshot('a', 'not-a-timestamp', 60)), /valid ISO 8601 timestamp/);
    });
});
//# sourceMappingURL=trust-score-snapshot.test.js.map