@bongos/core 1.19.1074 → 1.19.1075
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.
- package/.bongos-core.json +122 -122
- package/bin/bongos.js +1 -3
- package/docs/api/openapi.json +4 -4
- package/docs/module-api-changelog.md +2 -0
- package/modules/copy-desk/routes/copy-desk.js +1 -1
- package/modules/discord/ship-broadcast.js +3 -3
- package/modules/economy/credits.js +2 -2
- package/modules/ideas/projection.js +1 -1
- package/modules/lifecycle/db-ship.js +4 -4
- package/modules/lifecycle/github-push.js +12 -14
- package/modules/lifecycle/publish-reconciler.js +10 -10
- package/modules/lifecycle/routes/tasks.js +8 -8
- package/modules/lifecycle/ship-card.js +1 -1
- package/modules/lifecycle/task-visuals.js +2 -2
- package/modules/memory/routes/memory.js +2 -2
- package/modules/platform-identity/routes/sso.js +3 -2
- package/modules/provisioning/capacity.js +1 -1
- package/modules/provisioning/provisioning.js +4 -3
- package/modules/provisioning/routes/provisioning.js +4 -4
- package/modules/sessions/db.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/gds/artifact-staleness.js +2 -2
- package/scripts/gds/backfill-task-rewards.js +5 -5
- package/scripts/gds/claude-materialize.js +2 -2
- package/scripts/gds/cli-lib.js +7 -7
- package/scripts/gds/codemod-rename-src-gds.js +1 -1
- package/scripts/gds/context-pack.js +5 -7
- package/scripts/gds/doctor.js +3 -3
- package/scripts/gds/fitness-checks-packaging.js +4 -4
- package/scripts/gds/fitness-ratchets.js +1 -1
- package/scripts/gds/gen-api-client.js +4 -4
- package/scripts/gds/gen-api-docs.js +1 -1
- package/scripts/gds/gen-repo-map.js +13 -14
- package/scripts/gds/gen-session-index.js +8 -8
- package/scripts/gds/leak-scan-allowlist.js +1 -1
- package/scripts/gds/plain-cards.js +1 -1
- package/scripts/gds/push-path-brief.js +10 -10
- package/scripts/gds/rename-history-check.js +1 -1
- package/scripts/gds/session-digest-build.js +2 -2
- package/scripts/gds/ship-deploy-target.js +13 -13
- package/scripts/gds/ship-flow.js +1 -1
- package/scripts/gds/ship-land.js +6 -6
- package/scripts/gds/ship-merge.js +4 -4
- package/scripts/gds/ship-preflight-steps.js +4 -4
- package/scripts/gds/ship-regen.js +9 -12
- package/scripts/gds/ship.js +9 -9
- package/scripts/gds/skill-preflight.js +9 -8
- package/src/bongos/api-prefix.js +4 -4
- package/src/bongos/platform-visibility-gate.js +1 -1
- package/src/bongos/routes/me.js +1 -1
- package/src/bongos/routes/security.js +3 -3
- package/src/bongos/routes.js +2 -2
- package/src/bongos/serve-internal.js +5 -6
- package/src/bongos-downloads.js +1 -6
- package/src/branding.js +2 -2
- package/src/build-info.js +4 -6
- package/src/instance-config.js +2 -2
- package/src/module-api.js +3 -3
- package/src/module-loader/manifest-schema.js +1 -1
- package/tests/api_alias_caller_ratchet.mjs +1 -1
- package/tests/api_alias_exceptions.mjs +1 -1
- package/tests/api_client.mjs +1 -1
- package/tests/api_path_404.mjs +1 -1
- package/tests/backfill_task_rewards.mjs +1 -1
- package/tests/{box_ship_permissions.mjs → cli_allowlist_permissions.mjs} +5 -5
- package/tests/cli_sessions.mjs +1 -1
- package/tests/context_pack.mjs +13 -13
- package/tests/copy_inventory.mjs +7 -7
- package/tests/credit_grant.mjs +2 -2
- package/tests/criterion_suggest.mjs +1 -1
- package/tests/design_tokens_sync.mjs +3 -2
- package/tests/fitness_ratchets.mjs +2 -2
- package/tests/github_push_land_proof.mjs +1 -1
- package/tests/goal_advisory.mjs +3 -3
- package/tests/goal_suggest.mjs +3 -3
- package/tests/government_abuse_matrix.mjs +4 -3
- package/tests/hall_audit.mjs +1 -1
- package/tests/helpers.mjs +1 -1
- package/tests/host_topology_skips.mjs +2 -2
- package/tests/migration_namespace.mjs +6 -6
- package/tests/module_api.mjs +2 -2
- package/tests/module_cli.mjs +5 -5
- package/tests/module_manifest.mjs +13 -13
- package/tests/module_route_rank.mjs +2 -2
- package/tests/modules_route_wiring.mjs +1 -1
- package/tests/onboarding_route_signals.mjs +1 -1
- package/tests/paste_token_session_store.mjs +1 -1
- package/tests/platform_visibility_gate.mjs +1 -1
- package/tests/project_modules_ui.mjs +17 -17
- package/tests/projects_hub_module_picker.mjs +75 -74
- package/tests/provisioning_capacity.mjs +1 -1
- package/tests/provisioning_recommendations.mjs +2 -1
- package/tests/publish_branch_route.mjs +1 -1
- package/tests/publish_manifest.mjs +1 -1
- package/tests/publish_reconciler.mjs +8 -8
- package/tests/push_path_brief.mjs +10 -9
- package/tests/regrade_eligibility.mjs +1 -1
- package/tests/rename_history_restraint.mjs +1 -1
- package/tests/repo_map.mjs +9 -9
- package/tests/seam_wiring_guard.mjs +11 -11
- package/tests/search_isolation.mjs +3 -3
- package/tests/session_records.mjs +1 -1
- package/tests/session_rename_fallback.mjs +1 -1
- package/tests/session_start_freshness.mjs +3 -3
- package/tests/session_token_hash_db.mjs +1 -1
- package/tests/ship_card.mjs +1 -1
- package/tests/ship_ci_deploy.mjs +17 -17
- package/tests/ship_premerge.mjs +1 -1
- package/tests/ship_resume.mjs +1 -1
- package/tests/skill_preflight.mjs +2 -2
- package/tests/skip_is_not_pass.mjs +1 -1
- package/tests/terms_acceptance.mjs +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// tests/ is publishable, but some tests assert paths the neutral core deliberately does
|
|
4
4
|
// not carry: publish-manifest.js's PROTECTED_TOPOLOGY_EXCLUDE (`.claude/hooks/`,
|
|
5
5
|
// `.claude/settings.json`, `infra/`, `.github/`, `scripts/deploy/`) plus instance-only
|
|
6
|
-
// content (`
|
|
6
|
+
// content (`config/<instance>.json`, an uninstalled module). Those files
|
|
7
7
|
// used to CRASH at import and red-X the unit gate for every consumer; they now skip
|
|
8
8
|
// cleanly via the helpers in tests/helpers.mjs and run in full wherever the topology is
|
|
9
9
|
// materialized.
|
|
@@ -33,7 +33,7 @@ const ALLOWED = {
|
|
|
33
33
|
allow_rule_guard: '.claude/hooks/allow-rule-guard.js (package-excluded)',
|
|
34
34
|
art_stage: 'scripts/gds/art-stage.js (art-pipeline tooling, not in the core)',
|
|
35
35
|
bongos_downloads: 'config/bongos-cli.json (instance content)',
|
|
36
|
-
|
|
36
|
+
cli_allowlist_permissions: '.claude/settings.json (package-excluded)',
|
|
37
37
|
conductor: '.claude/hooks/conductor.js (package-excluded)',
|
|
38
38
|
deploy_instance: 'scripts/deploy/deploy-instance.sh (host topology)',
|
|
39
39
|
eviction_policy: '.claude/hooks/eviction-policy.js (package-excluded)',
|
|
@@ -46,8 +46,8 @@ t('parseMigrationName: core_NNN_slug.sql', () => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
t('parseMigrationName: multi-word module prefix splits at the number, not greedily', () => {
|
|
49
|
-
const p = ns.parseMigrationName('
|
|
50
|
-
assert.equal(p.prefix, '
|
|
49
|
+
const p = ns.parseMigrationName('platform_identity_003_add_col.sql');
|
|
50
|
+
assert.equal(p.prefix, 'platform_identity', 'platform_identity_003 → prefix platform_identity, not platform');
|
|
51
51
|
assert.equal(p.number, 3);
|
|
52
52
|
assert.equal(p.slug, 'add_col');
|
|
53
53
|
});
|
|
@@ -114,10 +114,10 @@ t('filenameViolation: NEW module migration must be <key>_-prefixed', () => {
|
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
t('filenameViolation: hyphenated module key maps to underscore prefix', () => {
|
|
117
|
-
assert.equal(ns.sqlNamespace('
|
|
118
|
-
assert.equal(ns.filenameViolation('module', '
|
|
119
|
-
const v = ns.filenameViolation('module', '003_x.sql', { moduleKey: '
|
|
120
|
-
assert.ok(v && /
|
|
117
|
+
assert.equal(ns.sqlNamespace('platform-identity'), 'platform_identity');
|
|
118
|
+
assert.equal(ns.filenameViolation('module', 'platform_identity_003_x.sql', { moduleKey: 'platform-identity' }), null);
|
|
119
|
+
const v = ns.filenameViolation('module', '003_x.sql', { moduleKey: 'platform-identity' });
|
|
120
|
+
assert.ok(v && /platform_identity_-prefixed|platform_identity_003_x/.test(v));
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
// ---- stemCollisions --------------------------------------------------------
|
package/tests/module_api.mjs
CHANGED
|
@@ -16,8 +16,8 @@ const seams = require(path.join(ROOT, 'src', 'module-seams.js'));
|
|
|
16
16
|
// removing/renaming a key is normally a MAJOR bump (a breaking change to every
|
|
17
17
|
// module). Exception (BV1.R68 / ADR 0091 §3): relocating a DOMAIN query whose
|
|
18
18
|
// sole in-repo consumer moves in lockstep is treated as MINOR — e.g.
|
|
19
|
-
//
|
|
20
|
-
// was added; no enabled module at its pinned coreVersion lost a capability.
|
|
19
|
+
// a per-builder setter left the kernel for its own module's db.js at 1.5.0, when
|
|
20
|
+
// `withTx` was added; no enabled module at its pinned coreVersion lost a capability.
|
|
21
21
|
const PUBLISHED_SURFACE = [
|
|
22
22
|
'CORE_VERSION',
|
|
23
23
|
'requireBuilder', 'requireRank', 'rankMeetsThreshold', 'requireBfgPrincipal', 'allowBoxScope', 'resolveSession',
|
package/tests/module_cli.mjs
CHANGED
|
@@ -63,11 +63,11 @@ test('scaffoldModule: writes a valid, loader-discoverable module with no core ed
|
|
|
63
63
|
|
|
64
64
|
test('scaffoldModule: a kebab-case key produces matching dir, route file, and title', () => {
|
|
65
65
|
const root = tmpModulesRoot();
|
|
66
|
-
const { dir, manifest } = mod.scaffoldModule({ key: '
|
|
67
|
-
assert.equal(manifest.title, '
|
|
68
|
-
assert.deepEqual(manifest.contributes.routes, ['
|
|
69
|
-
assert.ok(fs.existsSync(path.join(dir, 'routes', '
|
|
70
|
-
assert.match(fs.readFileSync(path.join(dir, 'routes', '
|
|
66
|
+
const { dir, manifest } = mod.scaffoldModule({ key: 'copy-desk', modulesDir: root, core: '1.0.0' });
|
|
67
|
+
assert.equal(manifest.title, 'Copy Desk');
|
|
68
|
+
assert.deepEqual(manifest.contributes.routes, ['copy-desk']);
|
|
69
|
+
assert.ok(fs.existsSync(path.join(dir, 'routes', 'copy-desk.js')));
|
|
70
|
+
assert.match(fs.readFileSync(path.join(dir, 'routes', 'copy-desk.js'), 'utf8'), /copyDeskRoutes/);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
test('scaffoldModule: rejects an invalid key', () => {
|
|
@@ -12,11 +12,11 @@ const { satisfies, isValidRange, parseVersion } = require(path.join(ROOT, 'src',
|
|
|
12
12
|
const { validateManifest, assertValidManifest } = require(path.join(ROOT, 'src', 'module-loader', 'manifest-schema.js'));
|
|
13
13
|
|
|
14
14
|
const baseManifest = () => ({
|
|
15
|
-
key: '
|
|
16
|
-
title: '
|
|
17
|
-
description: '
|
|
15
|
+
key: 'discord',
|
|
16
|
+
title: 'Discord',
|
|
17
|
+
description: 'A bridge to the team chat.',
|
|
18
18
|
version: '1.0.0', coreVersion: '^1.0.0',
|
|
19
|
-
contributes: { routes: ['
|
|
19
|
+
contributes: { routes: ['discord'], pollers: ['discord-bot'], uiSections: ['chat'] },
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
// --- semver ---
|
|
@@ -91,7 +91,7 @@ test('missing required fields each produce an error', () => {
|
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
test('bad key format is rejected', () => {
|
|
94
|
-
const r = validateManifest({ ...baseManifest(), key: '
|
|
94
|
+
const r = validateManifest({ ...baseManifest(), key: 'Discord_Bot' });
|
|
95
95
|
assert.equal(r.valid, false);
|
|
96
96
|
assert.ok(r.errors.some((e) => e.startsWith('key')));
|
|
97
97
|
});
|
|
@@ -112,13 +112,13 @@ test('version must be an exact X.Y.Z — a range or junk is rejected (task 10037
|
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
test('unknown contributes key is rejected (typo guard)', () => {
|
|
115
|
-
const r = validateManifest({ ...baseManifest(), contributes: { routs: ['
|
|
115
|
+
const r = validateManifest({ ...baseManifest(), contributes: { routs: ['discord'] } });
|
|
116
116
|
assert.equal(r.valid, false);
|
|
117
117
|
assert.ok(r.errors.some((e) => e.includes('contributes.routs')));
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
test('non-array contributes.routes is rejected', () => {
|
|
121
|
-
const r = validateManifest({ ...baseManifest(), contributes: { routes: '
|
|
121
|
+
const r = validateManifest({ ...baseManifest(), contributes: { routes: 'discord' } });
|
|
122
122
|
assert.equal(r.valid, false);
|
|
123
123
|
assert.ok(r.errors.some((e) => e.includes('contributes.routes')));
|
|
124
124
|
});
|
|
@@ -296,7 +296,7 @@ test('every bundled module declares a maintenance posture (the catalog has no un
|
|
|
296
296
|
test('declarativeSeams: a valid exemption naming a declared seam + entry passes', () => {
|
|
297
297
|
const r = validateManifest({
|
|
298
298
|
...baseManifest(),
|
|
299
|
-
declarativeSeams: { pollers: ['
|
|
299
|
+
declarativeSeams: { pollers: ['discord-bot'], uiSections: ['chat'] },
|
|
300
300
|
});
|
|
301
301
|
assert.equal(r.valid, true, r.errors.join('; '));
|
|
302
302
|
});
|
|
@@ -304,8 +304,8 @@ test('declarativeSeams: a valid exemption naming a declared seam + entry passes'
|
|
|
304
304
|
test('declarativeSeams: an exemption for a seam the module does not declare is an ERROR', () => {
|
|
305
305
|
const r = validateManifest({
|
|
306
306
|
...baseManifest(),
|
|
307
|
-
contributes: { routes: ['
|
|
308
|
-
declarativeSeams: { pollers: ['
|
|
307
|
+
contributes: { routes: ['discord'] }, // no pollers declared
|
|
308
|
+
declarativeSeams: { pollers: ['discord-bot'] },
|
|
309
309
|
});
|
|
310
310
|
assert.equal(r.valid, false);
|
|
311
311
|
assert.ok(r.errors.some((e) => /does not declare contributes\.pollers/.test(e)), r.errors.join('; '));
|
|
@@ -314,10 +314,10 @@ test('declarativeSeams: an exemption for a seam the module does not declare is a
|
|
|
314
314
|
test('declarativeSeams: an exemption naming an ENTRY the module does not list is an ERROR (the typo case)', () => {
|
|
315
315
|
const r = validateManifest({
|
|
316
316
|
...baseManifest(),
|
|
317
|
-
declarativeSeams: { pollers: ['
|
|
317
|
+
declarativeSeams: { pollers: ['discord-bto'] }, // transposed letters
|
|
318
318
|
});
|
|
319
319
|
assert.equal(r.valid, false);
|
|
320
|
-
assert.ok(r.errors.some((e) => /"
|
|
320
|
+
assert.ok(r.errors.some((e) => /"discord-bto" is not in contributes\.pollers/.test(e)), r.errors.join('; '));
|
|
321
321
|
});
|
|
322
322
|
|
|
323
323
|
test('declarativeSeams: the per-entry check spans EVERY seam, not just bare string arrays', () => {
|
|
@@ -344,7 +344,7 @@ test('declarativeSeams: an unknown seam key is an ERROR, not a silent no-op', ()
|
|
|
344
344
|
|
|
345
345
|
test('declarativeSeams: shape errors — non-object block, non-string-array entries', () => {
|
|
346
346
|
assert.equal(validateManifest({ ...baseManifest(), declarativeSeams: ['pollers'] }).valid, false);
|
|
347
|
-
assert.equal(validateManifest({ ...baseManifest(), declarativeSeams: { pollers: '
|
|
347
|
+
assert.equal(validateManifest({ ...baseManifest(), declarativeSeams: { pollers: 'discord-bot' } }).valid, false);
|
|
348
348
|
assert.equal(validateManifest({ ...baseManifest(), declarativeSeams: { pollers: [''] } }).valid, false);
|
|
349
349
|
});
|
|
350
350
|
|
|
@@ -91,9 +91,9 @@ test('checkRouteRanks: an absent modules/ dir leaves the core result clean (byte
|
|
|
91
91
|
|
|
92
92
|
// ---- the grade-time trigger fires on a module route change ----
|
|
93
93
|
test('ROUTE_FILE_RE matches a module route path (a module route change triggers the rank pre-pass)', () => {
|
|
94
|
-
assert.match('modules/
|
|
94
|
+
assert.match('modules/discord/routes/discord.js', rrc.ROUTE_FILE_RE);
|
|
95
95
|
assert.match('src/bongos/routes/tasks.js', rrc.ROUTE_FILE_RE); // core still matches
|
|
96
|
-
assert.ok(!rrc.ROUTE_FILE_RE.test('modules/
|
|
96
|
+
assert.ok(!rrc.ROUTE_FILE_RE.test('modules/discord/module.json'), 'a non-route module file does not trigger the pre-pass');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
// ---- a rank DRIFT on a pinned MODULE route reds the build (BV1.R61) ----
|
|
@@ -79,7 +79,7 @@ test('1002857: POST /modules/:key/enable rejects a non-boolean before touching t
|
|
|
79
79
|
// is why the next assertion matters more than this one.
|
|
80
80
|
const router = buildModulesRouter();
|
|
81
81
|
const res = await invoke(handlerFor(router, 'post', '/modules/:key/enable'), {
|
|
82
|
-
params: { key: '
|
|
82
|
+
params: { key: 'discord' }, query: {}, body: { enabled: 'yes-please' }, builder: { id: 1 },
|
|
83
83
|
});
|
|
84
84
|
assert.equal(res.body.error, 'invalid_body');
|
|
85
85
|
});
|
|
@@ -137,7 +137,7 @@ await t('no live signals → discord/cli both uncleared, and no retired box step
|
|
|
137
137
|
assert.equal(r.status, 200);
|
|
138
138
|
assert.equal(stepCleared(r.json, 'join_discord'), false);
|
|
139
139
|
assert.equal(stepCleared(r.json, 'cli_setup'), false);
|
|
140
|
-
assert.equal(stepCleared(r.json, 'box_connect'), undefined, 'the
|
|
140
|
+
assert.equal(stepCleared(r.json, 'box_connect'), undefined, 'the retired box_connect step is no longer on the checklist (task 1004310)');
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
await t('discord linked → join_discord clears from ITS OWN signal [the scootermack regression]', async () => {
|
|
@@ -139,7 +139,7 @@ test('pasting a token for instance B PRESERVES instance A — the bug this close
|
|
|
139
139
|
assert.equal(stored.token, 'B'.repeat(64));
|
|
140
140
|
|
|
141
141
|
// 3. The active pointer still follows the paste, in the same place every existing
|
|
142
|
-
// reader (skills, hooks
|
|
142
|
+
// reader (skills, hooks) looks. The fix is additive or it is a
|
|
143
143
|
// different, quieter outage.
|
|
144
144
|
const pointer = readJson(active);
|
|
145
145
|
assert.equal(pointer.api_base, base, 'the active pointer must follow the paste');
|
|
@@ -162,7 +162,7 @@ test('the exempt list: the door, the manifest, the probes, the downloads — on
|
|
|
162
162
|
'/api/gds/instance', '/api/bongos/instance', '/api/gds/healthz',
|
|
163
163
|
'/api/bongos/provisioning/tls-check', '/api/gds/provisioning/health',
|
|
164
164
|
'/healthz', '/version', '/metrics', '/__stealth',
|
|
165
|
-
'/downloads/bongos/install.sh', '/downloads/
|
|
165
|
+
'/downloads/bongos/install.sh', '/downloads/bongos/latest.json',
|
|
166
166
|
// Express routes case-insensitively and with a trailing slash — the list sees what the router sees
|
|
167
167
|
'/API/GDS/INSTANCE', '/api/gds/instance/', '/HEALTHZ', '/api/bongos/auth/',
|
|
168
168
|
]) assert.equal(isExempt(p), true, `${p} must answer a stranger`);
|
|
@@ -56,7 +56,7 @@ for (const fn of ['function ensureModsCatalog(', 'function renderMods(', 'functi
|
|
|
56
56
|
const CATALOG = {
|
|
57
57
|
core: ['memory', 'lifecycle', 'hall-ui'],
|
|
58
58
|
optional: [
|
|
59
|
-
{ key: '
|
|
59
|
+
{ key: 'npm-release', label: 'npm release', summary: 'Publishes each release of your project to npm.' },
|
|
60
60
|
{ key: 'discord', label: 'Discord', summary: 'Mirrors what ships into a Discord channel.' },
|
|
61
61
|
{ key: 'agents', label: 'Agents', summary: 'Lets AI builders claim and ship work on their own.' },
|
|
62
62
|
],
|
|
@@ -76,7 +76,7 @@ function parseRows(html) {
|
|
|
76
76
|
|
|
77
77
|
// Boot the region against a scripted catalog + detail channel and hand back the
|
|
78
78
|
// handles a test drives it with.
|
|
79
|
-
function boot({ selected = ['
|
|
79
|
+
function boot({ selected = ['npm-release'], source = 'chosen', catalog = CATALOG, catalogOk = true, patchAnswer } = {}) {
|
|
80
80
|
const requests = [];
|
|
81
81
|
const els = new Map();
|
|
82
82
|
const listeners = new Map();
|
|
@@ -156,14 +156,14 @@ function boot({ selected = ['dev-box'], source = 'chosen', catalog = CATALOG, ca
|
|
|
156
156
|
|
|
157
157
|
test('the band renders the optional catalog the server offered — a row and a sentence each', async () => {
|
|
158
158
|
// Arrange
|
|
159
|
-
const ui = boot({ selected: ['
|
|
159
|
+
const ui = boot({ selected: ['npm-release'] });
|
|
160
160
|
|
|
161
161
|
// Act
|
|
162
162
|
await ui.render();
|
|
163
163
|
|
|
164
164
|
// Assert
|
|
165
|
-
assert.deepEqual(ui.rows().map((r) => r.key), ['
|
|
166
|
-
assert.deepEqual(ui.rows().map((r) => r.label), ['
|
|
165
|
+
assert.deepEqual(ui.rows().map((r) => r.key), ['npm-release', 'discord', 'agents']);
|
|
166
|
+
assert.deepEqual(ui.rows().map((r) => r.label), ['npm release', 'Discord', 'Agents']);
|
|
167
167
|
for (const r of ui.rows()) assert.ok(r.summary.length > 20, `${r.label} says what it is for`);
|
|
168
168
|
});
|
|
169
169
|
|
|
@@ -171,11 +171,11 @@ test('what the project runs today is what is switched on — resolved by the ser
|
|
|
171
171
|
// A project that SKIPPED the picker reads as its own starter bundle, and the
|
|
172
172
|
// band must show that set rather than an empty one: the whole point of
|
|
173
173
|
// effectiveModules resolving `source: 'bundle'` on the way out.
|
|
174
|
-
const ui = boot({ selected: ['
|
|
174
|
+
const ui = boot({ selected: ['npm-release', 'discord'], source: 'bundle' });
|
|
175
175
|
|
|
176
176
|
await ui.render();
|
|
177
177
|
|
|
178
|
-
assert.deepEqual(ui.rows().filter((r) => r.on).map((r) => r.key), ['
|
|
178
|
+
assert.deepEqual(ui.rows().filter((r) => r.on).map((r) => r.key), ['npm-release', 'discord']);
|
|
179
179
|
assert.deepEqual(ui.rows().filter((r) => !r.on).map((r) => r.key), ['agents']);
|
|
180
180
|
});
|
|
181
181
|
|
|
@@ -185,7 +185,7 @@ test('the always-on core is COUNTED and stated, and is not a row anyone can swit
|
|
|
185
185
|
await ui.render();
|
|
186
186
|
|
|
187
187
|
assert.match(ui.slot(), /the same 3 core parts/, 'the clause names how many, from the server\'s own list');
|
|
188
|
-
assert.deepEqual(ui.rows().map((r) => r.key), ['
|
|
188
|
+
assert.deepEqual(ui.rows().map((r) => r.key), ['npm-release', 'discord', 'agents'], 'and no core key is on the band');
|
|
189
189
|
for (const key of CATALOG.core) {
|
|
190
190
|
assert.equal(ui.rows().some((r) => r.key === key), false, `${key} is not a choice`);
|
|
191
191
|
}
|
|
@@ -196,26 +196,26 @@ test('a toggle issues the write — the WHOLE list, in catalog order, on the own
|
|
|
196
196
|
const ui = boot({ selected: ['discord'] });
|
|
197
197
|
await ui.render();
|
|
198
198
|
|
|
199
|
-
// Act — switch
|
|
200
|
-
await ui.click('
|
|
199
|
+
// Act — switch npm release ON
|
|
200
|
+
await ui.click('npm-release');
|
|
201
201
|
|
|
202
202
|
// Assert
|
|
203
203
|
const patch = ui.requests.find((r) => r.method === 'PATCH');
|
|
204
204
|
assert.ok(patch, 'a PATCH was sent');
|
|
205
205
|
assert.equal(patch.url, '/api/bongos/provisioning/instances/18/detail');
|
|
206
|
-
assert.deepEqual(JSON.parse(patch.body), { modules: ['
|
|
206
|
+
assert.deepEqual(JSON.parse(patch.body), { modules: ['npm-release', 'discord'] },
|
|
207
207
|
'catalog order, exactly as the column stores it — never append order');
|
|
208
208
|
assert.equal(patch.credentials, 'same-origin', 'the cookie session is the auth — this page carries no token');
|
|
209
209
|
});
|
|
210
210
|
|
|
211
211
|
test('a toggle can REMOVE one too — the same list, one key shorter', async () => {
|
|
212
|
-
const ui = boot({ selected: ['
|
|
212
|
+
const ui = boot({ selected: ['npm-release', 'agents'] });
|
|
213
213
|
await ui.render();
|
|
214
214
|
|
|
215
215
|
await ui.click('agents');
|
|
216
216
|
|
|
217
217
|
const patch = ui.requests.find((r) => r.method === 'PATCH');
|
|
218
|
-
assert.deepEqual(JSON.parse(patch.body), { modules: ['
|
|
218
|
+
assert.deepEqual(JSON.parse(patch.body), { modules: ['npm-release'] });
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
test('switching the last one off sends an empty list — a real answer, not a skipped write', async () => {
|
|
@@ -243,8 +243,8 @@ test('the band repaints from the PLATFORM’S answer, and says the save changed
|
|
|
243
243
|
|
|
244
244
|
test('a refusal is SAID in the server’s own words, and the band goes back to what the row still holds', async () => {
|
|
245
245
|
const ui = boot({
|
|
246
|
-
selected: ['
|
|
247
|
-
patchAnswer: Promise.resolve({ ok: false, status: 400, _json: { error: { code: 'bad_modules', message: 'Unknown module "discrod" — the choices are:
|
|
246
|
+
selected: ['npm-release'],
|
|
247
|
+
patchAnswer: Promise.resolve({ ok: false, status: 400, _json: { error: { code: 'bad_modules', message: 'Unknown module "discrod" — the choices are: npm-release, discord, agents.' } } }),
|
|
248
248
|
});
|
|
249
249
|
await ui.render();
|
|
250
250
|
|
|
@@ -252,7 +252,7 @@ test('a refusal is SAID in the server’s own words, and the band goes back to w
|
|
|
252
252
|
|
|
253
253
|
assert.equal(ui.msg().className, 'projMsg err');
|
|
254
254
|
assert.match(ui.msg().textContent, /Unknown module "discrod"/);
|
|
255
|
-
assert.deepEqual(ui.rows().filter((r) => r.on).map((r) => r.key), ['
|
|
255
|
+
assert.deepEqual(ui.rows().filter((r) => r.on).map((r) => r.key), ['npm-release'],
|
|
256
256
|
'a refused write leaves the band showing the project’s real set');
|
|
257
257
|
});
|
|
258
258
|
|
|
@@ -269,7 +269,7 @@ test('a signed-out save offers a way back in rather than a dead end', async () =
|
|
|
269
269
|
});
|
|
270
270
|
|
|
271
271
|
test('a catalog that ANSWERED badly says so; nothing about the project is claimed to have changed', async () => {
|
|
272
|
-
const ui = boot({ selected: ['
|
|
272
|
+
const ui = boot({ selected: ['npm-release'], catalogOk: false });
|
|
273
273
|
|
|
274
274
|
await ui.render();
|
|
275
275
|
await ui.settle();
|