@bongos/core 1.19.322 → 1.19.324

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,201 @@
1
+ // tests/version_literals.mjs — no ops script hardcodes a version id
2
+ // (task 1003166 / G02, goal 1000077, criterion guards-no-hardcoded-version).
3
+ //
4
+ // THE INCIDENT: smoke-write.sh named 'PMS-V1'. That version shipped, so
5
+ // version_closed refused every create and the write-path smoke could not pass —
6
+ // while /merge-mode Step 5 still said "4/4 expected". FOUR of the versions the
7
+ // nine offending scripts named had already shipped when this was written.
8
+ //
9
+ // The tests care as much about what the guard must NOT flag as what it must: a
10
+ // scan that fires on a comment mentioning an old version would be the over-broad
11
+ // source assertion this repo keeps relearning (learning 1000196).
12
+ import { strict as assert } from 'node:assert';
13
+ import { test } from 'node:test';
14
+ import { createRequire } from 'node:module';
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ const require = createRequire(import.meta.url);
20
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
21
+ const g = require(path.join(ROOT, 'scripts', 'gds', 'version-literal-guard.js'));
22
+ const { pickDefaultVersion } = require(path.join(ROOT, 'scripts', 'gds', 'version-select.js'));
23
+ const read = (...p) => fs.readFileSync(path.join(ROOT, ...p), 'utf8');
24
+ const scan = (files) => g.scanScripts(Object.keys(files), (f) => files[f]);
25
+
26
+ // ---- the selection rule ---------------------------------------------------
27
+ test('the newest BUILDING version wins; shipped ones are never chosen', () => {
28
+ const vs = [
29
+ { id: 'PMS-V1', status: 'shipped', started_at: '2026-05-09' },
30
+ { id: 'GDS-V4', status: 'shipped', started_at: '2026-05-30' },
31
+ { id: 'BONGOS-V1', status: 'building', started_at: '2026-06-20' },
32
+ { id: 'CB-V1', status: 'building', started_at: '2026-06-21' },
33
+ ];
34
+ assert.equal(pickDefaultVersion(vs), 'CB-V1');
35
+ });
36
+
37
+ test('no building version yields NULL, never a guess', () => {
38
+ // The caller must stop and say so; interpolating '' into a create is worse.
39
+ assert.equal(pickDefaultVersion([{ id: 'X', status: 'shipped', started_at: '2026-01-01' }]), null);
40
+ assert.equal(pickDefaultVersion([]), null);
41
+ assert.equal(pickDefaultVersion(null), null);
42
+ });
43
+
44
+ test('a null started_at cannot decide the answer', () => {
45
+ const vs = [
46
+ { id: 'OLD', status: 'building', started_at: null },
47
+ { id: 'NEW', status: 'building', started_at: '2026-06-21' },
48
+ ];
49
+ assert.equal(pickDefaultVersion(vs), 'NEW', 'a half-filled row sorts last, it does not win');
50
+ });
51
+
52
+ // ---- what the guard MUST catch -------------------------------------------
53
+ test('THE INCIDENT: a version field assigned a literal is caught', () => {
54
+ const r = scan({ 'scripts/gds/smoke.sh': " 'version_id': 'PMS-V1'," });
55
+ assert.equal(r.violations.length, 1);
56
+ assert.match(r.violations[0], /RESOLVED/);
57
+ assert.match(r.violations[0], /version-select/, 'and it names the remedy');
58
+ });
59
+
60
+ test('a --version default is caught', () => {
61
+ const r = scan({ 'scripts/gds/a.js': " const v = arg('--version', args) || 'GDS-V3';" });
62
+ assert.equal(r.violations.length, 1);
63
+ });
64
+
65
+ test('a DEFAULT_VERSION constant is caught', () => {
66
+ const r = scan({ 'scripts/gds/b.js': "const DEFAULT_VERSION = process.env.X || 'BONGOS-V1';" });
67
+ assert.equal(r.violations.length, 1);
68
+ });
69
+
70
+ test('an unknown future prefix is caught too', () => {
71
+ // Enumerating PMS/GDS/BONGOS/CB would let the next scheme through.
72
+ const r = scan({ 'scripts/gds/c.js': " version_id: 'ACME-V7'," });
73
+ assert.equal(r.violations.length, 1, 'the shape is matched, not a fixed list');
74
+ });
75
+
76
+ // ---- what the guard must NOT catch ---------------------------------------
77
+ test('a COMMENT naming an old version is not a violation', () => {
78
+ // smoke-claim-consistency.sh legitimately says "PMS-V2 P6 not live yet".
79
+ const r = scan({
80
+ 'scripts/gds/d.sh': '# TODO: once PMS-V2 P6 ships, drop this',
81
+ 'scripts/gds/e.js': "// version_id: 'GDS-V3' used to live here",
82
+ });
83
+ assert.deepEqual(r.violations, []);
84
+ });
85
+
86
+ test('prose printed to the operator is not a violation', () => {
87
+ const r = scan({ 'scripts/gds/f.sh': ' echo "SKIP — PMS-V2 P6 not live yet"' });
88
+ assert.deepEqual(r.violations, []);
89
+ });
90
+
91
+ test('a RESOLVED assignment is not a violation', () => {
92
+ const r = scan({ 'scripts/gds/g.js': ' version_id: await resolveDefaultVersion(),' });
93
+ assert.deepEqual(r.violations, []);
94
+ });
95
+
96
+ test('seeders and tests are exempt BY PATH, not by allowlist', () => {
97
+ // A seeder's whole subject IS a historical version; asking it to resolve one
98
+ // at runtime would break it.
99
+ for (const p of ['scripts/gds/seed-v3-tasks.js', 'scripts/gds/newcomer-floor.test.js', 'scripts/hall-preview/fixtures/x.js']) {
100
+ assert.equal(g.isExempt(p), true, `${p} must be exempt`);
101
+ }
102
+ assert.equal(g.isExempt('scripts/gds/smoke-write.sh'), false, 'an ops script is not exempt');
103
+ });
104
+
105
+ // ---- the live tree --------------------------------------------------------
106
+ test('the live tree passes', () => {
107
+ const r = g.checkNoVersionLiterals();
108
+ assert.equal(r.hardFail, false, `unexpected:\n ${r.violations.join('\n ')}`);
109
+ });
110
+
111
+ test('an empty enumeration FAILS rather than reporting all-clear', () => {
112
+ const r = g.checkNoVersionLiterals({ files: [] });
113
+ assert.equal(r.hardFail, true);
114
+ assert.match(r.violations[0], /scan defect/);
115
+ });
116
+
117
+ test('every allowlist entry carries a real justification', () => {
118
+ assert.ok(g.VERSION_LITERAL_ALLOW.size > 0);
119
+ for (const [k, why] of g.VERSION_LITERAL_ALLOW) {
120
+ assert.match(k, /^scripts\//, `${k} is a script path`);
121
+ assert.ok(String(why).length > 60, `${k} needs a reason, not a label`);
122
+ }
123
+ });
124
+
125
+ // ---- the reported bug is actually fixed ----------------------------------
126
+ test('smoke-write.sh resolves its version instead of naming one', () => {
127
+ const sh = read('scripts', 'gds', 'smoke-write.sh');
128
+ assert.ok(!/'version_id': '[A-Z]/.test(sh), 'no literal remains');
129
+ // The invocation is `$(node "$(dirname "$0")/version-select.js")`, so the
130
+ // closing paren is preceded by a quote AND the inner $(dirname "$0") contains
131
+ // its own ')', so neither `.js)` nor [^)]* matches. Match to end-of-line.
132
+ assert.match(sh, /VERSION_ID=\$\(node[^\n]*version-select\.js/, 'it resolves at runtime');
133
+ assert.match(sh, /exit 1/, 'and stops if it cannot');
134
+ });
135
+
136
+ test('all four bash smokes resolve, and none names a version', () => {
137
+ for (const f of ['smoke-write.sh', 'smoke-claim-consistency.sh', 'smoke-batch-release.sh', 'smoke-batch-claim.sh']) {
138
+ const sh = read('scripts', 'gds', f);
139
+ assert.match(sh, /version-select\.js/, `${f} resolves`);
140
+ assert.match(sh, /'version_id': '\$\{VERSION_ID\}'/, `${f} uses the resolved id`);
141
+ }
142
+ });
143
+
144
+ test('capture.js no longer keeps its own copy of the rule', () => {
145
+ // It was the precedent; now it is a caller, so there is ONE implementation.
146
+ const src = read('scripts', 'gds', 'capture.js');
147
+ assert.match(src, /require\('\.\/version-select\.js'\)/);
148
+ assert.ok(!/async function resolveDefaultVersion/.test(src), 'the private copy is gone');
149
+ });
150
+
151
+ // ---- the four grader findings from the first pass -------------------------
152
+ test('an unsafe version id is REFUSED, not printed', () => {
153
+ // Bash callers splice this into a quoted Python string, so the one place that
154
+ // can guarantee safety is the source. Ids with a quote or backslash could
155
+ // otherwise break out of the literal (the grader's defense-in-depth finding).
156
+ const { SAFE_ID } = require(path.join(ROOT, 'scripts', 'gds', 'version-select.js'));
157
+ for (const bad of ["CB-V1'; import os", 'CB\\V1', "'", '', '1-V1', 'a b']) {
158
+ assert.equal(SAFE_ID.test(bad), false, `${JSON.stringify(bad)} must be refused`);
159
+ }
160
+ for (const good of ['CB-V1', 'BONGOS-V1', 'GDS-V3', 'v2.1_beta']) {
161
+ assert.equal(SAFE_ID.test(good), true, `${good} must be accepted`);
162
+ }
163
+ });
164
+
165
+ test('session-eval resolves the version ONCE, not per finding', () => {
166
+ // It was called inside `for (const s of systemic)` — a network round trip per
167
+ // finding, for a value invariant across the run.
168
+ const src = read('scripts', 'gds', 'session-eval.js');
169
+ // Anchored on the CALL COUNT rather than on a loop position: the file has TWO
170
+ // `for (const s of systemic)` loops, so an indexOf-based test picked the wrong
171
+ // one and passed vacuously. Exactly one call site is the real invariant.
172
+ const calls = src.match(/await resolveDefaultVersion\(\)/g) || [];
173
+ assert.equal(calls.length, 1, `resolved once per run, not per finding (found ${calls.length})`);
174
+ assert.match(src, /const suggestedVersion = await resolveDefaultVersion\(\)/, 'hoisted to a const');
175
+ assert.match(src, /suggested_version: suggestedVersion,/, 'and the filing uses it');
176
+ });
177
+
178
+ test('version-select exits through cliExit, not process.exit', () => {
179
+ // CLAUDE.md: cliExit is the always-correct move for handle closure.
180
+ const src = read('scripts', 'gds', 'version-select.js');
181
+ assert.match(src, /require\('\.\/cli-lib'\)/);
182
+ assert.ok(!/process\.exit\(/.test(src), 'no raw process.exit remains');
183
+ });
184
+
185
+ test('ship-flow.js imports every cross-module function it calls', () => {
186
+ // task 1003205's carve left pushVia() called in regradeMain but never imported,
187
+ // so EVERY resume of a task at 'completed' died with ReferenceError before the
188
+ // claim resolved — found by hitting it while shipping this task. Guarding the
189
+ // one name is not enough; assert the import exists for anything it calls from
190
+ // its sibling deploy-target module.
191
+ const src = read('scripts', 'gds', 'ship-flow.js');
192
+ const deployTargetExports = ['pushVia', 'resolveDeployMode', 'mainIsProtected', 'resolveDeployTarget'];
193
+ const imported = /require\('\.\/ship-deploy-target\.js'\)/.test(src)
194
+ ? (src.match(/const \{([^}]*)\} = require\('\.\/ship-deploy-target\.js'\)/) || [, ''])[1]
195
+ : '';
196
+ for (const name of deployTargetExports) {
197
+ if (new RegExp(`(?<![\\w.])${name}\\(`).test(src)) {
198
+ assert.ok(imported.includes(name), `ship-flow.js calls ${name}() but does not import it`);
199
+ }
200
+ }
201
+ });