test/dashboard-account-requests.test.js
Rama master · réplica de lectura del repositorio original.
// Per-account request history: the ring in auth.js and its dashboard route.
//
// The feature exists because neither existing request log can attribute a call
// to an account: stats.recentRequests stores the upstream KEY at most chat call
// sites, and request-ring rows are keyed by a caller hash. These tests pin the
// ring's honesty rules — newest-first, bounded, per-account, billed-only — and
// the route contract (auth gate inherited, 404 unknown, no key material).
import { afterEach, beforeEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { config } from '../src/config.js';
import {
ACCOUNT_REQUEST_CAP,
__resetAccountRequestsForTest,
addAccountByKey,
configureBindHost,
forgetAccountRequests,
getAccountRequests,
recordAccountAttempt,
recordAccountRequest,
recordAccountSpend,
removeAccount,
_resetLockoutForTests,
} from '../src/auth.js';
import { finalizeConnectAccount } from '../src/handlers/chat.js';
import { handleDashboardApi } from '../src/dashboard/api.js';
import { _resetRuntimeConfigForTests } from '../src/runtime-config.js';
const originalDashboardPassword = config.dashboardPassword;
const originalApiKey = config.apiKey;
const created = new Set();
function fakeRes() {
return {
statusCode: 0,
headers: {},
body: '',
writeHead(status, headers) { this.statusCode = status; this.headers = headers || {}; },
end(chunk) { this.body += chunk ? String(chunk) : ''; },
json() { return this.body ? JSON.parse(this.body) : null; },
};
}
function req(path, headers = {}) {
return { url: `/dashboard/api${path}`, headers, socket: { remoteAddress: '127.0.0.1' } };
}
function authed(path, extra = {}) {
return req(path, { 'x-dashboard-password': 'dash-secret', ...extra });
}
function addAccount(label) {
const account = addAccountByKey(`req-key-${label}-${Date.now()}-${Math.random().toString(16).slice(2)}`, label);
created.add(account.id);
return account;
}
beforeEach(() => {
__resetAccountRequestsForTest();
});
afterEach(() => {
for (const id of created) removeAccount(id);
created.clear();
__resetAccountRequestsForTest();
_resetRuntimeConfigForTests();
_resetLockoutForTests();
configureBindHost('0.0.0.0');
config.dashboardPassword = originalDashboardPassword;
config.apiKey = originalApiKey;
});
describe('account request ring', () => {
it('returns newest first and keeps the whole-window totals', () => {
const acct = { id: 'ring-acct-1' };
recordAccountRequest(acct, { model: 'a', usage: { prompt_tokens: 1, completion_tokens: 2 }, creditCost: 0.5, acuCost: 0.1, now: 100 });
recordAccountRequest(acct, { model: 'b', usage: { prompt_tokens: 3, completion_tokens: 4 }, creditCost: 1, acuCost: 0.2, now: 200 });
const page = getAccountRequests('ring-acct-1', { limit: 1 });
assert.deepEqual(page.items.map((r) => r.model), ['b']);
assert.equal(page.total, 2, 'total is the ring size, not the page size');
assert.equal(page.billed, 2);
assert.equal(page.failed, 0);
assert.equal(page.items.length, 1);
assert.equal(page.items[0].billed, true, 'recordAccountRequest is billed by default');
assert.deepEqual(page.summary, {
requests: 2,
promptTokens: 4,
completionTokens: 6,
totalTokens: 10,
credit: 1.5,
acu: 0.30000000000000004,
lastTs: 200,
});
});
it('is bounded and drops the oldest row past the cap', () => {
for (let i = 0; i < ACCOUNT_REQUEST_CAP + 3; i++) {
recordAccountRequest({ id: 'ring-cap' }, { model: `m${i}`, now: i });
}
const page = getAccountRequests('ring-cap', { limit: ACCOUNT_REQUEST_CAP });
assert.equal(page.total, ACCOUNT_REQUEST_CAP);
assert.equal(page.items[0].model, `m${ACCOUNT_REQUEST_CAP + 2}`, 'newest row survives');
assert.equal(page.items.at(-1).model, 'm3', 'the three oldest rows were evicted');
});
// P2 (wave-7 review): failed attempts and billed rows used to compete for the
// same 100 slots, so a failure burst could evict every billed row and the
// summary — recomputed from survivors — read as "this account served
// nothing". Billed rows are the money truth: they are never evicted by a
// failure.
it('a failure burst cannot evict the billed window or zero its summary', () => {
const acct = { id: 'ring-billed-reserve' };
recordAccountRequest(acct, {
model: 'claude-sonnet-4',
usage: { prompt_tokens: 5, completion_tokens: 3 },
creditCost: 2,
acuCost: 1,
now: 1,
});
const before = getAccountRequests(acct.id).summary;
for (let i = 0; i < ACCOUNT_REQUEST_CAP; i++) {
recordAccountAttempt(acct, { model: 'claude-sonnet-4', outcome: 'rate_limited', status: 429, now: 10 + i });
}
const page = getAccountRequests(acct.id, { limit: ACCOUNT_REQUEST_CAP });
assert.equal(page.billed, 1, 'the billed row survives a full failure burst');
assert.equal(page.failed, ACCOUNT_REQUEST_CAP - 1, 'failures share the window but stay bounded');
assert.equal(page.total, ACCOUNT_REQUEST_CAP);
assert.deepEqual(page.summary, before, 'the billed summary is byte-for-byte unchanged');
assert.equal(page.items.filter((r) => r.billed === true).length, 1);
});
it('drops an incoming failure rather than evicting a billed row from a full billed window', () => {
const acct = { id: 'ring-billed-full' };
for (let i = 0; i < ACCOUNT_REQUEST_CAP; i++) {
recordAccountRequest(acct, { model: `m${i}`, usage: { prompt_tokens: 1 }, now: i });
}
recordAccountAttempt(acct, { model: 'm0', outcome: 'rate_limited', status: 429, now: 1000 });
const page = getAccountRequests(acct.id, { limit: ACCOUNT_REQUEST_CAP });
assert.equal(page.billed, ACCOUNT_REQUEST_CAP);
assert.equal(page.failed, 0, 'the failure had no slot it could take without evicting a billed row');
assert.equal(page.total, ACCOUNT_REQUEST_CAP);
assert.equal(page.items[0].model, `m${ACCOUNT_REQUEST_CAP - 1}`, 'the newest billed row is still newest');
assert.equal(page.items.at(-1).model, 'm0', 'no billed row was evicted');
assert.equal(page.summary.requests, ACCOUNT_REQUEST_CAP);
});
it('a newer billed row evicts the oldest failure before any billed row', () => {
const acct = { id: 'ring-failure-first' };
for (let i = 0; i < ACCOUNT_REQUEST_CAP - 1; i++) {
recordAccountRequest(acct, { model: `m${i}`, now: i });
}
recordAccountAttempt(acct, { model: 'fail', outcome: 'upstream_error', status: 502, now: 500 });
recordAccountRequest(acct, { model: 'newest', now: 600 });
const page = getAccountRequests(acct.id, { limit: ACCOUNT_REQUEST_CAP });
assert.equal(page.billed, ACCOUNT_REQUEST_CAP);
assert.equal(page.failed, 0);
assert.equal(page.items[0].model, 'newest');
assert.ok(!page.items.some((r) => r.model === 'fail'), 'the failure was the row evicted');
assert.equal(page.items.at(-1).model, 'm0', 'the oldest billed row was not displaced');
});
it('keeps accounts isolated and forgets a removed account', () => {
recordAccountRequest({ id: 'iso-a' }, { model: 'a' });
recordAccountRequest({ id: 'iso-b' }, { model: 'b' });
assert.deepEqual(getAccountRequests('iso-a').items.map((r) => r.model), ['a']);
assert.deepEqual(getAccountRequests('iso-b').items.map((r) => r.model), ['b']);
forgetAccountRequests('iso-a');
assert.equal(getAccountRequests('iso-a').total, 0);
assert.equal(getAccountRequests('iso-b').total, 1);
});
it('clamps hostile usage instead of storing negatives', () => {
recordAccountRequest({ id: 'clamp' }, {
model: 42,
usage: { prompt_tokens: -5, completion_tokens: 'x', total_tokens: -1 },
creditCost: -3,
acuCost: -1,
});
const row = getAccountRequests('clamp').items[0];
assert.equal(row.model, '', 'a non-string model is not coerced into copy');
assert.equal(row.promptTokens, 0);
assert.equal(row.completionTokens, 0);
assert.equal(row.totalTokens, 0);
assert.equal(row.credit, 0);
assert.equal(row.acu, 0);
});
it('only billed rows enter the billed summary; failures are counted separately', () => {
const acct = addAccount('spend-attribution');
recordAccountSpend(acct.apiKey, { prompt_tokens: 10, completion_tokens: 5 }, { creditCost: 2, acuCost: 0.25, model: 'claude-sonnet-4' });
const before = getAccountRequests(acct.id);
recordAccountAttempt({ id: acct.id }, { model: 'claude-sonnet-4', outcome: 'rate_limited', status: 429, attemptId: 'attempt-7' });
// A request against a key that belongs to no account must not create a row,
// and a failure with no account id belongs to nobody.
recordAccountSpend('not-an-account-key', { prompt_tokens: 1 }, { model: 'x' });
recordAccountAttempt(null, { model: 'x', outcome: 'upstream_error', status: 502 });
const page = getAccountRequests(acct.id);
assert.equal(page.total, 2, 'the failed attempt occupies a retained slot');
assert.equal(page.billed, 1);
assert.equal(page.failed, 1);
// The billed summary is byte-for-byte what the success alone produced: the
// failed row must not move a single counter or lastTs.
assert.deepEqual(page.summary, before.summary, 'a failed row must not move the billed summary');
assert.deepEqual(page.summary, {
requests: 1,
promptTokens: 10,
completionTokens: 5,
totalTokens: 15,
credit: 2,
acu: 0.25,
lastTs: before.summary.lastTs,
});
const [failedRow, billedRow] = page.items;
assert.equal(billedRow.model, 'claude-sonnet-4');
assert.equal(billedRow.billed, true);
assert.equal(failedRow.billed, false);
assert.equal(failedRow.outcome, 'rate_limited');
assert.equal(failedRow.status, 429);
assert.equal(failedRow.attemptId, 'attempt-7');
assert.equal(failedRow.totalTokens, 0);
assert.equal(failedRow.promptTokens, 0);
assert.equal(failedRow.completionTokens, 0);
assert.equal(failedRow.credit, 0);
assert.equal(failedRow.acu, 0);
});
it('refuses an unattributed failure instead of inventing an owner', () => {
assert.equal(recordAccountAttempt(null, { model: 'x' }), null);
assert.equal(recordAccountAttempt({}, { model: 'x' }), null);
assert.equal(recordAccountAttempt({ id: '' }, { model: 'x' }), null);
assert.equal(getAccountRequests('x').total, 0);
});
it('records CONNECT failure exits against the account that was attempted', () => {
const acct = addAccount('connect-failures');
acct.reservationTimestamp = 4242;
const base = { model: 'swe-2', startTime: Date.now() };
finalizeConnectAccount(acct, { ...base, err: Object.assign(new Error('rate limited'), { code: 'RATE_LIMITED' }) });
finalizeConnectAccount(acct, { ...base, err: Object.assign(new Error('socket reset'), { code: 'ECONNRESET' }) });
finalizeConnectAccount(acct, { ...base, err: Object.assign(new Error('client gone'), { name: 'AbortError' }), aborted: true });
const page = getAccountRequests(acct.id);
assert.equal(page.billed, 0);
assert.equal(page.failed, 3);
assert.deepEqual(page.items.map((r) => [r.outcome, r.status]), [
['client_abort', 499],
['connection_error', 502],
['rate_limited', 429],
]);
assert.equal(page.items[0].attemptId, '4242');
assert.equal(page.items.every((r) => r.billed === false && r.credit === 0 && r.totalTokens === 0), true);
assert.equal(page.summary.requests, 0);
assert.equal(page.summary.totalTokens, 0);
assert.equal(page.summary.credit, 0);
});
it('drops the ring when the account is removed', () => {
const acct = addAccount('ring-removal');
recordAccountSpend(acct.apiKey, { prompt_tokens: 1 }, { model: 'm' });
assert.equal(getAccountRequests(acct.id).total, 1);
removeAccount(acct.id);
created.delete(acct.id);
assert.equal(getAccountRequests(acct.id).total, 0);
});
});
describe('GET /accounts/:id/requests', () => {
it('fails closed without dashboard auth', async () => {
_resetRuntimeConfigForTests();
config.dashboardPassword = 'dash-secret';
config.apiKey = '';
configureBindHost('0.0.0.0');
const res = fakeRes();
await handleDashboardApi('GET', '/accounts/any/requests', {}, req('/accounts/any/requests'), res);
assert.equal(res.statusCode, 401);
});
it('404s an unknown account', async () => {
_resetRuntimeConfigForTests();
config.dashboardPassword = 'dash-secret';
config.apiKey = '';
configureBindHost('0.0.0.0');
const res = fakeRes();
await handleDashboardApi('GET', '/accounts/missing/requests', {}, authed('/accounts/missing/requests'), res);
assert.equal(res.statusCode, 404);
});
it('returns rows, totals and a hashed account ref — never the key', async () => {
_resetRuntimeConfigForTests();
config.dashboardPassword = 'dash-secret';
config.apiKey = '';
configureBindHost('0.0.0.0');
const acct = addAccount('route-history');
recordAccountSpend(acct.apiKey, { prompt_tokens: 4, completion_tokens: 6 }, { creditCost: 1, model: 'gemini-2.5-flash' });
recordAccountAttempt({ id: acct.id }, { model: 'gemini-2.5-flash', outcome: 'rate_limited', status: 429 });
const res = fakeRes();
await handleDashboardApi('GET', `/accounts/${acct.id}/requests`, {}, authed(`/accounts/${acct.id}/requests?limit=10`), res);
assert.equal(res.statusCode, 200);
const body = res.json();
assert.equal(body.ok, true);
assert.equal(body.total, 2, 'the window counts billed rows and failed attempts');
assert.equal(body.billed, 1);
assert.equal(body.failed, 1);
assert.equal(body.capacity, ACCOUNT_REQUEST_CAP);
assert.equal(body.items[0].billed, false, 'the failed attempt is the newest row');
assert.equal(body.items[1].model, 'gemini-2.5-flash');
assert.equal(body.items[1].totalTokens, 10);
assert.equal(body.summary.requests, 1, 'the billed summary ignores the failed row');
assert.equal(body.summary.totalTokens, 10);
assert.equal(typeof body.accountRef, 'string');
assert.ok(body.accountRef.length <= 16, 'the ref is a short hash, not an identifier');
assert.equal(res.body.includes(acct.apiKey), false, 'the upstream key must never reach the response');
});
it('clamps a hostile limit instead of honouring it', async () => {
_resetRuntimeConfigForTests();
config.dashboardPassword = 'dash-secret';
config.apiKey = '';
configureBindHost('0.0.0.0');
const acct = addAccount('route-limit');
for (let i = 0; i < 5; i++) recordAccountSpend(acct.apiKey, { prompt_tokens: 1 }, { model: `m${i}` });
const res = fakeRes();
await handleDashboardApi('GET', `/accounts/${acct.id}/requests`, {}, authed(`/accounts/${acct.id}/requests?limit=9999`), res);
assert.equal(res.statusCode, 200);
assert.ok(res.json().items.length <= ACCOUNT_REQUEST_CAP);
assert.equal(res.json().total, 5);
});
});