Files
evomap__evolver/test/config.test.js
evolver-publish a1d22a3d5f Release v1.88.1
2026-06-03 18:04:02 +00:00

198 lines
7.3 KiB
JavaScript

const { describe, it, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const config = require('../src/config');
const { envInt, envPositiveInt, envFloat, envStr, resolveHubUrl } = config;
// All env accessors read process.env on every call (resolveHubUrl is
// explicitly documented as re-evaluating each call, and the env* helpers
// take an arbitrary key), so these tests drive the real exported functions
// directly — no require-cache juggling needed. We save/restore every env var
// we touch around each test so the suite never leaks state, mirroring
// envFingerprint.test.js.
const TOUCHED_ENV = [
'__CFG_TEST_KEY__',
'A2A_HUB_URL',
'EVOMAP_HUB_URL',
'EVOLVER_DEFAULT_HUB_URL',
'EVOMAP_HUB_ALLOW_INSECURE',
];
let savedEnv;
beforeEach(function () {
savedEnv = {};
for (const k of TOUCHED_ENV) { savedEnv[k] = process.env[k]; delete process.env[k]; }
});
afterEach(function () {
for (const k of TOUCHED_ENV) {
if (savedEnv[k] === undefined) delete process.env[k];
else process.env[k] = savedEnv[k];
}
});
const KEY = '__CFG_TEST_KEY__';
describe('envInt', function () {
it('falls back when unset', function () {
assert.equal(envInt(KEY, 42), 42);
});
it('falls back on empty string', function () {
process.env[KEY] = '';
assert.equal(envInt(KEY, 42), 42);
});
it('parses a valid integer', function () {
process.env[KEY] = '7';
assert.equal(envInt(KEY, 42), 7);
});
it('parses leading integer of a suffixed value (parseInt semantics)', function () {
process.env[KEY] = '5min';
assert.equal(envInt(KEY, 42), 5);
});
it('falls back on a non-numeric value', function () {
process.env[KEY] = 'abc';
assert.equal(envInt(KEY, 42), 42);
});
it('accepts 0 and negatives (unlike the positive variant)', function () {
process.env[KEY] = '0';
assert.equal(envInt(KEY, 42), 0);
process.env[KEY] = '-3';
assert.equal(envInt(KEY, 42), -3);
});
});
describe('envFloat', function () {
it('falls back when unset', function () {
assert.equal(envFloat(KEY, 0.5), 0.5);
});
it('falls back on empty string', function () {
process.env[KEY] = '';
assert.equal(envFloat(KEY, 0.5), 0.5);
});
it('parses a valid float', function () {
process.env[KEY] = '0.78';
assert.equal(envFloat(KEY, 0.5), 0.78);
});
it('falls back on a non-numeric value', function () {
process.env[KEY] = 'nope';
assert.equal(envFloat(KEY, 0.5), 0.5);
});
});
describe('envStr', function () {
it('falls back when unset', function () {
assert.equal(envStr(KEY, 'def'), 'def');
});
it('falls back on empty string', function () {
process.env[KEY] = '';
assert.equal(envStr(KEY, 'def'), 'def');
});
it('returns the set value', function () {
process.env[KEY] = 'custom';
assert.equal(envStr(KEY, 'def'), 'custom');
});
});
describe('envPositiveInt', function () {
it('falls back when unset', function () {
assert.equal(envPositiveInt(KEY, 1000), 1000);
});
it('falls back on empty string', function () {
process.env[KEY] = '';
assert.equal(envPositiveInt(KEY, 1000), 1000);
});
it('parses a valid positive integer', function () {
process.env[KEY] = '250';
assert.equal(envPositiveInt(KEY, 1000), 250);
});
it('accepts the leading positive integer of a suffixed value', function () {
process.env[KEY] = '5min';
assert.equal(envPositiveInt(KEY, 1000), 5);
});
it('rejects 0 (would hot-loop / zero a timeout) and falls back', function () {
process.env[KEY] = '0';
assert.equal(envPositiveInt(KEY, 1000), 1000);
});
it('rejects negatives and falls back', function () {
process.env[KEY] = '-5';
assert.equal(envPositiveInt(KEY, 1000), 1000);
});
it('rejects a suffix-only NaN value and falls back', function () {
process.env[KEY] = 'ms';
assert.equal(envPositiveInt(KEY, 1000), 1000);
});
it('rejects values >= 2^31 (setTimeout downgrades them to 1ms) and falls back', function () {
process.env[KEY] = String(2 ** 31);
assert.equal(envPositiveInt(KEY, 1000), 1000);
});
it('accepts the largest valid value just below 2^31', function () {
process.env[KEY] = String(2 ** 31 - 1);
assert.equal(envPositiveInt(KEY, 1000), 2 ** 31 - 1);
});
it('warns at most once per key on invalid input', function () {
const origWarn = console.warn;
const calls = [];
console.warn = (...args) => calls.push(args);
try {
// Use a key never warned before in this process so the one-time Set is clean.
const uniqueKey = '__CFG_TEST_WARN_ONCE__';
const savedUnique = process.env[uniqueKey];
try {
process.env[uniqueKey] = 'not-a-number';
envPositiveInt(uniqueKey, 1000);
envPositiveInt(uniqueKey, 1000);
envPositiveInt(uniqueKey, 1000);
} finally {
if (savedUnique === undefined) delete process.env[uniqueKey];
else process.env[uniqueKey] = savedUnique;
}
assert.equal(calls.length, 1, 'expected exactly one warning for repeated invalid reads of the same key');
assert.match(calls[0][0], /\[config\] __CFG_TEST_WARN_ONCE__=/);
} finally {
console.warn = origWarn;
}
});
});
describe('resolveHubUrl', function () {
it('returns the compile-time default when no env override is set', function () {
assert.equal(resolveHubUrl(), config.PUBLIC_DEFAULT_HUB_URL);
assert.equal(resolveHubUrl(), 'https://evomap.ai');
});
it('prefers A2A_HUB_URL over all other sources', function () {
process.env.A2A_HUB_URL = 'https://a2a.example';
process.env.EVOMAP_HUB_URL = 'https://evomap2.example';
process.env.EVOLVER_DEFAULT_HUB_URL = 'https://deploy.example';
assert.equal(resolveHubUrl(), 'https://a2a.example');
});
it('falls back to EVOMAP_HUB_URL when A2A_HUB_URL is unset', function () {
process.env.EVOMAP_HUB_URL = 'https://evomap2.example';
process.env.EVOLVER_DEFAULT_HUB_URL = 'https://deploy.example';
assert.equal(resolveHubUrl(), 'https://evomap2.example');
});
it('falls back to EVOLVER_DEFAULT_HUB_URL when the higher tiers are unset', function () {
process.env.EVOLVER_DEFAULT_HUB_URL = 'https://deploy.example';
assert.equal(resolveHubUrl(), 'https://deploy.example');
});
it('re-evaluates on every call (no module-load caching)', function () {
assert.equal(resolveHubUrl(), 'https://evomap.ai');
process.env.A2A_HUB_URL = 'https://later.example';
assert.equal(resolveHubUrl(), 'https://later.example');
});
it('throws on a non-https hub URL by default', function () {
process.env.A2A_HUB_URL = 'http://insecure.example';
assert.throws(() => resolveHubUrl(), /must use https/);
});
it('throws on a syntactically invalid hub URL by default', function () {
process.env.A2A_HUB_URL = 'not a url';
assert.throws(() => resolveHubUrl(), /not a valid URL/);
});
it('allows a non-https hub URL when EVOMAP_HUB_ALLOW_INSECURE=1', function () {
process.env.A2A_HUB_URL = 'http://insecure.example';
process.env.EVOMAP_HUB_ALLOW_INSECURE = '1';
assert.equal(resolveHubUrl(), 'http://insecure.example');
});
it('does not bypass the https check for ALLOW_INSECURE values other than exactly "1"', function () {
process.env.A2A_HUB_URL = 'http://insecure.example';
process.env.EVOMAP_HUB_ALLOW_INSECURE = 'true';
assert.throws(() => resolveHubUrl(), /must use https/);
});
});