flow-external-agent-e2e.test.mjs
108 lines 3.4 KB
Raw
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 11 hours ago
1 /**
2 * Tier 3 — E2E: mint → bundle → invoke → revoke → import sandbox.
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 { handleFlowProjectRequest } from '../lib/flow/flow-handlers.mjs';
11 import { handleFlowProposeRequest } from '../lib/flow/flow-authoring.mjs';
12 import {
13 handleFlowExternalGrantMintRequest,
14 handleFlowExternalGrantRevokeRequest,
15 handleFlowExternalToolInvokeRequest,
16 validateExternalGrantBearer,
17 } from '../lib/flow/external-agent.mjs';
18 import { upsertFlowVersion } from '../lib/flow/flow-store.mjs';
19 import { createProposal } from '../hub/proposals-store.mjs';
20 import {
21 writeExternalAgentPolicy,
22 makeExternalToolFlowBundle,
23 } from './fixtures/flow/external-agent-helpers.mjs';
24
25 const __dirname = path.dirname(fileURLToPath(import.meta.url));
26 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-flow-external-agent-e2e');
27
28 describe('Flow external-agent — e2e', () => {
29 const dataDir = path.join(tmpRoot, 'data');
30 const vaultId = 'default';
31
32 beforeEach(() => {
33 fs.rmSync(tmpRoot, { recursive: true, force: true });
34 fs.mkdirSync(dataDir, { recursive: true });
35 writeExternalAgentPolicy(dataDir);
36 process.env.FLOW_EXTERNAL_AGENT_ENABLED = '1';
37 process.env.FLOW_AUTHORING_WRITES = '1';
38 const bundle = makeExternalToolFlowBundle();
39 upsertFlowVersion(dataDir, vaultId, bundle.flow, bundle.steps);
40 });
41
42 afterEach(() => {
43 fs.rmSync(tmpRoot, { recursive: true, force: true });
44 delete process.env.FLOW_EXTERNAL_AGENT_ENABLED;
45 delete process.env.FLOW_AUTHORING_WRITES;
46 });
47
48 it('mint grant → fetch agent_bundle → invoke → revoke', () => {
49 const mint = handleFlowExternalGrantMintRequest({
50 dataDir,
51 vaultId,
52 flowId: 'flow_ext_agent_test',
53 flowVersion: '1.0.0',
54 requestedTools: ['web_search'],
55 });
56 assert.equal(mint.ok, true);
57
58 const project = handleFlowProjectRequest({
59 dataDir,
60 vaultId,
61 flowId: 'flow_ext_agent_test',
62 harness: 'agent_bundle',
63 cliScopes: ['personal'],
64 });
65 assert.equal(project.ok, true);
66 const bundle = JSON.parse(project.payload.projection.rendered);
67 assert.equal(bundle.schema, 'knowtation.agent_bundle/v0');
68
69 const invoke = handleFlowExternalToolInvokeRequest({
70 dataDir,
71 vaultId,
72 toolId: 'web_search',
73 bearer: mint.payload.bearer,
74 flowId: 'flow_ext_agent_test',
75 flowVersion: '1.0.0',
76 });
77 assert.equal(invoke.ok, true);
78
79 const revoke = handleFlowExternalGrantRevokeRequest({
80 dataDir,
81 vaultId,
82 grantId: mint.payload.grant.grant_id,
83 });
84 assert.equal(revoke.ok, true);
85
86 const denied = validateExternalGrantBearer({
87 dataDir,
88 vaultId,
89 bearer: mint.payload.bearer,
90 });
91 assert.equal(denied.ok, false);
92 assert.equal(denied.code, 'FLOW_EXTERNAL_GRANT_REVOKED');
93 });
94
95 it('import with unknown external_tool refused before proposal', () => {
96 const bad = makeExternalToolFlowBundle({ toolId: 'unknown_tool_xyz' });
97 const result = handleFlowProposeRequest({
98 dataDir,
99 vaultId,
100 kind: 'import',
101 bundle: bad,
102 intent: 'import bad tools',
103 createProposal,
104 });
105 assert.equal(result.ok, false);
106 assert.equal(result.code, 'FLOW_IMPORT_EXTERNAL_TOOL_DENIED');
107 });
108 });
File History 1 commit
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 11 hours ago