agent-delegation-unit.test.mjs
138 lines 4.4 KB
Raw
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor ⚠ breaking 10 days 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('scope intersection is deterministic', () => {
101 assert.equal(intersectScope('personal', 'org'), 'personal');
102 assert.equal(effectiveScope('project', 'org'), 'project');
103 assert.equal(effectiveScope('personal', 'org'), 'personal');
104 });
105
106 it('validateChain rejects delegate without grant', () => {
107 const dataDir = path.join(tmpRoot, 'chain');
108 fs.mkdirSync(dataDir);
109 writeDelegationPolicy(dataDir);
110 process.env.DELEGATION_ENABLED = '1';
111 const vaultId = 'default';
112 const identity = makeAgentIdentity({ kind: 'delegate' });
113 seedDelegationFixtures(dataDir, vaultId, identity);
114
115 const result = validateChain({
116 dataDir,
117 vaultId,
118 actorAgentId: identity.agent_id,
119 principalRef: TEST_PRINCIPAL_REF,
120 requireGrant: true,
121 });
122 assert.equal(result.ok, false);
123 assert.equal(result.code, 'DELEGATION_CONSENT_REQUIRED');
124 });
125
126 it('consent/grant status derivation', () => {
127 const consent = makeDelegationConsent();
128 assert.equal(resolveConsentStatus(consent), 'active');
129 assert.equal(resolveConsentStatus({ ...consent, revoked_at: '2026-06-21T01:00:00Z' }), 'revoked');
130 const grant = {
131 expires_at: '2099-01-01T00:00:00Z',
132 revoked_at: null,
133 max_actions: 2,
134 action_count: 2,
135 };
136 assert.equal(resolveGrantStatus(grant), 'exhausted');
137 });
138 });
File History 2 commits
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor 10 days ago
sha256:d8c648b20a4d53b2673c5c082ee7edfa7b2fc9b11080832da1f38807b6bf940b fix(7C-L1b): route hosted delegation proposals through cani… Human minor 30 days ago