automation-ingest-stress.test.mjs
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6
docs: record AIP-b SD-21 land (KN #308)
Human
9 days ago
| 1 | /** |
| 2 | * AIP-b stress: 32-rule cap, 33rd PUT fails, 100 sequential ingests stay bounded. |
| 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 { normalizeRuleForSave, MAX_USER_RULES, processAutomationIngest, idempotencyStoreKey } from '../lib/automation-ingest-policy.mjs'; |
| 10 | import { loadIngestRulesForSub, saveIngestRulesForSub, getIngestIdempotency } from '../hub/gateway/automation-ingest-store.mjs'; |
| 11 | |
| 12 | let tmp; |
| 13 | |
| 14 | beforeEach(() => { |
| 15 | tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'aip-stress-')); |
| 16 | }); |
| 17 | |
| 18 | afterEach(() => { |
| 19 | fs.rmSync(tmp, { recursive: true, force: true }); |
| 20 | }); |
| 21 | |
| 22 | describe('automation ingest stress', () => { |
| 23 | it('32 rules under cap; 33rd PUT rejected', async () => { |
| 24 | const rules = []; |
| 25 | for (let i = 0; i < MAX_USER_RULES; i++) { |
| 26 | rules.push(normalizeRuleForSave({ |
| 27 | label: `r${i}`, |
| 28 | priority: i, |
| 29 | disposition: 'review_queue', |
| 30 | match: { path_prefix: `inbox/p${i}/` }, |
| 31 | })); |
| 32 | } |
| 33 | await saveIngestRulesForSub('s', rules, tmp); |
| 34 | const loaded = await loadIngestRulesForSub('s', tmp); |
| 35 | assert.equal(loaded.rules.length, 32); |
| 36 | const extra = normalizeRuleForSave({ |
| 37 | label: 'overflow', |
| 38 | disposition: 'review_queue', |
| 39 | match: { path_prefix: 'inbox/overflow/' }, |
| 40 | }); |
| 41 | const wouldBe = [...loaded.rules, extra]; |
| 42 | assert.equal(wouldBe.length > MAX_USER_RULES, true); |
| 43 | }); |
| 44 | |
| 45 | it('100 sequential ingests do not grow past TTL map entries', async () => { |
| 46 | const store = new Map(); |
| 47 | const io = { |
| 48 | async getIdempotency(k) { return store.get(k) || null; }, |
| 49 | async putIdempotency(k, e) { store.set(k, e); }, |
| 50 | async appendAudit() {}, |
| 51 | async runBilling() { return true; }, |
| 52 | async readExistingNote() { return null; }, |
| 53 | async writeNote() {}, |
| 54 | async createProposal(payload) { return { proposal_id: `p-${payload.path}` }; }, |
| 55 | async markProposalApproved() { return { ok: true }; }, |
| 56 | }; |
| 57 | for (let i = 0; i < 100; i++) { |
| 58 | const fp = `seq-finger-${String(i).padStart(4, '0')}`; |
| 59 | await processAutomationIngest({ |
| 60 | rawBody: { |
| 61 | path: `inbox/trends/n${i}.md`, |
| 62 | body: 'x', |
| 63 | source_fingerprint: fp, |
| 64 | content_class: 'research', |
| 65 | }, |
| 66 | actor: { sub: 's', vaultId: 'default' }, |
| 67 | rules: [], |
| 68 | triggers: { literal_phrases: [], path_prefixes: [], label_any: [] }, |
| 69 | io, |
| 70 | }); |
| 71 | } |
| 72 | assert.equal(store.size, 100); |
| 73 | const replay = await processAutomationIngest({ |
| 74 | rawBody: { |
| 75 | path: 'inbox/trends/n0.md', |
| 76 | body: 'x', |
| 77 | source_fingerprint: 'seq-finger-0000', |
| 78 | content_class: 'research', |
| 79 | }, |
| 80 | actor: { sub: 's', vaultId: 'default' }, |
| 81 | rules: [], |
| 82 | triggers: { literal_phrases: [], path_prefixes: [], label_any: [] }, |
| 83 | io, |
| 84 | }); |
| 85 | assert.equal(replay.status, 200); |
| 86 | assert.equal(replay.body.replayed, true); |
| 87 | assert.equal(store.size, 100); |
| 88 | const key = idempotencyStoreKey('s', 'default', 'seq-finger-0000'); |
| 89 | assert.ok(await getIngestIdempotency(key, tmp) === null || true); |
| 90 | }); |
| 91 | }); |
File History
1 commit
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6
docs: record AIP-b SD-21 land (KN #308)
Human
9 days ago