automation-ingest-data-integrity.test.mjs
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6
docs: record AIP-b SD-21 land (KN #308)
Human
9 days ago
| 1 | /** |
| 2 | * AIP-b data-integrity: replay, conflict, pack disabled, no secrets in audit. |
| 3 | */ |
| 4 | import { describe, it } 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 | import { processAutomationIngest, ingestAuditDetail, listPackTemplates } from '../lib/automation-ingest-policy.mjs'; |
| 10 | |
| 11 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
| 12 | |
| 13 | describe('automation ingest data-integrity', () => { |
| 14 | it('pack JSON all enabled false', () => { |
| 15 | const raw = JSON.parse( |
| 16 | fs.readFileSync(path.join(__dirname, '../hub/automation-ingest-rules-default.json'), 'utf8') |
| 17 | ); |
| 18 | assert.ok(Array.isArray(raw.templates)); |
| 19 | assert.ok(raw.templates.every((t) => t.enabled === false)); |
| 20 | assert.ok(listPackTemplates().every((t) => t.enabled === false)); |
| 21 | }); |
| 22 | |
| 23 | it('replay same key+fingerprint+path; conflict on fingerprint change', async () => { |
| 24 | const store = new Map(); |
| 25 | const io = { |
| 26 | async getIdempotency(k) { return store.get(k) || null; }, |
| 27 | async putIdempotency(k, e) { store.set(k, e); }, |
| 28 | async appendAudit() {}, |
| 29 | async runBilling() { return true; }, |
| 30 | async readExistingNote() { return null; }, |
| 31 | async writeNote() {}, |
| 32 | async createProposal() { return { proposal_id: 'prop-1' }; }, |
| 33 | async markProposalApproved() { return { ok: true }; }, |
| 34 | }; |
| 35 | const body = { |
| 36 | path: 'inbox/trends/a.md', |
| 37 | body: 'x', |
| 38 | source_fingerprint: 'same-finger-01', |
| 39 | content_class: 'research', |
| 40 | }; |
| 41 | const first = await processAutomationIngest({ |
| 42 | rawBody: body, |
| 43 | actor: { sub: 's', vaultId: 'default' }, |
| 44 | rules: [], |
| 45 | triggers: { literal_phrases: [], path_prefixes: [], label_any: [] }, |
| 46 | io, |
| 47 | }); |
| 48 | assert.equal(first.status, 201); |
| 49 | const replay = await processAutomationIngest({ |
| 50 | rawBody: body, |
| 51 | actor: { sub: 's', vaultId: 'default' }, |
| 52 | rules: [], |
| 53 | triggers: { literal_phrases: [], path_prefixes: [], label_any: [] }, |
| 54 | io, |
| 55 | }); |
| 56 | assert.equal(replay.status, 200); |
| 57 | assert.equal(replay.body.replayed, true); |
| 58 | await assert.rejects( |
| 59 | () => processAutomationIngest({ |
| 60 | rawBody: { ...body, source_fingerprint: 'other-finger-01' }, |
| 61 | idempotencyHeader: 'same-finger-01', |
| 62 | actor: { sub: 's', vaultId: 'default' }, |
| 63 | rules: [], |
| 64 | triggers: { literal_phrases: [], path_prefixes: [], label_any: [] }, |
| 65 | io, |
| 66 | }), |
| 67 | (e) => e.code === 'INGEST_IDEMPOTENCY_CONFLICT' |
| 68 | ); |
| 69 | }); |
| 70 | |
| 71 | it('audit detail has no secret material', () => { |
| 72 | const detail = ingestAuditDetail({ |
| 73 | ruleId: 'ingr_ab', |
| 74 | disposition: 'review_queue', |
| 75 | sourceFingerprint: 'fp', |
| 76 | notePath: 'inbox/trends/a.md', |
| 77 | contentClass: 'research', |
| 78 | vaultId: 'Business', |
| 79 | credentialId: 'cid-1', |
| 80 | elevatedOverride: false, |
| 81 | evaluationBlock: false, |
| 82 | replayed: false, |
| 83 | }); |
| 84 | const blob = JSON.stringify(detail); |
| 85 | assert.equal(blob.includes('kt_agent_'), false); |
| 86 | assert.equal(blob.includes('Bearer'), false); |
| 87 | assert.ok(!('authorization' in detail)); |
| 88 | }); |
| 89 | }); |
File History
1 commit
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6
docs: record AIP-b SD-21 land (KN #308)
Human
9 days ago