"use strict";
/**
 * RED-GREEN tests for the audit-chain emitter.
 *
 * @connexum/ai-governance  audit-chain unification
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const node_test_1 = require("node:test");
const assert = __importStar(require("node:assert/strict"));
const audit_chain_emitter_1 = require("./audit-chain-emitter");
const sampleOverride = () => ({
    overrideId: 'ov-1',
    orgId: 'org-x',
    agentId: 'agent-1',
    capability: 'capability:mcp',
    baselineAction: 'BLOCK',
    relaxedAction: 'LOG',
    approver: 'cto-alice',
    justification: 'internal use only',
    approvedAt: '2026-05-12T10:00:00.000Z',
    composedAt: '2026-05-12T10:00:01.000Z',
});
const samplePackVersion = (kind = 'VERSION_PUBLISHED') => ({
    kind,
    packId: 'co-pack',
    version: '1.0.0',
    actor: 'alice',
    timestamp: '2026-05-12T10:00:05.000Z',
});
const sampleRegistration = () => ({
    kind: 'AGENT_REGISTERED',
    agentId: 'agent-1',
    orgId: 'org-x',
    tool: 'Bash',
    timestamp: '2026-05-12T10:00:10.000Z',
    manifest: {
        agentId: 'agent-1',
        name: 'a',
        tools: ['Bash'],
        hooks: { preToolUse: true, postToolUse: false, postResponse: false },
        declaredApprovalGates: [{ capability: 'capability:filesystem-write', reviewer: 'cto-alice' }],
        declaredForbiddenCapabilities: ['capability:network-egress'],
        pluginIntegrityVerified: true,
        llmProvider: 'anthropic/claude-opus-4-7',
    },
});
(0, node_test_1.describe)('audit-chain emitter', () => {
    (0, node_test_1.it)('append assigns monotonic seq starting at 0', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        const a = chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        const b = chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        assert.equal(a.seq, 0);
        assert.equal(b.seq, 1);
        assert.equal(chain.size(), 2);
    });
    (0, node_test_1.it)('first entry has prevHash = GENESIS', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        const a = chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        assert.equal(a.prevHash, 'GENESIS');
    });
    (0, node_test_1.it)('subsequent entries reference prior selfHash', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        const a = chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        const b = chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        assert.equal(b.prevHash, a.selfHash);
    });
    (0, node_test_1.it)('every entry has a non-empty selfHash', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        const a = chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        assert.ok(a.selfHash.length > 0);
        assert.match(a.selfHash, /^[A-Za-z0-9_-]+$/);
    });
    (0, node_test_1.it)('source mapping covers all 5 supported event classes', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        chain.append((0, audit_chain_emitter_1.fromSelfRegistrationEvent)(sampleRegistration()));
        chain.append((0, audit_chain_emitter_1.fromSelfRegistrationEvent)({ ...sampleRegistration(), kind: 'AGENT_TOOL_OBSERVED', tool: 'WebFetch' }));
        chain.append((0, audit_chain_emitter_1.fromApprovalNotification)({
            ticketId: 'tic-1',
            orgId: 'org-x',
            recipientId: 'reviewer-bob',
            channel: 'email',
            delivered: true,
            at: '2026-05-12T10:00:20.000Z',
        }));
        const sources = chain.list().map((e) => e.source);
        assert.deepEqual(sources, [
            'OVERRIDE',
            'PACK_VERSION',
            'AGENT_REGISTERED',
            'AGENT_TOOL_OBSERVED',
            'APPROVAL_NOTIFICATION',
        ]);
    });
    (0, node_test_1.it)('onAppend sink fires per entry', () => {
        const observed = [];
        const chain = new audit_chain_emitter_1.AuditChainEmitter({ onAppend: (e) => observed.push(e) });
        chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        assert.equal(observed.length, 2);
        assert.equal(observed[0].source, 'OVERRIDE');
        assert.equal(observed[1].source, 'PACK_VERSION');
    });
    (0, node_test_1.it)('verifyAuditChain returns OK for an intact chain', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        chain.append((0, audit_chain_emitter_1.fromSelfRegistrationEvent)(sampleRegistration()));
        const result = (0, audit_chain_emitter_1.verifyAuditChain)(chain.list());
        assert.equal(result.code, 'OK');
    });
    (0, node_test_1.it)('verifyAuditChain detects payload tampering (SELF_HASH_MISMATCH)', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        const tampered = chain.list();
        // Tamper with entry 0's data without recomputing the hash.
        tampered[0] = {
            ...tampered[0],
            data: { ...tampered[0].data, justification: 'attacker rewrite' },
        };
        const result = (0, audit_chain_emitter_1.verifyAuditChain)(tampered);
        assert.equal(result.code, 'SELF_HASH_MISMATCH');
        if (result.code === 'SELF_HASH_MISMATCH') {
            assert.equal(result.seq, 0);
        }
    });
    (0, node_test_1.it)('verifyAuditChain detects deleted middle entry (PREV_HASH_MISMATCH)', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        chain.append((0, audit_chain_emitter_1.fromSelfRegistrationEvent)(sampleRegistration()));
        const tampered = chain.list();
        tampered.splice(1, 1); // remove the middle entry
        // Renumber to keep seqs contiguous so we hit PREV_HASH_MISMATCH, not OUT_OF_ORDER.
        tampered[1] = { ...tampered[1], seq: 1 };
        const result = (0, audit_chain_emitter_1.verifyAuditChain)(tampered);
        assert.equal(result.code, 'PREV_HASH_MISMATCH');
    });
    (0, node_test_1.it)('verifyAuditChain detects out-of-order seq', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        const reordered = chain.list().reverse();
        const result = (0, audit_chain_emitter_1.verifyAuditChain)(reordered);
        assert.equal(result.code, 'OUT_OF_ORDER_SEQ');
    });
    (0, node_test_1.it)('approval notification audit input handles failed deliveries with structured error', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        const entry = chain.append((0, audit_chain_emitter_1.fromApprovalNotification)({
            ticketId: 'tic-1',
            orgId: 'org-x',
            recipientId: 'reviewer-bob',
            channel: 'sms',
            delivered: false,
            error: 'twilio status=400',
            at: '2026-05-12T10:00:30.000Z',
        }));
        assert.equal(entry.source, 'APPROVAL_NOTIFICATION');
        assert.equal(entry.data.delivered, false);
        assert.equal(entry.data.error, 'twilio status=400');
    });
    (0, node_test_1.it)('round-trip through JSON preserves verification', () => {
        const chain = new audit_chain_emitter_1.AuditChainEmitter();
        chain.append((0, audit_chain_emitter_1.fromOverrideEvent)(sampleOverride()));
        chain.append((0, audit_chain_emitter_1.fromPackVersionEvent)(samplePackVersion(), 'org-x'));
        const serialized = JSON.stringify(chain.list());
        const restored = JSON.parse(serialized);
        const result = (0, audit_chain_emitter_1.verifyAuditChain)(restored);
        assert.equal(result.code, 'OK');
    });
    (0, node_test_1.it)('empty chain verifies as OK', () => {
        const result = (0, audit_chain_emitter_1.verifyAuditChain)([]);
        assert.equal(result.code, 'OK');
    });
    (0, node_test_1.it)('F-NEW-DASHBOARD-E2E-006: fromSelfRegistrationEvent persists manifest build-shape fields', () => {
        // Pre-fix the data field only carried `tool` + `toolInventory`. The
        // trust-score-refresh pipeline could not recover `declaredApprovalGates`
        // / `hooks` / `llmProvider` etc., so has_human_in_loop was always false.
        const entry = (0, audit_chain_emitter_1.fromSelfRegistrationEvent)(sampleRegistration());
        const data = entry.data;
        const manifest = data['manifest'];
        assert.ok(manifest, 'manifest must be persisted on the audit entry');
        assert.deepEqual(manifest['hooks'], { preToolUse: true, postToolUse: false, postResponse: false });
        assert.deepEqual(manifest['declaredApprovalGates'], [{ capability: 'capability:filesystem-write', reviewer: 'cto-alice' }]);
        assert.deepEqual(manifest['declaredForbiddenCapabilities'], ['capability:network-egress']);
        assert.equal(manifest['pluginIntegrityVerified'], true);
        assert.equal(manifest['llmProvider'], 'anthropic/claude-opus-4-7');
    });
});
//# sourceMappingURL=audit-chain-emitter.test.js.map