test/dashboard-account-kpi.test.js
Rama master · réplica de lectura del repositorio original.
// KPI truth (UISpec P0-1).
//
// 3.9.53's KPI strip was six overlapping counters that looked additive:
// - `disabled` was `status !== 'active'`, so banned/reauth/error were
// reported as "停用" (the operator's own switch) AND again as "需关注";
// - `flagged` was a hand-written OR that missed quota-cooled/capacity
// accounts while the "仅异常" filter counted a different set;
// - `balanceTotal` summed the on-demand balance once per ACCOUNT, but Teams
// members read the same team balance — the pool total was multiplied.
//
// These tests pin the partition, the shared predicate, and the dedupe.
import { afterEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
addAccountByKey,
configureBindHost,
getAccountInternal,
getAccountListStats,
markNeedsReauth,
removeAccount,
setAccountStatus,
_resetLockoutForTests,
} from '../src/auth.js';
import { config } from '../src/config.js';
import { handleDashboardApi } from '../src/dashboard/api.js';
import { __resetOrgIdentityForTest, rememberAccountOrg } from '../src/org-identity.js';
import { _resetRuntimeConfigForTests } from '../src/runtime-config.js';
const originalDashboardPassword = config.dashboardPassword;
const originalApiKey = config.apiKey;
const originalAllowNoAuth = process.env.DASHBOARD_ALLOW_NO_AUTH;
const created = new Set();
afterEach(() => {
for (const id of created) removeAccount(id);
created.clear();
__resetOrgIdentityForTest();
_resetRuntimeConfigForTests();
_resetLockoutForTests();
config.dashboardPassword = originalDashboardPassword;
config.apiKey = originalApiKey;
if (originalAllowNoAuth === undefined) delete process.env.DASHBOARD_ALLOW_NO_AUTH;
else process.env.DASHBOARD_ALLOW_NO_AUTH = originalAllowNoAuth;
configureBindHost('0.0.0.0');
});
function enableOpenLocal() {
_resetRuntimeConfigForTests();
config.dashboardPassword = '';
config.apiKey = '';
configureBindHost('127.0.0.1');
process.env.DASHBOARD_ALLOW_NO_AUTH = '1';
}
function add(label, patch = {}) {
const account = addAccountByKey(`kpi-key-${label}-${Date.now()}-${Math.random().toString(16).slice(2)}`, `${label}@fixture.invalid`);
Object.assign(account, patch);
created.add(account.id);
return account;
}
function fakeRes() {
return {
statusCode: 0,
body: '',
writeHead(status) { this.statusCode = status; },
end(chunk) { this.body += chunk ? String(chunk) : ''; },
json() { return this.body ? JSON.parse(this.body) : null; },
};
}
async function get(path) {
const res = fakeRes();
await handleDashboardApi('GET', path.split('?')[0], {}, { url: `/dashboard/api${path}`, headers: {}, socket: { remoteAddress: '127.0.0.1' } }, res);
return { status: res.statusCode, body: res.json() };
}
describe('KPI partition (AC-KPI-1)', () => {
it('reports 已停用 = operator-disabled only, and 需处理 = everything else non-healthy', () => {
enableOpenLocal();
const before = getAccountListStats();
const active = add('kpi-active');
const disabled = add('kpi-disabled');
const banned = add('kpi-banned');
const reauth = add('kpi-reauth');
const errored = add('kpi-error');
setAccountStatus(disabled.id, 'disabled');
setAccountStatus(banned.id, 'banned');
markNeedsReauth(reauth.id, { source: 'request', code: 'UNAUTHORIZED' });
setAccountStatus(errored.id, 'error');
const stats = getAccountListStats();
assert.equal(stats.total - before.total, 5);
assert.equal(stats.stateCounts.active - before.stateCounts.active, 1, 'one healthy account');
assert.equal(stats.stateCounts.disabled - before.stateCounts.disabled, 1, 'only the manual switch counts as 已停用');
assert.equal(stats.attention - before.attention, 3, 'banned + reauth + error are 需处理');
assert.equal(stats.disabled, stats.stateCounts.disabled);
assert.equal(stats.flagged, stats.attention, 'KPI and filter share one predicate');
// The partition is exact: no account is double-counted or dropped.
assert.equal(
stats.stateCounts.active + stats.stateCounts.disabled + stats.attention,
stats.total,
'healthy + switched-off + needs-attention must equal the pool size',
);
for (const account of [active, disabled, banned, reauth, errored]) {
assert.ok(getAccountInternal(account.id), 'fixture account survives the assertion');
}
});
it('makes filter=flagged return exactly the 需处理 set, including quota-cooled accounts', async () => {
enableOpenLocal();
const healthy = add('flag-healthy');
const cooled = add('flag-cooled', { quotaResetAt: Date.now() + 3600_000 });
const limited = add('flag-limited', { rateLimitedUntil: Date.now() + 3600_000 });
const off = add('flag-disabled');
setAccountStatus(off.id, 'disabled');
const res = await get('/accounts?view=summary&filter=flagged');
assert.equal(res.status, 200);
const ids = res.body.accounts.map((a) => a.id);
assert.ok(ids.includes(cooled.id), 'a quota-cooled account needs attention');
assert.ok(ids.includes(limited.id), 'a rate-limited account needs attention');
assert.ok(!ids.includes(healthy.id), 'a healthy account is not flagged');
assert.ok(!ids.includes(off.id), 'a manually disabled account is not "flagged" — it has its own KPI');
assert.equal(res.body.stats.flagged, res.body.total, 'KPI number and filtered rows agree');
});
});
describe('pool balance dedupe (AC-KPI-2)', () => {
it('counts a team-shared balance once and keeps per-account balances for the rest', () => {
const before = getAccountListStats();
const teamA = add('bal-team-a', {
userStatus: { teamId: 'team-alpha' },
credits: { balance: 25, fetchedAt: 1_000 },
});
const teamB = add('bal-team-b', {
userStatus: { teamId: 'team-alpha' },
credits: { balance: 25, fetchedAt: 2_000 },
});
const solo = add('bal-solo', { credits: { balance: 5, fetchedAt: 1_000 } });
const stats = getAccountListStats();
assert.equal(stats.balanceAccounts - before.balanceAccounts, 3, 'three accounts reported a balance');
assert.equal(stats.balanceOwners - before.balanceOwners, 2, 'two billing owners: one team + one individual');
assert.equal(stats.balanceSharedAccounts - before.balanceSharedAccounts, 1);
assert.equal(stats.balanceTotal - before.balanceTotal, 30, '25 for the team (once) + 5 for the individual');
assert.ok(getAccountInternal(teamA.id) && getAccountInternal(teamB.id) && getAccountInternal(solo.id));
});
it('prefers the freshest snapshot inside one team', () => {
const before = getAccountListStats();
add('bal-fresh', {
userStatus: { teamId: 'team-fresh' },
credits: { balance: 10, fetchedAt: 9_000 },
});
add('bal-stale', {
userStatus: { teamId: 'team-fresh' },
credits: { balance: 99, fetchedAt: 1_000 },
});
const stats = getAccountListStats();
assert.equal(stats.balanceTotal - before.balanceTotal, 10, 'the newer read wins; the stale sibling cannot inflate it');
});
it('dedupes by the org the ACU reader resolved, even without a team id', () => {
const before = getAccountListStats();
const one = add('bal-org-1', { credits: { balance: 7, fetchedAt: 1_000 } });
const two = add('bal-org-2', { credits: { balance: 7, fetchedAt: 2_000 } });
rememberAccountOrg(one.id, 'org-abc');
rememberAccountOrg(two.id, 'org-abc');
const stats = getAccountListStats();
assert.equal(stats.balanceTotal - before.balanceTotal, 7, 'one org, one balance');
assert.equal(stats.balanceOwners - before.balanceOwners, 1);
});
it('never counts a sentinel or unknown balance', () => {
const before = getAccountListStats();
add('bal-sentinel', { credits: { balance: 18_446_744_073_709.43, fetchedAt: 1_000 } });
add('bal-negative', { credits: { balance: -1, fetchedAt: 1_000 } });
add('bal-null', { credits: { balance: null, fetchedAt: 1_000 } });
const stats = getAccountListStats();
assert.equal(stats.balanceAccounts, before.balanceAccounts);
assert.equal(stats.balanceTotal, before.balanceTotal);
});
});