Merge DC-091: config-schema KNOWN_KEYS licenseBackup/_version fix [glm-grade=A]
CI / Test & Lint (push) Canceled after 0s
CI / Security audit (push) Canceled after 0s

This commit is contained in:
Hermes
2026-08-22 17:22:20 -07:00
2 changed files with 77 additions and 1 deletions
@@ -0,0 +1,72 @@
'use strict';
/**
* Regression tests for config-schema.js KNOWN_KEYS — DC-091.
*
* Bug: license-manager.js persists config.licenseBackup (activation
* restore-on-restart) and src/config/migrations.js stamps config._version,
* but neither key was in KNOWN_KEYS — so every startup logged
* `Unknown config key "licenseBackup" / "_version" — possible typo?`
* false positives (verified in live dashcaddy-api container logs,
* 2026-08-22T23:53:54Z restart).
*
* These tests pin: (1) the live production config key set validates with
* zero unknown-key warnings, (2) genuine typos still warn, (3) the schema
* stays in sync with the first-party writer keys.
*/
const { validateConfig } = require('../src/utilities/config-schema');
describe('config-schema KNOWN_KEYS vs first-party writers (DC-091)', () => {
// Exact key set of the live production config.json (DNS2, verified
// 2026-08-23). If a new key appears here, teach KNOWN_KEYS about it —
// or fix the writer if it's a typo.
const LIVE_CONFIG_KEYS = [
'_version', 'configurationType', 'customFavicon', 'customLogo',
'dashboardHost', 'dashboardTitle', 'dns', 'dnsServers', 'language',
'license', 'licenseBackup', 'logoPosition', 'pylon', 'setupComplete',
'timestamp', 'tld', 'updatedAt'
];
test('live production config key set produces zero unknown-key warnings', () => {
const config = {};
for (const key of LIVE_CONFIG_KEYS) {
// Minimal valid-ish values; validateConfig only cares about shape
// for these keys, and unknown-key detection is the target here.
config[key] = key === '_version' ? 2 : (key === 'dnsServers' ? {} : 'x');
}
const result = validateConfig(config);
const unknownWarnings = result.warnings.filter((w) => w.includes('Unknown config key'));
expect(unknownWarnings).toEqual([]);
});
test('licenseBackup and _version (first-party writer keys) do not warn', () => {
const result = validateConfig({ licenseBackup: { code: 'DC-...' }, _version: 2 });
expect(result.warnings).toEqual([]);
});
test('genuine typos still warn (guard against over-allowing)', () => {
const result = validateConfig({ dashboadTitle: 'typo' });
expect(result.warnings).toEqual([
'Unknown config key "dashboadTitle" — possible typo?'
]);
});
test('KNOWN_KEYS stays in sync with license-manager writer keys', () => {
// license-manager writes config.licenseBackup and config.license — both
// must be recognized. We assert via validateConfig (public surface)
// rather than importing the private KNOWN_KEYS array.
const result = validateConfig({ license: { code: 'DC-...' }, licenseBackup: { code: 'DC-...' } });
expect(result.warnings.filter((w) => w.includes('Unknown config key'))).toEqual([]);
});
});
describe('config-schema sync guard: migrations writer', () => {
test('_version is recognized at every migration version value', () => {
// migrations.js bumps _version 0→1→2; the key itself must never warn.
for (const v of [0, 1, 2, 99]) {
const result = validateConfig({ _version: v });
expect(result.warnings).toEqual([]);
}
});
});
+5 -1
View File
@@ -14,7 +14,11 @@ const KNOWN_KEYS = [
'configurationType', 'defaults', 'customLogo', 'customFavicon',
'dashboardTitle', 'tailscale', 'license', 'skipped',
'routingMode', 'domain', 'email', 'defaultIP', 'pylon',
'customLogoDark', 'customLogoLight', 'language'
'customLogoDark', 'customLogoLight', 'language',
// license-manager.js persists the last activation to config.licenseBackup
// (restore-on-restart path); src/config/migrations.js stamps _version.
// Both are first-party writes — see DC-091.
'licenseBackup', '_version'
];
/**