@bongos/core 1.19.224 → 1.19.225

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,144 @@
1
+ // tests/government_board_window.mjs — a window opens automatically when a Full
2
+ // Idea passes the grade (BV1.R07 of goal 1000069, ADR 0175 §5).
3
+ //
4
+ // DB-free: the pure bar gate is exercised directly; the open path runs against
5
+ // a monkeypatched db module (the government_board_membership.mjs idiom). The
6
+ // invariants that matter:
7
+ //
8
+ // 1. THE BAR IS THE SAME ONE THE CREDIT LANE USES — ADR 0175 §5 pins the
9
+ // window gate to economy's IDEA_FULL_PASS_BAR. board.js may not import
10
+ // economy (ADR 0083), so the two constants are held equal HERE — the
11
+ // no-second-list-that-drifts discipline, enforced by test.
12
+ // 2. A SUB-BAR IDEA NEVER REACHES THE BOARD — refused inside board.js, not
13
+ // by trusting the caller, and without touching the db.
14
+ // 3. THE CONSTITUTION IS SNAPSHOTTED — the open passes R05's sanitized board
15
+ // block, so an amendment cannot rewrite how an already-open item closes.
16
+ // 4. THE IDEMPOTENCY IS STRUCTURAL — the INSERT targets government_003's
17
+ // partial unique index; a losing racer gets the existing open item back.
18
+ import assert from 'node:assert/strict';
19
+ import { test } from 'node:test';
20
+ import { createRequire } from 'node:module';
21
+ import fs from 'node:fs';
22
+ import path from 'node:path';
23
+ import { fileURLToPath } from 'node:url';
24
+
25
+ const require = createRequire(import.meta.url);
26
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
27
+ const GOV = path.join(ROOT, 'modules', 'government');
28
+ const board = require(path.join(GOV, 'board.js'));
29
+ const db = require(path.join(GOV, 'db.js'));
30
+ const { BOARD_DEFAULTS } = require(path.join(GOV, 'config.js'));
31
+ const credits = require(path.join(ROOT, 'modules', 'economy', 'credits.js'));
32
+
33
+ const { FULL_IDEA_PASS_BAR, ideaPassesBar, openWindowForFullIdea } = board;
34
+
35
+ // ── invariant 1: one bar, two modules, held equal by this line ──────────────
36
+
37
+ test('the window bar IS the credit-lane bar (ADR 0175 §5 — no second list that drifts)', () => {
38
+ assert.equal(FULL_IDEA_PASS_BAR, credits.IDEA_FULL_PASS_BAR,
39
+ 'board.js and economy/credits.js must gate on the SAME completeness bar; '
40
+ + 'if the bar becomes policy, move BOTH reads to one config surface in the same change');
41
+ });
42
+
43
+ // ── invariant 2: the pure gate, fail-closed ─────────────────────────────────
44
+
45
+ test('ideaPassesBar: the bar passes at exactly 60, and every unscorable shape fails closed', () => {
46
+ assert.equal(ideaPassesBar(FULL_IDEA_PASS_BAR), true, 'the bar itself passes (>=)');
47
+ assert.equal(ideaPassesBar(100), true);
48
+ assert.equal(ideaPassesBar(FULL_IDEA_PASS_BAR - 1), false);
49
+ for (const bad of [null, undefined, NaN, 'sixty', '', {}, [], -1, Infinity]) {
50
+ assert.equal(ideaPassesBar(bad), Number.isFinite(Number(bad)) && Number(bad) >= FULL_IDEA_PASS_BAR ? true : false,
51
+ `${String(bad)} must not reach the board`);
52
+ }
53
+ // "not scored" and "could not be scored" look the same (R142) — both stay off the board.
54
+ assert.equal(ideaPassesBar(null), false);
55
+ });
56
+
57
+ // ── the open path, against a stubbed db ─────────────────────────────────────
58
+
59
+ async function withDb(stubs, fn) {
60
+ const originals = {};
61
+ for (const [k, v] of Object.entries(stubs)) { originals[k] = db[k]; db[k] = v; }
62
+ try { return await fn(); }
63
+ finally { for (const [k, v] of Object.entries(originals)) db[k] = v; }
64
+ }
65
+
66
+ test('a sub-bar idea is refused WITHOUT touching the db', async () => {
67
+ await withDb({
68
+ openBoardItem: async () => { throw new Error('must not be called'); },
69
+ getOpenBoardItem: async () => { throw new Error('must not be called'); },
70
+ }, async () => {
71
+ assert.deepEqual(await openWindowForFullIdea({ ideaId: 7, completenessScore: 59 }),
72
+ { opened: false, reason: 'below_bar' });
73
+ assert.deepEqual(await openWindowForFullIdea({ ideaId: 7, completenessScore: null }),
74
+ { opened: false, reason: 'below_bar' });
75
+ });
76
+ });
77
+
78
+ test('an unidentifiable idea id is refused before the bar is even consulted', async () => {
79
+ await withDb({ openBoardItem: async () => { throw new Error('must not be called'); } }, async () => {
80
+ for (const bad of [undefined, null, 0, -3, 'seven', NaN]) {
81
+ assert.deepEqual(await openWindowForFullIdea({ ideaId: bad, completenessScore: 100 }),
82
+ { opened: false, reason: 'bad_subject' }, `${String(bad)} names no subject`);
83
+ }
84
+ });
85
+ });
86
+
87
+ test('a passing idea opens a window carrying the SANITIZED constitution snapshot', async () => {
88
+ let got = null;
89
+ await withDb({
90
+ openBoardItem: async (args) => { got = args; return { id: '11', subject_type: 'full_idea', subject_id: '7', closes_at: null }; },
91
+ }, async () => {
92
+ const out = await openWindowForFullIdea({ ideaId: 7, completenessScore: 82 });
93
+ assert.equal(out.opened, true);
94
+ assert.equal(out.item.id, '11');
95
+ });
96
+ assert.equal(got.subjectType, 'full_idea');
97
+ assert.equal(got.subjectId, 7);
98
+ assert.equal(got.openedBy, null, 'the grade opened this, nobody did (government_003)');
99
+ // This checkout has no config/government.json, so the snapshot is the neutral
100
+ // starter's board block — which R05 pins equal to BOARD_DEFAULTS (monarchy).
101
+ assert.deepEqual(got.constitution, BOARD_DEFAULTS,
102
+ 'the snapshot is R05\'s sanitized board block, not raw file contents');
103
+ assert.equal(got.windowMinutes, BOARD_DEFAULTS.window_minutes,
104
+ 'monarchy has no clock — closes_at stays NULL until a project configures a sitting');
105
+ });
106
+
107
+ test('a lost race returns the EXISTING open item, opened:false', async () => {
108
+ const existing = { id: '4', subject_type: 'full_idea', subject_id: '7', closed_at: null };
109
+ await withDb({
110
+ openBoardItem: async () => null, // the partial-unique-index conflict path
111
+ getOpenBoardItem: async (type, id) => {
112
+ assert.equal(type, 'full_idea'); assert.equal(id, 7);
113
+ return existing;
114
+ },
115
+ }, async () => {
116
+ const out = await openWindowForFullIdea({ ideaId: 7, completenessScore: 82 });
117
+ assert.deepEqual(out, { opened: false, reason: 'already_open', item: existing });
118
+ });
119
+ });
120
+
121
+ // ── the structural idempotency + clock, pinned as SQL source ────────────────
122
+
123
+ test('the INSERT targets the partial unique index and the DATABASE clock', () => {
124
+ const src = fs.readFileSync(path.join(GOV, 'db.js'), 'utf8');
125
+ const start = src.indexOf('async function openBoardItem');
126
+ assert.ok(start > -1);
127
+ const block = src.slice(start, src.indexOf('async function getOpenBoardItem'));
128
+ assert.match(block, /ON CONFLICT \(subject_type, subject_id\) WHERE closed_at IS NULL DO NOTHING/,
129
+ 'idempotency must be the partial unique index, not a SELECT-then-INSERT race');
130
+ assert.match(block, /now\(\) \+ make_interval/,
131
+ 'closes_at comes from the database clock — absolute, so a config change never moves an open deadline');
132
+ });
133
+
134
+ // ── the port carries it ──────────────────────────────────────────────────────
135
+
136
+ test('the government port exposes openBoardWindowForIdea for the ideas capture path', () => {
137
+ const routes = require(path.join(GOV, 'routes', 'government.js'));
138
+ routes(); // registration is hasProvider-guarded — safe to call again
139
+ const api = require(path.join(ROOT, 'src', 'module-api.js'));
140
+ const port = api.resolveOptional('government');
141
+ assert.equal(typeof port.openBoardWindowForIdea, 'function');
142
+ assert.equal(port.openBoardWindowForIdea, board.openWindowForFullIdea,
143
+ 'the port method IS board.js\'s — no wrapper that could drift the bar gate');
144
+ });
@@ -64,12 +64,34 @@ test('the routes file declares the full R110 surface', () => {
64
64
  for (const key of wanted) assert.ok(declared.has(key), `missing route ${key}`);
65
65
  });
66
66
 
67
- test('every /governance route gates on government.manage via REAL middleware', () => {
67
+ test('every /governance route gates via REAL middleware on exactly its PINNED atom', () => {
68
+ // Until R08 every route here gated on government.manage and this test said so
69
+ // literally. The Board Room's vote route (R08 of goal 1000069) gates on the
70
+ // deliberately XENOS-floored board.vote.cast — the atom is the coarse gate,
71
+ // membership is the in-handler wall — so the invariant generalizes: each
72
+ // route's middleware must declare EXACTLY the atom its pin promises, and the
73
+ // rank each classifies to must be that atom's real catalog floor. This is
74
+ // STRICTER than the old literal: a new route cannot sneak in on a different
75
+ // atom without also changing the pin table, which is the reviewed surface.
68
76
  assert.ok(classified.length > 0, 'no /government routes found to classify');
77
+ const pins = rrc.MODULE_EXPECTED_RANKS.government;
69
78
  for (const r of classified) {
70
79
  assert.equal(r.via, 'middleware', `${r.key}: gate must be middleware, not an annotation`);
71
- assert.deepEqual(r.perms, ['government.manage'], `${r.key}: must gate on exactly government.manage`);
72
- assert.equal(r.rank, 'archon', `${r.key}: government.manage floors at archon`);
80
+ const pinned = pins[r.key];
81
+ assert.ok(pinned && pinned.startsWith(rrc.PERM_PIN_PREFIX), `${r.key}: must carry a perm pin`);
82
+ const atom = pinned.slice(rrc.PERM_PIN_PREFIX.length);
83
+ assert.deepEqual(r.perms, [atom], `${r.key}: must gate on exactly its pinned atom (${atom})`);
84
+ // The auditor speaks rank LABELS, not raw floors (route-rank-check.js
85
+ // FLOOR_LABEL): both sub-Metic floors collapse to 'any-builder' — a label
86
+ // that may under-report strictness, never over-report it.
87
+ const FLOOR_LABEL = { xenos: 'any-builder', thetes: 'any-builder', metic: 'metic+archon', archon: 'archon' };
88
+ const floor = (catalog.byKey(atom) || {}).floor;
89
+ assert.equal(r.rank, FLOOR_LABEL[floor], `${r.key}: classifies to ${atom}'s floor label (${FLOOR_LABEL[floor]})`);
90
+ }
91
+ // The admin surface's own rule survives the generalization, stated directly:
92
+ // everything that is not a board route still gates on government.manage.
93
+ for (const r of classified.filter((x) => !x.key.includes('/board/'))) {
94
+ assert.deepEqual(r.perms, ['government.manage'], `${r.key}: the admin surface gates on the manage atom`);
73
95
  }
74
96
  });
75
97
 
@@ -78,8 +100,7 @@ test('every declared /governance route is pinned, and every pin exists (both dir
78
100
  assert.ok(pins, 'MODULE_EXPECTED_RANKS has no government block');
79
101
  const declared = new Set(classified.map((r) => r.key));
80
102
  for (const key of declared) {
81
- assert.equal(pins[key], `${rrc.PERM_PIN_PREFIX}government.manage`,
82
- `${key} must be pinned to perm:government.manage`);
103
+ assert.ok(pins[key], `${key} must be pinned (atom equality is asserted above)`);
83
104
  }
84
105
  for (const key of Object.keys(pins)) {
85
106
  assert.ok(declared.has(key), `pin ${key} names a route the file no longer declares`);