test/devin-connect-warning-cache-bound.test.js
Rama master · réplica de lectura del repositorio original.
// D-SCALE-001 (scale lane, reproduced P2) — the DEVIN_CONNECT degrade-warning record
// was process-lifetime AND caller-controlled.
//
// resolveConnectSelector() records every UNKNOWN model name so the "paid request
// downgraded to free tier" warning fires once per distinct name instead of on every
// request. That record had no cap, no TTL and no delete, and src/handlers/chat.js:3245
// resolves the model BEFORE the strict-model 400 at :3253 — so a completed, rejected
// request still left the caller's raw string resident until restart. 1,025 distinct
// junk names retained 1,025 strings; nothing in the suite bounded it.
//
// The repair keeps the dedupe, keys the cache by SHA-256 digest (fixed 64 chars, so
// retained bytes are bounded as well as cardinality) and evicts the oldest insertion
// once 1,024 entries are exceeded. A duplicate hit does NOT refresh its position, so
// the oldest name may warn again after eviction.
//
// Everything below drives the REAL resolver and the REAL handler; the only seam is
// __testing.degradeWarned, the same object the two pre-existing suites reset. Bounded
// fixtures only: short synthetic names, no sockets, no accounts, no upstream calls.
// A live socket attempt fails loudly here — the deny preload that the audit gates
// export into this child turns any non-loopback connect into NETWORK_STUB_MISS.
import { afterEach, beforeEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { resolveConnectSelector, __testing, FREE_TIER_SELECTOR } from '../src/devin-connect-models.js';
import { handleChatCompletions } from '../src/handlers/chat.js';
import { buildConnectReachability } from '../src/handlers/models.js';
// The cap Main approved: at most 1,024 retained entries, oldest insertion evicted.
const MAX_ENTRIES = 1024;
const DEGRADE_MARK = 'paid request downgraded to free tier';
const SAVED_ENV = {
DEVIN_CONNECT: process.env.DEVIN_CONNECT,
WINDSURFAPI_STRICT_MODEL: process.env.WINDSURFAPI_STRICT_MODEL,
};
let warnings = [];
let originalWarn = null;
/** Warning lines this test process captured; the filter isolates the degrade signal. */
const degradeWarns = () => warnings.filter((line) => line.includes(DEGRADE_MARK));
const degradeWarnsFor = (name) => degradeWarns().filter((line) => line.includes(name)).length;
beforeEach(() => {
warnings = [];
originalWarn = console.warn;
console.warn = (...args) => { warnings.push(args.join(' ')); };
__testing.degradeWarned.clear();
});
afterEach(() => {
console.warn = originalWarn;
__testing.degradeWarned.clear();
for (const [key, value] of Object.entries(SAVED_ENV)) {
if (value === undefined) delete process.env[key];
else process.env[key] = value;
}
});
describe('DEVIN_CONNECT degrade-warning cache is bounded (D-SCALE-001)', () => {
it('warning-cache-bounds-distinct-rejections', () => {
const count = MAX_ENTRIES + 1;
for (let i = 0; i < count; i++) {
const r = resolveConnectSelector(`zzscale-junk-${i}`);
assert.equal(r.selector, FREE_TIER_SELECTOR, 'an unknown name still degrades to the free selector');
assert.equal(r.mapped, false, 'an unknown name still reports mapped:false');
}
assert.equal(
degradeWarns().length, count,
'every distinct unknown name still gets its one warning — the bound must not swallow names',
);
assert.ok(
__testing.degradeWarned.size <= MAX_ENTRIES,
`${count} distinct rejected names left ${__testing.degradeWarned.size} entries resident; the cache must stay at or below ${MAX_ENTRIES}`,
);
assert.equal(
__testing.degradeWarned.size, MAX_ENTRIES,
'after overflowing by one the cache sits exactly at its cap',
);
});
it('warning-cache-forgets-oldest-keeps-recent', () => {
const oldest = 'zzscale-oldest-ghost';
const recent = 'zzscale-recent-ghost';
resolveConnectSelector(oldest);
resolveConnectSelector(oldest);
assert.equal(degradeWarnsFor(oldest), 1, 'a repeat of the same unknown name is deduped');
// Fill the cache: 1,024 distinct names is enough to push the oldest insertion out.
for (let i = 0; i < MAX_ENTRIES; i++) resolveConnectSelector(`zzscale-fill-${i}`);
resolveConnectSelector(recent);
resolveConnectSelector(oldest);
assert.equal(
degradeWarnsFor(oldest), 2,
'the evicted oldest name must be able to warn again instead of being suppressed forever',
);
resolveConnectSelector(recent);
assert.equal(degradeWarnsFor(recent), 1, 'a recent name stays deduped after the eviction');
});
it('warning-cache-retains-no-raw-long-model', () => {
const longName = `zzscale-long-${'ghostname'.repeat(40)}`;
assert.ok(longName.length > 300, 'fixture precondition: a modestly long synthetic name');
resolveConnectSelector(longName);
const entries = [...__testing.degradeWarned];
assert.equal(entries.length, 1, 'the unknown name is still recorded, so the dedupe works');
for (const key of entries) {
assert.notEqual(key, longName, 'the raw caller-supplied name must never be retained');
assert.equal(
String(key).includes('zzscale-long-'), false,
'no retained key may embed the raw model name',
);
assert.equal(
String(key).length, 64,
`retained keys must be fixed-width digests, not caller-length strings (got ${String(key).length} chars)`,
);
assert.match(String(key), /^[0-9a-f]{64}$/, 'a retained key is a lowercase hex SHA-256 digest');
}
});
it('warning-cache-controls-preserve-resolution', () => {
assert.equal(
typeof __testing.degradeWarned.clear, 'function',
'the pre-existing suites reset this seam with __testing.degradeWarned.clear()',
);
const before = __testing.degradeWarned.size;
const known = 'claude-opus-4-8-medium';
assert.deepEqual(
resolveConnectSelector(known),
{ selector: known, mapped: true },
'a real catalog selector still resolves to itself',
);
assert.deepEqual(
resolveConnectSelector('claude-opus-4.8'),
{ selector: known, mapped: true },
'a curated dotted alias still resolves to its catalog target',
);
assert.deepEqual(
resolveConnectSelector(''),
{ selector: FREE_TIER_SELECTOR, mapped: false },
'empty input still returns the free-tier default',
);
assert.deepEqual(
resolveConnectSelector('zzscale-warnoff-ghost', { warnOnFallback: false }),
{ selector: FREE_TIER_SELECTOR, mapped: false },
'warnOnFallback:false still degrades silently',
);
assert.equal(
__testing.degradeWarned.size, before,
'a mapped, empty or silent call must not consume cache entries',
);
assert.equal(degradeWarns().length, 0, 'none of the control calls emits the degrade warning');
});
it('warning-cache-handler-strict400-unchanged', async () => {
process.env.DEVIN_CONNECT = '1';
process.env.WINDSURFAPI_STRICT_MODEL = '1';
const model = 'zzscale-handler-ghost';
const first = await handleChatCompletions({
model,
messages: [{ role: 'user', content: 'ping' }],
});
assert.equal(first.status, 400, 'an unmapped model is still rejected by the strict gate');
assert.equal(first.body.error.code, 'model_not_found');
assert.equal(first.body.error.param, 'model');
assert.match(first.body.error.message, /not a valid model/i);
assert.equal(degradeWarnsFor(model), 1, 'the first rejected request warns once');
const sizeAfterFirst = __testing.degradeWarned.size;
const second = await handleChatCompletions({
model,
messages: [{ role: 'user', content: 'ping' }],
});
assert.deepEqual(second, first, 'the strict rejection body must not move');
assert.equal(degradeWarnsFor(model), 1, 'a repeat rejected request is deduped');
assert.equal(
__testing.degradeWarned.size, sizeAfterFirst,
'a repeat rejected request adds no cache entry',
);
});
});
describe('read-only discovery must not consume the degrade warning', () => {
it('a later real request still warns once after discovery', () => {
const name = 'zzscale-discovery-ghost';
const reach = buildConnectReachability({ ...process.env, DEVIN_CONNECT: '1' });
reach(name);
assert.equal(
degradeWarnsFor(name), 0,
'catalog discovery is not a paid request and must not spend the one-time warning',
);
resolveConnectSelector(name);
assert.equal(
degradeWarnsFor(name), 1,
'the first real request after discovery must still warn exactly once',
);
});
});