agent-delegation-unit.test.mjs
153 lines 5.0 KB
Raw
sha256:fbe982a22c05c6fe2e93876f250deecdb43d883f648b4e1810c7546caa9f17db docs: activate KNOWTATION- board identity and preserve livi… Human minor ⚠ breaking 1 day ago
1 /**
2 * Tier 1 — UNIT: delegation schemas, scope math, validateChain, bearer stripping.
3 */
4 import { describe, it, beforeEach, afterEach } from 'node:test';
5 import assert from 'node:assert/strict';
6 import fs from 'node:fs';
7 import path from 'node:path';
8 import { fileURLToPath } from 'node:url';
9
10 import {
11 getDelegationEnabled,
12 validateAgentIdentityRecord,
13 validateConsentRecord,
14 validateGrantRecord,
15 validateAuditRecord,
16 intersectScope,
17 effectiveScope,
18 grantForClient,
19 hashGrantBearer,
20 validateChain,
21 resolveConsentStatus,
22 resolveGrantStatus,
23 seedDelegationFixtures,
24 AGENT_IDENTITY_SCHEMA,
25 DELEGATION_GRANT_SCHEMA,
26 } from '../lib/agent/delegation.mjs';
27 import {
28 writeDelegationPolicy,
29 makeAgentIdentity,
30 makeDelegationConsent,
31 TEST_PRINCIPAL_REF,
32 } from './fixtures/agent/delegation-helpers.mjs';
33
34 const __dirname = path.dirname(fileURLToPath(import.meta.url));
35 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-agent-delegation-unit');
36
37 describe('Agent delegation — unit', () => {
38 beforeEach(() => {
39 fs.rmSync(tmpRoot, { recursive: true, force: true });
40 fs.mkdirSync(tmpRoot, { recursive: true });
41 delete process.env.DELEGATION_ENABLED;
42 });
43
44 afterEach(() => {
45 fs.rmSync(tmpRoot, { recursive: true, force: true });
46 delete process.env.DELEGATION_ENABLED;
47 });
48
49 it('gate defaults off; policy file can enable', () => {
50 const dataDir = path.join(tmpRoot, 'off');
51 fs.mkdirSync(dataDir);
52 assert.equal(getDelegationEnabled(dataDir), false);
53 writeDelegationPolicy(dataDir);
54 assert.equal(getDelegationEnabled(dataDir), true);
55 });
56
57 it('four schemas validate fixture records', () => {
58 const identity = makeAgentIdentity();
59 const consent = makeDelegationConsent();
60 const grant = {
61 schema: DELEGATION_GRANT_SCHEMA,
62 grant_id: 'dgrnt_unit_test01',
63 consent_id: consent.consent_id,
64 actor_agent_id: identity.agent_id,
65 principal_ref: TEST_PRINCIPAL_REF,
66 scope: 'project',
67 workspace_id: 'ws_class_101',
68 expires_at: '2026-06-22T00:00:00Z',
69 revoked_at: null,
70 action_count: 0,
71 issued_at: '2026-06-21T00:00:00Z',
72 };
73 const audit = {
74 schema: 'knowtation.delegation_audit/v0',
75 audit_id: 'daud_unit_test01',
76 grant_id: grant.grant_id,
77 actor_agent_id: identity.agent_id,
78 principal_ref: TEST_PRINCIPAL_REF,
79 action: 'advance_step',
80 evidence_refs: ['proposal:prop_xyz'],
81 occurred_at: '2026-06-21T00:00:00Z',
82 };
83 assert.equal(validateAgentIdentityRecord(identity).ok, true);
84 assert.equal(validateConsentRecord(consent).ok, true);
85 assert.equal(validateGrantRecord(grant).ok, true);
86 assert.equal(validateAuditRecord(audit).ok, true);
87 });
88
89 it('grantForClient never includes bearer hash', () => {
90 const stored = {
91 schema: DELEGATION_GRANT_SCHEMA,
92 grant_id: 'dgrnt_test',
93 grant_bearer_hash: hashGrantBearer('dgrnt_bearer_secret'),
94 };
95 const client = grantForClient(stored);
96 assert.equal(client.grant_bearer_hash, undefined);
97 assert.equal(JSON.stringify(client).includes('bearer'), false);
98 });
99
100 it('grantForClient never includes authority audit counters', () => {
101 const stored = {
102 schema: DELEGATION_GRANT_SCHEMA,
103 grant_id: 'dgrnt_test',
104 grant_bearer_hash: hashGrantBearer('dgrnt_bearer_secret'),
105 audit_sequence: 1,
106 pending_audit_count: 1,
107 last_materialized_audit_sequence: 0,
108 };
109 const client = grantForClient(stored);
110 assert.equal(client.audit_sequence, undefined);
111 assert.equal(client.pending_audit_count, undefined);
112 assert.equal(client.last_materialized_audit_sequence, undefined);
113 });
114
115 it('scope intersection is deterministic', () => {
116 assert.equal(intersectScope('personal', 'org'), 'personal');
117 assert.equal(effectiveScope('project', 'org'), 'project');
118 assert.equal(effectiveScope('personal', 'org'), 'personal');
119 });
120
121 it('validateChain rejects delegate without grant', () => {
122 const dataDir = path.join(tmpRoot, 'chain');
123 fs.mkdirSync(dataDir);
124 writeDelegationPolicy(dataDir);
125 process.env.DELEGATION_ENABLED = '1';
126 const vaultId = 'default';
127 const identity = makeAgentIdentity({ kind: 'delegate' });
128 seedDelegationFixtures(dataDir, vaultId, identity);
129
130 const result = validateChain({
131 dataDir,
132 vaultId,
133 actorAgentId: identity.agent_id,
134 principalRef: TEST_PRINCIPAL_REF,
135 requireGrant: true,
136 });
137 assert.equal(result.ok, false);
138 assert.equal(result.code, 'DELEGATION_CONSENT_REQUIRED');
139 });
140
141 it('consent/grant status derivation', () => {
142 const consent = makeDelegationConsent();
143 assert.equal(resolveConsentStatus(consent), 'active');
144 assert.equal(resolveConsentStatus({ ...consent, revoked_at: '2026-06-21T01:00:00Z' }), 'revoked');
145 const grant = {
146 expires_at: '2099-01-01T00:00:00Z',
147 revoked_at: null,
148 max_actions: 2,
149 action_count: 2,
150 };
151 assert.equal(resolveGrantStatus(grant), 'exhausted');
152 });
153 });
File History 1 commit
sha256:fbe982a22c05c6fe2e93876f250deecdb43d883f648b4e1810c7546caa9f17db docs: activate KNOWTATION- board identity and preserve livi… Human minor 1 day ago