delegation-hosted-proposal-l1b.test.mjs
387 lines 13.5 KB
Raw
sha256:e4c529f14a0bb908c1caaaeb3f95f3623a1a82e636e7e3722ca2cd3dc9821263 security: npm audit fix pre-bridge 2026-07-29 Human 40 days ago
1 /**
2 * Phase 7C-L1b — hosted delegation proposal parity (canister propose + bridge apply).
3 *
4 * Tiers: unit, integration, e2e, stress, data-integrity, performance, security.
5 */
6 import { describe, it, beforeEach, afterEach } from 'node:test';
7 import assert from 'node:assert/strict';
8 import fs from 'node:fs';
9 import path from 'node:path';
10 import { fileURLToPath } from 'node:url';
11
12 import {
13 mergeDelegationFrontmatter,
14 normalizeCanisterProposalForDelegationPrecheck,
15 isDelegationProposalIntent,
16 createDelegationProposalOnCanister,
17 applyApprovedDelegationProposalFromCanister,
18 FM_PROPOSAL_SOURCE,
19 FM_RECORD_KIND,
20 } from '../lib/agent/delegation-hosted-proposal.mjs';
21 import {
22 DELEGATION_PROPOSAL_SOURCE,
23 precheckApprovedDelegationProposal,
24 applyDelegationProposalToIndex,
25 handleDelegationGrantMintRequest,
26 getAgentIdentity,
27 getConsent,
28 seedDelegationFixtures,
29 } from '../lib/agent/delegation.mjs';
30 import { writeDelegationPolicy, makeAgentIdentity, makeDelegationConsent, TEST_USER_ID } from './fixtures/agent/delegation-helpers.mjs';
31
32 const __dirname = path.dirname(fileURLToPath(import.meta.url));
33 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-delegation-l1b');
34
35 describe('7C-L1b delegation hosted proposal — unit', () => {
36 it('isDelegationProposalIntent recognizes delegation intents', () => {
37 assert.equal(isDelegationProposalIntent('agent_identity_register'), true);
38 assert.equal(isDelegationProposalIntent('delegation_consent_create'), true);
39 assert.equal(isDelegationProposalIntent('flow_edit'), false);
40 });
41
42 it('mergeDelegationFrontmatter embeds source and record kind', () => {
43 const fm = mergeDelegationFrontmatter({ agent_id: 'agent_x' }, { record_kind: 'agent_identity', agent_id: 'agent_x' });
44 assert.equal(fm[FM_PROPOSAL_SOURCE], DELEGATION_PROPOSAL_SOURCE);
45 assert.equal(fm[FM_RECORD_KIND], 'agent_identity');
46 assert.equal(fm.agent_id, 'agent_x');
47 });
48
49 it('normalizeCanisterProposalForDelegationPrecheck maps frontmatter to delegation_meta', () => {
50 const fm = mergeDelegationFrontmatter({}, { record_kind: 'delegation_consent', consent_id: 'dcons_abc' });
51 const normalized = normalizeCanisterProposalForDelegationPrecheck({
52 proposal_id: 'prop-1',
53 intent: 'delegation_consent_create',
54 status: 'approved',
55 vault_id: 'Business',
56 body: '{}',
57 frontmatter: JSON.stringify(fm),
58 });
59 assert.ok(normalized);
60 assert.equal(normalized.source, DELEGATION_PROPOSAL_SOURCE);
61 assert.equal(normalized.delegation_meta.record_kind, 'delegation_consent');
62 assert.equal(normalized.delegation_meta.consent_id, 'dcons_abc');
63 });
64 });
65
66 describe('7C-L1b delegation hosted proposal — integration', () => {
67 const dataDir = path.join(tmpRoot, 'integration', 'data');
68 const vaultId = 'Business';
69
70 beforeEach(() => {
71 fs.rmSync(path.join(tmpRoot, 'integration'), { recursive: true, force: true });
72 fs.mkdirSync(dataDir, { recursive: true });
73 writeDelegationPolicy(dataDir);
74 process.env.DELEGATION_ENABLED = '1';
75 });
76
77 afterEach(() => {
78 delete process.env.DELEGATION_ENABLED;
79 });
80
81 it('createDelegationProposalOnCanister POSTs frontmatter to canister', async () => {
82 const calls = [];
83 const originalFetch = globalThis.fetch;
84 globalThis.fetch = async (url, init) => {
85 calls.push({ url: String(url), init });
86 return {
87 ok: true,
88 status: 200,
89 text: async () =>
90 JSON.stringify({ proposal_id: 'prop-canister-1', path: 'meta/agents/smoke.md', status: 'proposed' }),
91 };
92 };
93 try {
94 const proposal = await createDelegationProposalOnCanister({
95 canisterUrl: 'https://canister.test',
96 headers: { 'X-User-Id': 'owner', 'X-Vault-Id': vaultId },
97 input: {
98 path: 'meta/agents/smoke.md',
99 body: '{"schema":"knowtation.agent_identity/v0"}',
100 intent: 'agent_identity_register',
101 vault_id: vaultId,
102 review_queue: 'delegation',
103 delegation_meta: { record_kind: 'agent_identity', agent_id: 'agent_smoke01' },
104 },
105 });
106 assert.equal(proposal.proposal_id, 'prop-canister-1');
107 assert.equal(calls.length, 1);
108 assert.match(calls[0].url, /\/api\/v1\/proposals$/);
109 const sent = JSON.parse(String(calls[0].init.body));
110 assert.equal(sent.intent, 'agent_identity_register');
111 assert.equal(sent.frontmatter[FM_PROPOSAL_SOURCE], DELEGATION_PROPOSAL_SOURCE);
112 } finally {
113 globalThis.fetch = originalFetch;
114 }
115 });
116
117 it('applyApprovedDelegationProposalFromCanister updates bridge index after canister approve', async () => {
118 const identity = makeAgentIdentity({ agentId: 'agent_l1b_smoke01' });
119 const body = JSON.stringify(identity);
120 const fm = mergeDelegationFrontmatter(
121 { agent_id: identity.agent_id },
122 { record_kind: 'agent_identity', agent_id: identity.agent_id },
123 );
124 const originalFetch = globalThis.fetch;
125 globalThis.fetch = async () => ({
126 ok: true,
127 status: 200,
128 text: async () =>
129 JSON.stringify({
130 proposal_id: 'prop-l1b-identity',
131 path: 'meta/agents/l1b01.md',
132 status: 'approved',
133 vault_id: vaultId,
134 intent: 'agent_identity_register',
135 created_by: TEST_USER_ID,
136 body,
137 frontmatter: JSON.stringify(fm),
138 }),
139 });
140 try {
141 const result = await applyApprovedDelegationProposalFromCanister({
142 dataDir,
143 canisterUrl: 'https://canister.test',
144 headers: { 'X-User-Id': 'owner', 'X-Vault-Id': vaultId },
145 proposalId: 'prop-l1b-identity',
146 });
147 assert.equal(result.ok, true);
148 assert.equal(result.payload.applied, true);
149 const stored = getAgentIdentity(dataDir, vaultId, identity.agent_id);
150 assert.ok(stored);
151 assert.equal(stored.agent_id, identity.agent_id);
152 } finally {
153 globalThis.fetch = originalFetch;
154 }
155 });
156 });
157
158 describe('7C-L1b delegation hosted proposal — e2e', () => {
159 const dataDir = path.join(tmpRoot, 'e2e', 'data');
160 const vaultId = 'Business';
161
162 beforeEach(() => {
163 fs.rmSync(path.join(tmpRoot, 'e2e'), { recursive: true, force: true });
164 fs.mkdirSync(dataDir, { recursive: true });
165 writeDelegationPolicy(dataDir);
166 process.env.DELEGATION_ENABLED = '1';
167 });
168
169 afterEach(() => {
170 delete process.env.DELEGATION_ENABLED;
171 });
172
173 it('identity approve apply → consent approve apply → grant mint', async () => {
174 const identity = makeAgentIdentity({ agentId: 'agent_l1b_e2e01' });
175 const consentBody = makeDelegationConsent({
176 consentId: 'dcons_l1b_e2e01',
177 agentId: identity.agent_id,
178 });
179
180 const identityProposal = {
181 proposal_id: 'prop-id-e2e',
182 path: 'meta/agents/l1b_e2e.md',
183 status: 'approved',
184 vault_id: vaultId,
185 intent: 'agent_identity_register',
186 created_by: TEST_USER_ID,
187 body: JSON.stringify(identity),
188 frontmatter: JSON.stringify(
189 mergeDelegationFrontmatter({}, { record_kind: 'agent_identity', agent_id: identity.agent_id }),
190 ),
191 };
192 const consentProposal = {
193 proposal_id: 'prop-consent-e2e',
194 path: 'meta/delegation/consents/l1b_e2e.md',
195 status: 'approved',
196 vault_id: vaultId,
197 intent: 'delegation_consent_create',
198 created_by: TEST_USER_ID,
199 body: JSON.stringify(consentBody),
200 frontmatter: JSON.stringify(
201 mergeDelegationFrontmatter({}, { record_kind: 'delegation_consent', consent_id: consentBody.consent_id }),
202 ),
203 };
204
205 let fetchCount = 0;
206 const originalFetch = globalThis.fetch;
207 globalThis.fetch = async () => {
208 fetchCount += 1;
209 const payload = fetchCount === 1 ? identityProposal : consentProposal;
210 return { ok: true, status: 200, text: async () => JSON.stringify(payload) };
211 };
212
213 try {
214 const idApply = await applyApprovedDelegationProposalFromCanister({
215 dataDir,
216 canisterUrl: 'https://canister.test',
217 headers: { 'X-User-Id': 'owner', 'X-Vault-Id': vaultId },
218 proposalId: identityProposal.proposal_id,
219 });
220 assert.equal(idApply.ok, true);
221
222 const consentApply = await applyApprovedDelegationProposalFromCanister({
223 dataDir,
224 canisterUrl: 'https://canister.test',
225 headers: { 'X-User-Id': 'owner', 'X-Vault-Id': vaultId },
226 proposalId: consentProposal.proposal_id,
227 });
228 assert.equal(consentApply.ok, true);
229
230 const mint = handleDelegationGrantMintRequest({
231 dataDir,
232 vaultId,
233 consentId: consentBody.consent_id,
234 actorAgentId: identity.agent_id,
235 taskRef: 'task_hw_week3',
236 });
237 assert.equal(mint.ok, true);
238 assert.match(mint.payload.bearer, /^dgrnt_bearer_/);
239 } finally {
240 globalThis.fetch = originalFetch;
241 }
242 });
243 });
244
245 describe('7C-L1b delegation hosted proposal — stress', () => {
246 it('normalize 200 canister rows without throwing', () => {
247 for (let i = 0; i < 200; i += 1) {
248 const fm = mergeDelegationFrontmatter({}, { record_kind: 'agent_identity', agent_id: `agent_st_${i}` });
249 const out = normalizeCanisterProposalForDelegationPrecheck({
250 proposal_id: `prop-${i}`,
251 intent: 'agent_identity_register',
252 frontmatter: JSON.stringify(fm),
253 body: '{}',
254 });
255 assert.ok(out);
256 }
257 });
258 });
259
260 describe('7C-L1b delegation hosted proposal — data-integrity', () => {
261 const dataDir = path.join(tmpRoot, 'di', 'data');
262 const vaultId = 'default';
263
264 beforeEach(() => {
265 fs.rmSync(path.join(tmpRoot, 'di'), { recursive: true, force: true });
266 fs.mkdirSync(dataDir, { recursive: true });
267 writeDelegationPolicy(dataDir);
268 process.env.DELEGATION_ENABLED = '1';
269 });
270
271 afterEach(() => {
272 delete process.env.DELEGATION_ENABLED;
273 });
274
275 it('precheck + apply preserves consent evidence_ref with proposal id', () => {
276 const identity = makeAgentIdentity({ agentId: 'agent_di_test01' });
277 seedDelegationFixtures(dataDir, vaultId, identity);
278 const consentBody = makeDelegationConsent({ consentId: 'dcons_di_test01', agentId: identity.agent_id });
279 const proposal = normalizeCanisterProposalForDelegationPrecheck({
280 proposal_id: 'prop-di-consent',
281 status: 'approved',
282 vault_id: vaultId,
283 intent: 'delegation_consent_create',
284 created_by: TEST_USER_ID,
285 body: JSON.stringify(consentBody),
286 frontmatter: JSON.stringify(
287 mergeDelegationFrontmatter({}, { record_kind: 'delegation_consent', consent_id: consentBody.consent_id }),
288 ),
289 });
290 assert.ok(proposal);
291 const pre = precheckApprovedDelegationProposal(dataDir, proposal, { author: TEST_USER_ID });
292 assert.equal(pre.ok, true);
293 applyDelegationProposalToIndex(dataDir, pre);
294 const stored = getConsent(dataDir, vaultId, consentBody.consent_id);
295 assert.equal(stored.evidence_ref, 'proposal:prop-di-consent');
296 });
297 });
298
299 describe('7C-L1b delegation hosted proposal — performance', () => {
300 it('normalizeCanisterProposalForDelegationPrecheck completes 1000 rows under 500ms', () => {
301 const fm = mergeDelegationFrontmatter({}, { record_kind: 'agent_identity', agent_id: 'agent_perf' });
302 const row = {
303 proposal_id: 'prop-perf',
304 intent: 'agent_identity_register',
305 frontmatter: JSON.stringify(fm),
306 body: '{}',
307 };
308 const start = performance.now();
309 for (let i = 0; i < 1000; i += 1) {
310 normalizeCanisterProposalForDelegationPrecheck(row);
311 }
312 assert.ok(performance.now() - start < 500);
313 });
314 });
315
316 describe('7C-L1b delegation hosted proposal — security', () => {
317 const dataDir = path.join(tmpRoot, 'sec', 'data');
318
319 beforeEach(() => {
320 fs.rmSync(path.join(tmpRoot, 'sec'), { recursive: true, force: true });
321 fs.mkdirSync(dataDir, { recursive: true });
322 writeDelegationPolicy(dataDir);
323 process.env.DELEGATION_ENABLED = '1';
324 });
325
326 afterEach(() => {
327 delete process.env.DELEGATION_ENABLED;
328 });
329
330 it('apply rejects non-delegation canister proposals', async () => {
331 const originalFetch = globalThis.fetch;
332 globalThis.fetch = async () => ({
333 ok: true,
334 status: 200,
335 text: async () =>
336 JSON.stringify({
337 proposal_id: 'prop-script',
338 path: 'projects/x/script-proposal.md',
339 status: 'approved',
340 intent: 'script_proposal',
341 body: 'hello',
342 frontmatter: '{}',
343 }),
344 });
345 try {
346 const result = await applyApprovedDelegationProposalFromCanister({
347 dataDir,
348 canisterUrl: 'https://canister.test',
349 headers: { 'X-User-Id': 'owner', 'X-Vault-Id': 'default' },
350 proposalId: 'prop-script',
351 });
352 assert.equal(result.ok, false);
353 assert.equal(result.code, 'BAD_REQUEST');
354 } finally {
355 globalThis.fetch = originalFetch;
356 }
357 });
358
359 it('apply rejects unapproved delegation proposals', async () => {
360 const fm = mergeDelegationFrontmatter({}, { record_kind: 'agent_identity', agent_id: 'agent_sec01' });
361 const originalFetch = globalThis.fetch;
362 globalThis.fetch = async () => ({
363 ok: true,
364 status: 200,
365 text: async () =>
366 JSON.stringify({
367 proposal_id: 'prop-pending',
368 status: 'proposed',
369 intent: 'agent_identity_register',
370 body: '{}',
371 frontmatter: JSON.stringify(fm),
372 }),
373 });
374 try {
375 const result = await applyApprovedDelegationProposalFromCanister({
376 dataDir,
377 canisterUrl: 'https://canister.test',
378 headers: { 'X-User-Id': 'owner', 'X-Vault-Id': 'default' },
379 proposalId: 'prop-pending',
380 });
381 assert.equal(result.ok, false);
382 assert.equal(result.code, 'CONFLICT');
383 } finally {
384 globalThis.fetch = originalFetch;
385 }
386 });
387 });
File History 4 commits
sha256:e4c529f14a0bb908c1caaaeb3f95f3623a1a82e636e7e3722ca2cd3dc9821263 security: npm audit fix pre-bridge 2026-07-29 Human 40 days ago
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor 56 days ago
sha256:873e30b7fafe601346295f8f4289f388f21d8f715f28584d5481899ba2b714fc Merge pull request #249 from aaronrene/muse-mirror Agent 73 days ago
sha256:d8c648b20a4d53b2673c5c082ee7edfa7b2fc9b11080832da1f38807b6bf940b fix(7C-L1b): route hosted delegation proposals through cani… Human minor 76 days ago