/**
 * RED-GREEN tests for F-NEW-PRODUCT-CAPABILITY-CLASS-RULES (W1.5 of
 * F-NEW-PRODUCT-AGENT-VISIBILITY-CLUSTER-2026-05-12).
 *
 * Customer-path probe:
 *   1. Customer authors a company pack with `{ capability: 'capability:mcp', action: 'BLOCK' }`
 *   2. Agent attempts an MCP tool call
 *   3. PackEvaluator returns DENY (BLOCK -> DENY in actionToVerdict)
 *
 * Same probe verified for: WEB_FETCH, EXTERNAL_DB_WRITE, SHELL_EXEC,
 * FILESYSTEM_WRITE, NETWORK_EGRESS, SECRET_ACCESS, and the REQUIRE_APPROVAL
 * variant (-> PENDING which queues an ApprovalRecord through the existing
 * pack-verdict-adapter pipeline).
 *
 * Failure modes intentionally probed:
 *   - Unknown capability id at build time -> throws (fail closed)
 *   - Tool call that should NOT match the capability stays ALLOW
 *   - REQUIRE_APPROVAL maps to PENDING (the approval-required customer path)
 *
 * @connexum/ai-governance
 */
import { describe, it } from 'node:test';
import * as assert from 'node:assert/strict';
import { PackEvaluator } from './pack-evaluator.js';
import { PackRegistry } from '../packs/index.js';
import { EventBus } from '../audit/event-bus.js';
import { createActionContext } from '../adapters/index.js';
import { buildCompanyPack } from './company-pack-builder.js';
import { CAPABILITY_CLASS } from './capability-classes.js';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
function freshRegistryWith(pack) {
    const registry = new PackRegistry();
    registry.register(pack);
    registry.activate(pack.id);
    return registry;
}
function freshEvaluator(registry) {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-class-'));
    const bus = new EventBus({ eventDir: dir, agentName: 'test-cap' });
    return new PackEvaluator(registry, bus, { ttlMs: 60_000, enableCache: false });
}
function action(toolName, input = {}) {
    return createActionContext(toolName, input, { name: 'cap-test-agent', orgId: 'tenant-cap' });
}
describe('F-NEW-PRODUCT-CAPABILITY-CLASS-RULES — capability-class rule vocabulary', () => {
    it('BLOCK on capability:mcp denies an MCP tool call', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-1',
            name: 'No MCP company rule',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.MCP, action: 'BLOCK' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const verdict = evaluator.evaluate('tenant-cap', 'agent-1', action('mcp__list_resources'));
        assert.equal(verdict.verdict, 'DENY');
        assert.ok(verdict.triggeringClassifierIds.includes(CAPABILITY_CLASS.MCP));
        assert.ok(verdict.triggeringPackIds.includes('co-pack-1'));
    });
    it('REQUIRE_APPROVAL on capability:external-db-write returns PENDING for DROP TABLE', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-db',
            name: 'No destructive SQL without approval',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.EXTERNAL_DB_WRITE, action: 'REQUIRE_APPROVAL' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const verdict = evaluator.evaluate('tenant-cap', 'agent-1', action('postgres_query', { query: 'DROP TABLE users' }));
        assert.equal(verdict.verdict, 'PENDING');
        assert.ok(verdict.triggeringClassifierIds.includes(CAPABILITY_CLASS.EXTERNAL_DB_WRITE));
    });
    it('BLOCK on capability:external-db-write does NOT fire on a SELECT (read-only)', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-db-2',
            name: 'No destructive SQL',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.EXTERNAL_DB_WRITE, action: 'BLOCK' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const verdict = evaluator.evaluate('tenant-cap', 'agent-1', action('postgres_query', { query: 'SELECT * FROM users LIMIT 10' }));
        assert.equal(verdict.verdict, 'ALLOW');
    });
    it('BLOCK on capability:shell-exec denies Bash', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-sh',
            name: 'No shell',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.SHELL_EXEC, action: 'BLOCK' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const verdict = evaluator.evaluate('tenant-cap', 'agent-1', action('Bash', { command: 'ls' }));
        assert.equal(verdict.verdict, 'DENY');
    });
    it('BLOCK on capability:web-fetch denies WebFetch and Fetch alike', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-web',
            name: 'No web fetch',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.WEB_FETCH, action: 'BLOCK' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const v1 = evaluator.evaluate('tenant-cap', 'a', action('WebFetch', { url: 'https://x' }));
        const v2 = evaluator.evaluate('tenant-cap', 'a', action('fetch', { url: 'https://x' }));
        assert.equal(v1.verdict, 'DENY');
        assert.equal(v2.verdict, 'DENY');
    });
    it('REQUIRE_APPROVAL on capability:filesystem-write returns PENDING for Write', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-fs',
            name: 'Approval on file writes',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.FILESYSTEM_WRITE, action: 'REQUIRE_APPROVAL' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const verdict = evaluator.evaluate('tenant-cap', 'agent-1', action('Write', { file_path: '/etc/passwd', content: 'x' }));
        assert.equal(verdict.verdict, 'PENDING');
    });
    it('BLOCK on capability:secret-access fires on tool name AND on secret-shaped args', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-sec',
            name: 'No secrets',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.SECRET_ACCESS, action: 'BLOCK' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const byTool = evaluator.evaluate('tenant-cap', 'a', action('vault_read', { path: 'secret/data/db' }));
        const byArgs = evaluator.evaluate('tenant-cap', 'a', action('http_post', { url: 'https://x', headers: { Authorization: 'Bearer secrettoken1' } }));
        assert.equal(byTool.verdict, 'DENY');
        assert.equal(byArgs.verdict, 'DENY');
    });
    it('unrelated tool call stays ALLOW when capability rules do not match', () => {
        const pack = buildCompanyPack({
            id: 'co-pack-clean',
            name: 'No MCP',
            orgId: 'tenant-cap',
            rules: [{ capability: CAPABILITY_CLASS.MCP, action: 'BLOCK' }],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const verdict = evaluator.evaluate('tenant-cap', 'a', action('summarize', { text: 'hello' }));
        assert.equal(verdict.verdict, 'ALLOW');
        assert.equal(verdict.triggeringClassifierIds.length, 0);
    });
    it('buildCompanyPack throws on unknown capability id (fail-closed)', () => {
        assert.throws(() => buildCompanyPack({
            id: 'bad',
            name: 'bad',
            orgId: 'tenant-cap',
            // @ts-expect-error — intentional unknown capability id
            rules: [{ capability: 'capability:does-not-exist', action: 'BLOCK' }],
        }), /unknown capability class/i);
    });
    it('strictest-wins across multiple rules on the same call (BLOCK over REQUIRE_APPROVAL)', () => {
        // Two rules on different capability classes both fire for the same call:
        // web-fetch returns BLOCK; network-egress returns REQUIRE_APPROVAL. Strictest wins => DENY.
        const pack = buildCompanyPack({
            id: 'co-pack-multi',
            name: 'Multi-class',
            orgId: 'tenant-cap',
            rules: [
                { capability: CAPABILITY_CLASS.WEB_FETCH, action: 'BLOCK' },
                { capability: CAPABILITY_CLASS.NETWORK_EGRESS, action: 'REQUIRE_APPROVAL' },
            ],
        });
        const evaluator = freshEvaluator(freshRegistryWith(pack));
        const verdict = evaluator.evaluate('tenant-cap', 'a', action('WebFetch', { url: 'https://x' }));
        assert.equal(verdict.verdict, 'DENY');
        assert.ok(verdict.triggeringClassifierIds.includes(CAPABILITY_CLASS.WEB_FETCH));
    });
});
//# sourceMappingURL=capability-classes.test.js.map