hosted-write-eval-kn-b-integration.test.mjs
147 lines 4.8 KB
Raw
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6 docs: record AIP-b SD-21 land (KN #308) Human 10 days ago
1 /**
2 * HOSTED-WRITE-EVAL-KN-b — Tier 2 integration: create E1 + member approve gate + discard admin-only.
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 os from 'node:os';
8 import path from 'node:path';
9 import { augmentProposalCreateRequestBody } from '../lib/hub-proposal-create-augment.mjs';
10 import {
11 SCOOLING_REVIEW_TRAY_INTENT,
12 personalSelfApplyAllowsApprove,
13 } from '../lib/hub-proposal-personal-self-apply.mjs';
14 import { createProposal, getProposal, updateProposalStatus } from '../hub/proposals-store.mjs';
15 import { actorMayApproveProposals } from '../hub/lib/hub-evaluator-may-approve.mjs';
16
17 const INTENT = SCOOLING_REVIEW_TRAY_INTENT;
18
19 describe('HOSTED-WRITE-EVAL-KN-b integration', () => {
20 /** @type {string} */
21 let dataDir;
22 /** @type {string|undefined} */
23 let prevEval;
24
25 beforeEach(() => {
26 dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kt-hwe-int-'));
27 prevEval = process.env.HUB_PROPOSAL_EVALUATION_REQUIRED;
28 process.env.HUB_PROPOSAL_EVALUATION_REQUIRED = '1';
29 });
30
31 afterEach(() => {
32 if (prevEval === undefined) delete process.env.HUB_PROPOSAL_EVALUATION_REQUIRED;
33 else process.env.HUB_PROPOSAL_EVALUATION_REQUIRED = prevEval;
34 fs.rmSync(dataDir, { recursive: true, force: true });
35 });
36
37 it('create matching proposal → evaluation passed (E1) → member self-apply allows approve', () => {
38 const body = augmentProposalCreateRequestBody(
39 {
40 path: 'reviewed/review-request-fixture-001.md',
41 body: '# Note\n',
42 intent: INTENT,
43 external_ref: 'scooling.review:review-request-fixture-001',
44 labels: [],
45 },
46 dataDir,
47 { evaluationRequired: true, evaluatedBy: 'google:member1' },
48 );
49 assert.equal(body.evaluation_status, 'passed');
50 assert.equal(body.evaluated_by, 'google:member1');
51
52 const proposal = createProposal(dataDir, {
53 path: body.path,
54 body: body.body,
55 intent: body.intent,
56 external_ref: body.external_ref,
57 evaluationRequired: true,
58 proposed_by: 'google:member1',
59 });
60 assert.equal(proposal.evaluation_status, 'passed');
61 assert.equal(
62 personalSelfApplyAllowsApprove({
63 proposal,
64 hasVaultWrite: true,
65 partitionOwned: true,
66 role: 'member',
67 }),
68 true,
69 );
70 assert.equal(actorMayApproveProposals('google:member1', 'member', {}, false), false);
71 });
72
73 it('mismatched intent → pending under gate → member cannot self-apply', () => {
74 const body = augmentProposalCreateRequestBody(
75 {
76 path: 'reviewed/other.md',
77 body: 'x',
78 intent: 'agent.suggest',
79 external_ref: 'scooling.review:other',
80 labels: [],
81 },
82 dataDir,
83 { evaluationRequired: true, evaluatedBy: 'google:member1' },
84 );
85 assert.equal(body.evaluation_status, 'pending');
86 assert.equal(
87 personalSelfApplyAllowsApprove({
88 proposal: { ...body, status: 'proposed' },
89 hasVaultWrite: true,
90 partitionOwned: true,
91 role: 'member',
92 }),
93 false,
94 );
95 });
96
97 it('elevated trigger → no E1 self-pass → no self-apply', () => {
98 fs.writeFileSync(
99 path.join(dataDir, 'hub_proposal_review_triggers.json'),
100 JSON.stringify({
101 literal_phrases: [{ match: 'api_key', review_queue: 'sec', review_severity: 'elevated' }],
102 path_prefixes: [],
103 label_any: [],
104 }),
105 );
106 const body = augmentProposalCreateRequestBody(
107 {
108 path: 'reviewed/flagged.md',
109 body: 'contains api_key value',
110 intent: INTENT,
111 external_ref: 'scooling.review:flagged',
112 labels: [],
113 },
114 dataDir,
115 { evaluationRequired: true, evaluatedBy: 'google:member1' },
116 );
117 assert.equal(body.review_severity, 'elevated');
118 assert.equal(body.evaluation_status, 'pending');
119 assert.equal(
120 personalSelfApplyAllowsApprove({
121 proposal: { ...body, status: 'proposed' },
122 hasVaultWrite: true,
123 partitionOwned: true,
124 role: 'member',
125 }),
126 false,
127 );
128 });
129
130 it('discard remains admin-only (self-apply never grants discard)', () => {
131 const proposal = createProposal(dataDir, {
132 path: 'reviewed/d.md',
133 body: 'x',
134 intent: INTENT,
135 external_ref: 'scooling.review:d',
136 evaluationRequired: true,
137 proposed_by: 'google:member1',
138 });
139 assert.equal(proposal.evaluation_status, 'passed');
140 // Simulate discard RBAC: only admin — member self-apply is approve-only.
141 const memberMayDiscard = false;
142 assert.equal(memberMayDiscard, false);
143 const discarded = updateProposalStatus(dataDir, proposal.proposal_id, 'discarded');
144 assert.equal(discarded.status, 'discarded');
145 assert.equal(getProposal(dataDir, proposal.proposal_id).status, 'discarded');
146 });
147 });
File History 1 commit
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6 docs: record AIP-b SD-21 land (KN #308) Human 10 days ago