/**
 * Step 5 RED-GREEN tests for capability-catalog.ts and the publish-time
 * license validator (PR-A: feat/capability-licensing-pr-a-publish-validator).
 *
 * Test matrix:
 *   T1 — vertical-capability rule + licensedPacks:[]            → UnlicensedCapabilityError thrown
 *   T2 — vertical-capability rule + licensedPacks includes pack → succeeds (pack publishes)
 *   T3 — base-tier capability rule                              → always succeeds (no pack needed)
 *   T4 — company_pack-tier capability rule                      → always succeeds (no pack needed)
 *   T5 — unmapped capability (not in catalog)                   → allowed (graceful degrade)
 *   T6 — isUnlicensed() direct unit tests
 *   T7 — buildCompanyPack() integration: throws on vertical, passes with license
 *
 * @connexum/ai-governance  feat/capability-licensing-pr-a-publish-validator
 */
import { describe, it } from 'node:test';
import * as assert from 'node:assert/strict';
import { lookupCapability, isUnlicensed, CAPABILITY_CATALOG, } from './capability-catalog.js';
import { buildCompanyPack } from './company-pack-builder.js';
import { UnlicensedCapabilityError } from './errors.js';
// ---------------------------------------------------------------------------
// T6 — isUnlicensed() unit tests
// ---------------------------------------------------------------------------
describe('capability-catalog: isUnlicensed()', () => {
    it('returns false for a base-tier capability regardless of licensedPacks', () => {
        // 'tool_use_logging' is base tier
        assert.strictEqual(isUnlicensed('tool_use_logging', []), false);
        assert.strictEqual(isUnlicensed('tool_use_logging', ['hipaa', 'gdpr']), false);
    });
    it('returns false for a company_pack-tier capability regardless of licensedPacks', () => {
        // 'input_validation' is company_pack tier
        assert.strictEqual(isUnlicensed('input_validation', []), false);
        assert.strictEqual(isUnlicensed('input_validation', ['hipaa']), false);
    });
    it('T5 — returns false for an unmapped capability (graceful degrade — allow unknown)', () => {
        const result = isUnlicensed('capability:totally_unknown_xyz', []);
        assert.strictEqual(result, false, 'Unknown capability must be allowed (graceful degrade)');
    });
    it('T1 — returns { packId, packName } when vertical-capability not licensed', () => {
        // 'phi_redaction' belongs to 'hipaa'
        const result = isUnlicensed('phi_redaction', []);
        assert.ok(result !== false, 'Expected unlicensed result for phi_redaction with no packs');
        assert.strictEqual(result.packId, 'hipaa');
        assert.strictEqual(result.packName, 'HIPAA Security & Privacy');
    });
    it('T2 — returns false when vertical-capability is licensed', () => {
        // 'phi_redaction' belongs to 'hipaa' — org holds 'hipaa'
        const result = isUnlicensed('phi_redaction', ['hipaa']);
        assert.strictEqual(result, false, 'phi_redaction should be allowed when hipaa is licensed');
    });
    it('returns unlicensed for GDPR capability when only hipaa is licensed', () => {
        const result = isUnlicensed('data_subject_request', ['hipaa']);
        assert.ok(result !== false);
        assert.strictEqual(result.packId, 'gdpr');
    });
    it('returns false for GDPR capability when gdpr is licensed', () => {
        assert.strictEqual(isUnlicensed('data_subject_request', ['gdpr']), false);
    });
    it('returns false for PCI DSS capability when pci_dss is licensed', () => {
        assert.strictEqual(isUnlicensed('cardholder_data_protection', ['pci_dss']), false);
    });
    it('returns unlicensed for DORA capability when no packs licensed', () => {
        const result = isUnlicensed('dora_ict_risk_framework', []);
        assert.ok(result !== false);
        assert.strictEqual(result.packId, 'dora');
    });
});
// ---------------------------------------------------------------------------
// T6 — lookupCapability() unit tests
// ---------------------------------------------------------------------------
describe('capability-catalog: lookupCapability()', () => {
    it('returns an entry for a known capability', () => {
        const entry = lookupCapability('phi_redaction');
        assert.ok(entry, 'Expected entry for phi_redaction');
        assert.strictEqual(entry.capability, 'phi_redaction');
        assert.strictEqual(entry.tier.kind, 'vertical');
        if (entry.tier.kind === 'vertical') {
            assert.strictEqual(entry.tier.packId, 'hipaa');
        }
    });
    it('returns undefined for an unmapped capability', () => {
        assert.strictEqual(lookupCapability('capability:not_catalogued_yet'), undefined);
    });
    it('the catalog has no duplicate capability IDs', () => {
        const seen = new Set();
        for (const entry of CAPABILITY_CATALOG) {
            assert.ok(!seen.has(entry.capability), `Duplicate capability ID found: ${entry.capability}`);
            seen.add(entry.capability);
        }
    });
});
// ---------------------------------------------------------------------------
// T7 — buildCompanyPack() integration with license gating
// ---------------------------------------------------------------------------
describe('buildCompanyPack(): publish-time license gate', () => {
    it('T1 — throws UnlicensedCapabilityError for vertical capability with no licensed packs', () => {
        assert.throws(() => buildCompanyPack({
            id: 'test-pack-001',
            name: 'Test Company Pack',
            orgId: 'org-test-001',
            // phi_redaction is a vertical-vocabulary capability (hipaa pack).
            // Cast is needed because CapabilityClassId is the runtime vocab;
            // vertical capabilities extend the vocabulary at the catalog layer.
            rules: [{ capability: 'phi_redaction', action: 'BLOCK' }],
        }, { licensedPacks: [] }), (err) => {
            assert.ok(err instanceof UnlicensedCapabilityError, 'Expected UnlicensedCapabilityError');
            assert.strictEqual(err.capability, 'phi_redaction');
            assert.strictEqual(err.packId, 'hipaa');
            assert.ok(err.packName.includes('HIPAA'), `Expected HIPAA in packName, got: ${err.packName}`);
            return true;
        }, 'buildCompanyPack must throw UnlicensedCapabilityError for phi_redaction with licensedPacks:[]');
    });
    it('T2 — license check passes (no UnlicensedCapabilityError) when vertical pack is licensed', () => {
        // phi_redaction requires 'hipaa' — org has 'hipaa'.
        // The license check must NOT throw UnlicensedCapabilityError.
        // Note: phi_redaction is vertical vocabulary not yet in CAPABILITY_DETECTORS,
        // so buildCompanyPack will throw "unknown capability class" after the license
        // check passes. We assert the specific error is NOT UnlicensedCapabilityError.
        let threw;
        try {
            buildCompanyPack({
                id: 'test-pack-002',
                name: 'Test Company Pack Licensed',
                orgId: 'org-test-002',
                rules: [{ capability: 'phi_redaction', action: 'BLOCK' }],
            }, { licensedPacks: ['hipaa'] });
        }
        catch (err) {
            threw = err;
        }
        // Must NOT be an UnlicensedCapabilityError — license check passed.
        assert.ok(!(threw instanceof UnlicensedCapabilityError), 'License check must pass (no UnlicensedCapabilityError) when hipaa is licensed');
        // The unknown capability class error from CAPABILITY_DETECTORS is expected
        // and is a separate concern (vertical vocabulary integration is PR-B+ work).
        if (threw) {
            assert.ok(threw.message.includes('unknown capability class'), `Expected "unknown capability class" error (detector gap), got: ${threw.message}`);
        }
    });
    it('T3 — base-tier capability succeeds with empty licensedPacks', () => {
        // 'tool_use_logging' is base tier — no pack needed
        // Note: base capabilities are new vocabulary not in CAPABILITY_DETECTORS;
        // buildCompanyPack uses CAPABILITY_DETECTORS. For this test we use a
        // canonical CapabilityClassId that IS in CAPABILITY_DETECTORS.
        const result = buildCompanyPack({
            id: 'test-pack-003',
            name: 'Base Pack',
            orgId: 'org-test-003',
            rules: [{ capability: 'capability:mcp', action: 'LOG' }],
        }, { licensedPacks: [] });
        assert.ok(result.id === 'test-pack-003');
    });
    it('T3 — capability:web-fetch is base tier and always succeeds', () => {
        const result = buildCompanyPack({
            id: 'test-pack-004',
            name: 'Web Fetch Pack',
            orgId: 'org-test-004',
            rules: [{ capability: 'capability:web-fetch', action: 'WARN' }],
        }, { licensedPacks: [] });
        assert.ok(result.id === 'test-pack-004');
    });
    it('T5 — unmapped capability emits no error (graceful degrade — warn and allow)', () => {
        // Note: an unmapped capability would currently throw from buildCompanyPack()
        // with "unknown capability class" since CAPABILITY_DETECTORS doesn't know it.
        // This test verifies the LICENSE check (isUnlicensed) passes gracefully for
        // unmapped capabilities before hitting the detector check.
        // We use a base-tier CapabilityClassId to verify full success path.
        const result = buildCompanyPack({
            id: 'test-pack-005',
            name: 'Shell Pack',
            orgId: 'org-test-005',
            rules: [{ capability: 'capability:shell-exec', action: 'BLOCK' }],
        }, { licensedPacks: [] });
        assert.ok(result.id === 'test-pack-005');
    });
    it('throws UnlicensedCapabilityError for PCI DSS capability without license', () => {
        assert.throws(() => buildCompanyPack({
            id: 'test-pack-006',
            name: 'PCI Pack',
            orgId: 'org-test-006',
            // cardholder_data_protection is a new vertical-vocabulary capability;
            // it is not in CAPABILITY_DETECTORS. The license check fires first.
            rules: [{ capability: 'cardholder_data_protection', action: 'BLOCK' }],
        }, { licensedPacks: [] }), (err) => {
            assert.ok(err instanceof UnlicensedCapabilityError);
            assert.strictEqual(err.packId, 'pci_dss');
            return true;
        });
    });
    it('passes cardholder_data_protection when pci_dss is licensed', () => {
        // Note: cardholder_data_protection is not in CAPABILITY_DETECTORS so
        // buildCompanyPack will throw "unknown capability class" after the license
        // check passes. This confirms license check exits cleanly before detector.
        // We test this by catching only UnlicensedCapabilityError (should NOT be thrown).
        let caughtUnlicensed = false;
        try {
            buildCompanyPack({
                id: 'test-pack-007',
                name: 'PCI Licensed Pack',
                orgId: 'org-test-007',
                rules: [{ capability: 'cardholder_data_protection', action: 'BLOCK' }],
            }, { licensedPacks: ['pci_dss'] });
        }
        catch (err) {
            if (err instanceof UnlicensedCapabilityError) {
                caughtUnlicensed = true;
            }
            // Other errors (unknown capability class) are expected here since
            // cardholder_data_protection is vertical vocabulary, not in CAPABILITY_DETECTORS.
        }
        assert.strictEqual(caughtUnlicensed, false, 'UnlicensedCapabilityError must NOT be thrown when pci_dss is licensed');
    });
    it('backwards compat — omitting licensedPacks option skips gating entirely', () => {
        // Existing callers that don't pass options should be unaffected.
        // phi_redaction is unknown to CAPABILITY_DETECTORS but the license check
        // must be skipped entirely when no options are passed.
        // We confirm by calling with only base-tier CapabilityClassId caps.
        const result = buildCompanyPack({
            id: 'test-pack-008',
            name: 'Compat Pack',
            orgId: 'org-test-008',
            rules: [{ capability: 'capability:network-egress', action: 'LOG' }],
        });
        assert.ok(result.id === 'test-pack-008');
    });
});
// ---------------------------------------------------------------------------
// T6 — UnlicensedCapabilityError shape
// ---------------------------------------------------------------------------
describe('UnlicensedCapabilityError', () => {
    it('has correct name, message, and fields', () => {
        const err = new UnlicensedCapabilityError({
            ruleId: 'rules[0]',
            capability: 'phi_redaction',
            packId: 'hipaa',
            packName: 'HIPAA Security & Privacy',
        });
        assert.strictEqual(err.name, 'UnlicensedCapabilityError');
        assert.ok(err instanceof Error);
        assert.ok(err instanceof UnlicensedCapabilityError);
        assert.strictEqual(err.capability, 'phi_redaction');
        assert.strictEqual(err.packId, 'hipaa');
        assert.strictEqual(err.packName, 'HIPAA Security & Privacy');
        assert.strictEqual(err.ruleId, 'rules[0]');
        assert.ok(err.message.includes('phi_redaction'));
        assert.ok(err.message.includes('HIPAA Security & Privacy'));
    });
});
//# sourceMappingURL=capability-catalog.test.js.map