test/cache-replay-usage-accounting.test.js
Rama master · réplica de lectura del repositorio original.
// D4 / V-27 — a cache-served Connect turn must not be booked as upstream
// consumption.
//
// MEASURED on the audited baseline (evidence/audit/FIX-QUEUE.md row 27, plus the
// source-confirmed replay calls at chat.js:4519-4522 and :4913-4916): both
// Connect cache-replay arms called recordTokenUsage() with the cached usage and
// recordRequest() as a successful turn. No upstream request ran, yet every hit
// re-added the whole prompt+completion token block to the dashboard ledger and
// charged the model's credit cost again. A client that retried the same request
// N times reported N upstream turns.
//
// The contract pinned here:
// * a cache HIT adds ZERO upstream usage and ZERO credits, and leaves the
// per-account spend untouched — the client still receives the cached usage
// block, which is its answer's metadata, not a second upstream turn;
// * the hit is still counted as served traffic, and its row says
// `cacheServed: true` so the two cannot be confused;
// * the forced-miss control (same body, different caller scope) bills exactly
// one upstream turn.
//
// Both Connect arms are covered: stream and non-stream share one cache slot.
import { afterEach, beforeEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
handleChatCompletions,
__resetConnectDeps,
__setConnectDeps,
} from '../src/handlers/chat.js';
import { addAccountByKey, getAccountInternal, removeAccount } from '../src/auth.js';
import { cacheClear, cacheStats } from '../src/cache.js';
import { getStats, recordRequest, resetStats } from '../src/dashboard/stats.js';
const CALLER = 'api:d4:user:u1';
const OTHER_CALLER = 'api:d4:user:u2';
// A mapped connect selector that carries a non-zero credit card (4), so a
// duplicated charge is visible in the ledger instead of hiding in a free tier.
const MODEL = 'claude-sonnet-4-6';
const USAGE = { prompt_tokens: 5, completion_tokens: 3, total_tokens: 8 };
const created = [];
let savedDevinConnect = null;
function seedAccount(label) {
const a = addAccountByKey(`v27-${label}-${Math.random().toString(36).slice(2)}`, label);
created.push(a.id);
const acct = getAccountInternal(a.id);
if (acct) {
acct.tier = 'pro';
acct.status = 'active';
}
return a;
}
function body(extra = {}) {
return {
model: MODEL,
stream: false,
messages: [{ role: 'user', content: 'what is the capital of France?' }],
...extra,
};
}
function completionDeps(text) {
let calls = 0;
const deps = {
toChatCompletion: async () => {
calls++;
return {
status: 200,
body: {
id: 'x',
object: 'chat.completion',
created: 1,
model: MODEL,
choices: [{ index: 0, message: { role: 'assistant', content: text }, finish_reason: 'stop' }],
usage: { ...USAGE },
},
};
},
streamChatCompletion: async (_params, send, meta) => {
calls++;
const base = { id: meta.id, object: 'chat.completion.chunk', created: meta.created, model: meta.displayModel };
await send({ ...base, choices: [{ index: 0, delta: { role: 'assistant', content: '' }, finish_reason: null }] });
await send({ ...base, choices: [{ index: 0, delta: { content: text }, finish_reason: null }] });
await send({ ...base, choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] });
return { content: text, delivered: text, reasoning: '', finish_reason: 'stop', usage: { ...USAGE }, toolCalls: [], stop: null };
},
};
return { deps, upstreamCalls: () => calls };
}
function fakeRes() {
const listeners = new Map();
return {
body: '',
writableEnded: false,
write(chunk) { this.body += String(chunk); return true; },
end(chunk) {
if (chunk) this.write(chunk);
this.writableEnded = true;
for (const cb of listeners.get('close') || []) cb();
},
on(event, cb) { if (!listeners.has(event)) listeners.set(event, []); listeners.get(event).push(cb); return this; },
once(event, cb) { this.on(event, cb); return this; },
removeListener() {},
writeHead() { return this; },
};
}
/** Upstream-consumption ledger: what a completed upstream turn costs. */
function upstreamLedger() {
const s = getStats();
return {
tokens: s.tokenTotals.total,
fresh: s.tokenTotals.fresh_input,
output: s.tokenTotals.output,
usageRequests: s.tokenTotals.requests_with_usage,
credits: s.creditsTotal,
requests: s.totalRequests,
};
}
function accountSpend(id) {
return getAccountInternal(id)?._totalSpend?.requests || 0;
}
beforeEach(() => {
savedDevinConnect = process.env.DEVIN_CONNECT;
process.env.DEVIN_CONNECT = '1';
cacheClear();
resetStats();
});
afterEach(() => {
__resetConnectDeps();
if (savedDevinConnect === undefined) delete process.env.DEVIN_CONNECT;
else process.env.DEVIN_CONNECT = savedDevinConnect;
cacheClear();
resetStats();
while (created.length) {
try { removeAccount(created.pop()); } catch {}
}
});
describe('D4: a Connect cache hit is client traffic, not upstream consumption', () => {
it('non-stream: the hit adds no tokens, no credits and no account spend; a scoped miss adds exactly one turn', async () => {
const acct = seedAccount('nonstream');
const { deps, upstreamCalls } = completionDeps('Paris.');
__setConnectDeps(deps);
const miss = await handleChatCompletions(body(), { callerKey: CALLER });
assert.equal(miss.status, 200);
const afterMiss = upstreamLedger();
assert.equal(afterMiss.tokens, USAGE.total_tokens, 'the real turn is billed once');
assert.equal(afterMiss.credits, 4, 'the real turn charges the model credit card');
assert.equal(accountSpend(acct.id), 1, 'the account really served one upstream turn');
assert.equal(upstreamCalls(), 1);
const hit = await handleChatCompletions(body(), { callerKey: CALLER });
assert.equal(hit.status, 200);
assert.equal(hit.body.choices[0].message.content, 'Paris.');
assert.deepEqual(hit.body.usage.total_tokens, USAGE.total_tokens,
'the client still receives its answer usage block');
const afterHit = upstreamLedger();
assert.equal(upstreamCalls(), 1, 'precondition: the second request was served from cache');
assert.equal(cacheStats().hits, 1);
assert.equal(afterHit.tokens, afterMiss.tokens, 'a cache hit consumes no upstream tokens');
assert.equal(afterHit.usageRequests, afterMiss.usageRequests, 'a cache hit is not a usage-bearing upstream turn');
assert.equal(afterHit.credits, afterMiss.credits, 'a cache hit charges no credits');
assert.equal(accountSpend(acct.id), 1, 'a cache hit must not touch the per-account spend ledger');
assert.equal(afterHit.requests, afterMiss.requests + 1, 'the hit is still counted as served traffic');
const row = getStats().recentRequests.at(-1);
assert.equal(row.cacheServed, true, 'the row says it was cache-served');
assert.equal(row.credit, 0, 'the row charges nothing');
assert.equal(row.tokensIn, 0, 'the row claims no upstream input tokens');
// Forced-miss control: same body, different caller scope → no shared slot.
const scoped = await handleChatCompletions(body(), { callerKey: OTHER_CALLER });
assert.equal(scoped.status, 200);
const afterScoped = upstreamLedger();
assert.equal(upstreamCalls(), 2, 'a different caller scope is a real upstream turn');
assert.equal(afterScoped.tokens, afterMiss.tokens + USAGE.total_tokens, 'exactly one turn of usage');
assert.equal(afterScoped.credits, afterMiss.credits + 4, 'exactly one turn of credits');
assert.equal(accountSpend(acct.id), 2, 'exactly one more upstream turn on the account');
});
it('stream: the replay adds no tokens, no credits and no account spend', async () => {
const acct = seedAccount('stream');
const { deps, upstreamCalls } = completionDeps('Paris.');
__setConnectDeps(deps);
const first = await handleChatCompletions(body({ stream: true }), { callerKey: CALLER });
assert.equal(first.stream, true);
const res = fakeRes();
await first.handler(res);
assert.ok(res.body.includes('data: [DONE]'));
const afterMiss = upstreamLedger();
assert.equal(afterMiss.tokens, USAGE.total_tokens);
assert.equal(afterMiss.credits, 4);
assert.equal(accountSpend(acct.id), 1);
assert.equal(upstreamCalls(), 1);
const second = await handleChatCompletions(body({ stream: true }), { callerKey: CALLER });
const res2 = fakeRes();
await second.handler(res2);
assert.ok(res2.body.includes('Paris.'), 'the cached answer is replayed');
const afterHit = upstreamLedger();
assert.equal(upstreamCalls(), 1, 'precondition: the replay did not call upstream');
assert.equal(cacheStats().hits, 1);
assert.equal(afterHit.tokens, afterMiss.tokens, 'a cache hit consumes no upstream tokens');
assert.equal(afterHit.usageRequests, afterMiss.usageRequests);
assert.equal(afterHit.credits, afterMiss.credits, 'a cache hit charges no credits');
assert.equal(accountSpend(acct.id), 1, 'the replay must not touch the per-account spend ledger');
assert.equal(afterHit.requests, afterMiss.requests + 1);
assert.equal(getStats().recentRequests.at(-1).cacheServed, true);
});
});
describe('D4: recordRequest separates a cache-served turn from an upstream one', () => {
it('charges the credit card only for a turn that ran upstream', () => {
resetStats();
recordRequest(MODEL, true, 0, null);
assert.equal(getStats().creditsTotal, 4, 'a normal successful turn is charged');
recordRequest(MODEL, true, 0, null, { cacheServed: true });
assert.equal(getStats().creditsTotal, 4, 'a cache-served turn adds nothing');
assert.equal(getStats().totalRequests, 2, 'but it is still counted as served traffic');
assert.equal(getStats().recentRequests.at(-1).cacheServed, true);
assert.equal(getStats().recentRequests.at(-1).credit, 0);
});
});