Files
msd-core/tests/federated-config-key-removal.test.cjs
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

419 lines
14 KiB
JavaScript

'use strict';
/**
* federated-config-key-removal.test.cjs
*
* ADR-857 deliverable F — Decision 3:
* "Uninstalling a Capability removes its config keys cleanly."
*
* Tests:
* [happy] Capability X present → its key surfaces; X removed → key gone, others intact.
* [happy] loadConfig after removing a capability no longer surfaces the key, but central
* base-config keys remain.
* [BVA] Orphaned user value for the removed key is dropped — not leaked as a phantom key.
* [negative] Removing capability 'x' does NOT drop a differently-prefixed capability's key
* (e.g. 'xy.enabled' survives when only 'x.enabled' is removed).
*/
const { describe, test, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const { cleanup } = require('./helpers.cjs');
const { mergeFederatedConfig } = require('../msd-core/bin/lib/federated-config.cjs');
const configLoader = require('../msd-core/bin/lib/config-loader.cjs');
const {
loadConfig,
_setFederatedRegistryForTests,
_resetFederatedRegistryForTests,
} = configLoader;
// ─── Fixtures ─────────────────────────────────────────────────────────────────
/** Never treated as a central key — all keys federated freely. */
const neverCentral = (_key) => false;
/** Minimal well-formed boolean slice. */
function boolSlice(owner, defaultValue = true) {
return { owner, type: 'boolean', default: defaultValue, description: `Boolean key for ${owner}.` };
}
// ─── Temp project helpers (mirrors federated-config-loadconfig.test.cjs) ──────
let tmpDirs = [];
beforeEach(() => {
tmpDirs = [];
_resetFederatedRegistryForTests();
});
afterEach(() => {
_resetFederatedRegistryForTests();
for (const d of tmpDirs) {
cleanup(d);
}
});
function mkTempProject() {
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cap-removal-test-'));
tmpDirs.push(d);
fs.mkdirSync(path.join(d, '.planning', 'phases'), { recursive: true });
return d;
}
function writeConfig(dir, obj) {
fs.writeFileSync(
path.join(dir, '.planning', 'config.json'),
JSON.stringify(obj, null, 2),
'utf-8',
);
}
// ─── 1. [happy] key present → surfaces; capability removed → key gone ─────────
describe('[happy] capability X present then removed — key lifecycle', () => {
test('registry WITH capability X: x.enabled surfaces with default true', () => {
const withX = {
'x.enabled': boolSlice('cap-x', true),
};
const result = mergeFederatedConfig({
configSchema: withX,
isCentralKey: neverCentral,
userConfig: {},
});
// Positive assertion: the key is in validKeys AND values
assert.ok(
result.validKeys.includes('x.enabled'),
'x.enabled must be in validKeys when cap-x is installed',
);
assert.strictEqual(
result.values['x.enabled'],
true,
'x.enabled default must be true',
);
assert.deepEqual(result.warnings, [], 'no warnings for valid federated key');
});
test('registry WITHOUT capability X: x.enabled is absent from result', () => {
// Registry after cap-x is uninstalled — its key is no longer in configSchema
const withoutX = {};
const result = mergeFederatedConfig({
configSchema: withoutX,
isCentralKey: neverCentral,
userConfig: {},
});
assert.ok(
!result.validKeys.includes('x.enabled'),
'x.enabled must NOT be in validKeys after cap-x is removed',
);
assert.ok(
!Object.prototype.hasOwnProperty.call(result.values, 'x.enabled'),
'x.enabled must NOT appear in values after cap-x is removed',
);
assert.strictEqual(Object.keys(result.values).length, 0, 'values must be empty');
});
test('[no contamination] removing cap-x leaves cap-y.flag intact', () => {
// Before removal: both capabilities present
const withBoth = {
'x.enabled': boolSlice('cap-x', true),
'y.flag': boolSlice('cap-y', false),
};
const before = mergeFederatedConfig({
configSchema: withBoth,
isCentralKey: neverCentral,
userConfig: {},
});
assert.ok(before.validKeys.includes('x.enabled'), 'x.enabled present before removal');
assert.ok(before.validKeys.includes('y.flag'), 'y.flag present before removal');
// After removal: only cap-y remains in the registry
const withoutX = {
'y.flag': boolSlice('cap-y', false),
};
const after = mergeFederatedConfig({
configSchema: withoutX,
isCentralKey: neverCentral,
userConfig: {},
});
// x.enabled must be gone
assert.ok(
!after.validKeys.includes('x.enabled'),
'x.enabled must be absent after cap-x removal',
);
assert.ok(
!Object.prototype.hasOwnProperty.call(after.values, 'x.enabled'),
'x.enabled must not appear in values after removal',
);
// y.flag must still be present AND have the correct value
assert.ok(
after.validKeys.includes('y.flag'),
'y.flag must still be in validKeys after cap-x removal',
);
assert.strictEqual(
after.values['y.flag'],
false,
'y.flag value must remain false (its default) after cap-x removal',
);
});
});
// ─── 2. [happy] loadConfig: removed capability key absent, base keys intact ───
describe('[happy] loadConfig after capability removal — base keys survive', () => {
test('cap-x present → loadConfig surfaces mytool.enabled; cap-x absent → key gone', () => {
const tmpDir = mkTempProject();
writeConfig(tmpDir, {});
// Phase A: cap-x installed
_setFederatedRegistryForTests({
configSchema: {
'mytool.enabled': boolSlice('cap-x', true),
},
});
const resultWith = loadConfig(tmpDir);
assert.ok(
typeof resultWith['mytool'] === 'object' && resultWith['mytool'] !== null,
'mytool section must exist when cap-x is installed',
);
assert.strictEqual(
resultWith['mytool']['enabled'],
true,
'mytool.enabled must be true (cap-x default)',
);
// Phase B: cap-x uninstalled — registry now empty
_resetFederatedRegistryForTests();
_setFederatedRegistryForTests({ configSchema: {} });
const resultWithout = loadConfig(tmpDir);
// Central base-config key must still be present
assert.ok(
Object.prototype.hasOwnProperty.call(resultWithout, 'model_profile'),
'model_profile (central key) must still exist after cap-x removal',
);
// Federated key must be absent — either undefined or not surfaced under 'mytool'
const myToolSection = resultWithout['mytool'];
const enabledValue = (myToolSection && typeof myToolSection === 'object')
? myToolSection['enabled']
: undefined;
assert.strictEqual(
enabledValue,
undefined,
'mytool.enabled must NOT be present after cap-x removal; got: ' + JSON.stringify(enabledValue),
);
});
test('base config keys (model_profile, research) survive capability removal', () => {
const tmpDir = mkTempProject();
writeConfig(tmpDir, { model_profile: 'fast', research: false });
// Install and then remove a synthetic capability
_setFederatedRegistryForTests({
configSchema: {
'extra.flag': boolSlice('cap-extra', true),
},
});
const before = loadConfig(tmpDir);
assert.strictEqual(before['model_profile'], 'fast', 'model_profile from user config before removal');
assert.strictEqual(before['research'], false, 'research from user config before removal');
_setFederatedRegistryForTests({ configSchema: {} });
const after = loadConfig(tmpDir);
// Central keys from user's config.json must be unchanged
assert.strictEqual(after['model_profile'], 'fast', 'model_profile must survive capability removal');
assert.strictEqual(after['research'], false, 'research must survive capability removal');
});
});
// ─── 3. [BVA] orphaned user value not surfaced after removal ──────────────────
describe('[BVA] orphaned user value is silently dropped after capability removal', () => {
test('user config sets removed key → orphaned value not surfaced as phantom', () => {
// User has 'mytool.enabled': false in their config.json
// BUT the capability is now uninstalled (not in registry configSchema)
const result = mergeFederatedConfig({
configSchema: {}, // cap-x removed — configSchema is empty
isCentralKey: neverCentral,
userConfig: { mytool: { enabled: false } }, // user value remains in file
});
// The orphaned user value must NOT leak into validKeys or values
assert.ok(
!result.validKeys.includes('mytool.enabled'),
'orphaned user key must not appear in validKeys',
);
assert.ok(
!Object.prototype.hasOwnProperty.call(result.values, 'mytool.enabled'),
'orphaned user key must not appear in values',
);
// The entire values map must be empty (no phantom keys)
assert.strictEqual(
Object.keys(result.values).length,
0,
'values must be empty when registry has no keys — got: ' + JSON.stringify(Object.keys(result.values)),
);
assert.deepEqual(result.validKeys, [], 'validKeys must be empty when registry has no keys');
});
test('user config sets removed key — top-level orphan also not surfaced', () => {
// Top-level orphan: user set 'orphan_flag' but the cap is gone
const result = mergeFederatedConfig({
configSchema: {},
isCentralKey: neverCentral,
userConfig: { orphan_flag: true },
});
assert.ok(
!Object.prototype.hasOwnProperty.call(result.values, 'orphan_flag'),
'top-level orphaned key must not appear in values',
);
assert.deepEqual(result.validKeys, []);
assert.strictEqual(Object.keys(result.values).length, 0);
});
test('loadConfig: orphaned user value in config.json not surfaced after removal', () => {
const tmpDir = mkTempProject();
// User config contains a value for a key whose capability will be removed
writeConfig(tmpDir, { orphancap: { flag: true } });
// Capability removed: inject empty registry
_setFederatedRegistryForTests({ configSchema: {} });
const result = loadConfig(tmpDir);
// The orphaned capability key must NOT be surfaced in the resolved config object.
// loadConfig extracts only known/central/federated keys into _baseConfig — any key
// whose capability has been uninstalled (configSchema: {}) must not appear in the result.
assert.ok(
!Object.prototype.hasOwnProperty.call(result, 'orphancap'),
'orphaned top-level key must not appear in resolved config when capability is removed',
);
// Central keys must remain unaffected by capability removal.
assert.ok(
Object.prototype.hasOwnProperty.call(result, 'model_profile'),
'model_profile must still be present (central key unaffected by federated removal)',
);
});
});
// ─── 4. [negative] removing 'x' does NOT drop 'xy.enabled' ──────────────────
describe('[negative] prefix-adjacent key not dropped when shorter-prefix cap removed', () => {
test("removing cap 'x' (key x.enabled) does NOT remove cap 'xy' (key xy.enabled)", () => {
// Registry after 'x' is uninstalled but 'xy' remains
const registryAfterXRemoved = {
'xy.enabled': boolSlice('cap-xy', false),
};
const result = mergeFederatedConfig({
configSchema: registryAfterXRemoved,
isCentralKey: neverCentral,
userConfig: {},
});
// x.enabled must not appear (was removed)
assert.ok(
!result.validKeys.includes('x.enabled'),
'x.enabled must not appear (cap-x was uninstalled)',
);
assert.ok(
!Object.prototype.hasOwnProperty.call(result.values, 'x.enabled'),
'x.enabled must not be in values',
);
// xy.enabled MUST still appear (different capability)
assert.ok(
result.validKeys.includes('xy.enabled'),
'xy.enabled must still be present after cap-x removal',
);
assert.strictEqual(
result.values['xy.enabled'],
false,
'xy.enabled value must be false (cap-xy default), not contaminated by cap-x removal',
);
});
test("removing 'x' does not drop 'x2.enabled' (numeric suffix, distinct cap)", () => {
const registryAfterXRemoved = {
'x2.enabled': boolSlice('cap-x2', true),
};
const result = mergeFederatedConfig({
configSchema: registryAfterXRemoved,
isCentralKey: neverCentral,
userConfig: {},
});
assert.ok(
!result.validKeys.includes('x.enabled'),
'x.enabled must not appear (not in registry)',
);
assert.ok(
result.validKeys.includes('x2.enabled'),
'x2.enabled must survive — it belongs to cap-x2, not cap-x',
);
assert.strictEqual(
result.values['x2.enabled'],
true,
'x2.enabled must have its own default (true)',
);
});
test("removing 'alpha' cap does not affect 'alphabeta.flag' cap", () => {
const registryAfterAlphaRemoved = {
'alphabeta.flag': boolSlice('cap-alphabeta', false),
'gamma.flag': boolSlice('cap-gamma', true),
};
const result = mergeFederatedConfig({
configSchema: registryAfterAlphaRemoved,
isCentralKey: neverCentral,
userConfig: {},
});
// alpha.flag not present (removed)
assert.ok(
!result.validKeys.includes('alpha.flag'),
'alpha.flag must not appear after cap-alpha removal',
);
// alphabeta.flag MUST be present (distinct capability)
assert.ok(
result.validKeys.includes('alphabeta.flag'),
'alphabeta.flag must survive removal of alpha capability',
);
assert.strictEqual(
result.values['alphabeta.flag'],
false,
'alphabeta.flag value must be false (its own default)',
);
// gamma.flag also unaffected
assert.ok(
result.validKeys.includes('gamma.flag'),
'gamma.flag must be unaffected by alpha removal',
);
assert.strictEqual(
result.values['gamma.flag'],
true,
'gamma.flag value must be true (its own default)',
);
});
});