前端(Vite + React)、API/worker(Fastify + BullMQ)、api-contracts 契约包、 迁移脚本、测试与自托管 docker-compose 示例。 开源准备:运维脚本与机器信息已拆到私有仓库;公司名、备案号、隐私邮箱改读 publicCompliance.local.json(仓库只保留占位模板);站点地址与客服二维码改为 VITE_ 环境变量;fixtures 中的真实用户标识已匿名化;三个 package.json 标注 Apache-2.0。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1819 lines
92 KiB
TypeScript
1819 lines
92 KiB
TypeScript
import { endpointContracts } from '@lindyson/api-contracts';
|
||
import { describe, expect, it, vi } from 'vitest';
|
||
import type { DbClient } from '../src/db/pool.js';
|
||
import {
|
||
getAdminApiCostReconciliationDetail,
|
||
getAdminApiCostDashboard,
|
||
getAdminApiCostOverview,
|
||
listAdminApiCostAttempts,
|
||
listAdminApiCostTasks,
|
||
listAdminApiCostUsers,
|
||
listAdminApiCostReconciliation,
|
||
listAdminApiCostSettlement,
|
||
resolveBusinessReconciliationStatus,
|
||
} from '../src/domain/apiCostReporting.js';
|
||
|
||
const taskId = '00000000-0000-4000-8000-000000000001';
|
||
const usageId = '00000000-0000-4000-8000-000000000002';
|
||
const userId = '00000000-0000-4000-8000-000000000003';
|
||
const zeroCostCaseId = '00000000-0000-4000-8000-000000000009';
|
||
|
||
describe('API 人民币成本报告', () => {
|
||
it('主看盘月份范围只执行四组经营查询,不加载覆盖、审计或资源包数据', async () => {
|
||
const now = new Date().toISOString();
|
||
const resolveQuery = async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('WITH latest_balance AS')) return rows([{
|
||
balance: {
|
||
provider: 'volcengine-ark', available_balance_cny: '0', cash_balance_cny: '0',
|
||
frozen_amount_cny: '0', arrears_balance_cny: '0', credit_limit_cny: '0',
|
||
currency: 'CNY', synced_at: now,
|
||
},
|
||
sync_runs: [syncRun('balance', now), syncRun('bill', now), syncRun('packages', now)],
|
||
}]);
|
||
if (text.includes('GROUP BY GROUPING SETS')) return rows([{
|
||
model: 'Seedance 2.0', total_row: false, task_count: 3, user_count: 1,
|
||
price_versions: [
|
||
'volcengine-seedance-2026-07-10',
|
||
'volcengine-seedance-2026-06-01',
|
||
'volcengine-seedance-2026-07-10',
|
||
],
|
||
settled_credits: '1433', held_credits: '0', refunded_credits: '472',
|
||
estimated_cost_cny: '405.424506', incomplete_task_count: 3,
|
||
pending_final_bill_task_count: 3, approved_zero_cost_task_count: 0,
|
||
unresolved_task_count: 0, admin_direct_usage_count: 3,
|
||
admin_direct_estimated_cost_cny: '37.488825',
|
||
timing_shift_cost_cny: '0',
|
||
}, {
|
||
model: null, total_row: true, task_count: 3, user_count: 1,
|
||
price_versions: [],
|
||
settled_credits: '1433', held_credits: '0', refunded_credits: '472',
|
||
estimated_cost_cny: '405.424506', incomplete_task_count: 3,
|
||
pending_final_bill_task_count: 3, approved_zero_cost_task_count: 0,
|
||
unresolved_task_count: 0, admin_direct_usage_count: 3,
|
||
admin_direct_estimated_cost_cny: '37.488825',
|
||
timing_shift_cost_cny: '0',
|
||
}]);
|
||
if (text.includes('FROM operational_sources') && text.includes('GROUP BY model')) return rows([{
|
||
model: 'Seedance 2.0', call_count: 3, estimated_cost_cny: '37.488825',
|
||
unpriced_call_count: 0, price_versions: ['volcengine-seedance-2026-07-10'],
|
||
}]);
|
||
if (text.includes('WITH selected_dates AS')) return rows([{
|
||
...officialRevision({
|
||
sourceDetailKey: 'usage-overview', billLineType: 'usage', recognizedModel: 'Seedance 2.0',
|
||
billableTokens: '9552570', deductionCount: '9552570', listEquivalentCostCny: '442.913331',
|
||
originalBillAmountCny: '0', payableAmountCny: '0', paidAmountCny: '0', unpaidAmountCny: '0',
|
||
expenseDate: '2026-06-30',
|
||
}),
|
||
coverage_complete: true,
|
||
}]);
|
||
throw new Error(`轻量主看盘执行了未预期 SQL:${text}`);
|
||
};
|
||
const queryProbe = serialQueryProbe(resolveQuery);
|
||
const query = queryProbe.query;
|
||
|
||
const overview = await getAdminApiCostOverview({ query } as unknown as DbClient, {
|
||
range: 'month', month: '2026-06', asOf: now,
|
||
});
|
||
|
||
expect(() => endpointContracts.adminApiUsageOverview.response.parse(overview)).not.toThrow();
|
||
expect(overview.operations).toMatchObject({
|
||
taskCount: 3,
|
||
userCount: 1,
|
||
settledCredits: 1433,
|
||
refundedCredits: 472,
|
||
estimatedCostCny: '442.913331',
|
||
businessEstimatedCostCny: '405.424506',
|
||
operationalCallCount: 3,
|
||
operationalEstimatedCostCny: '37.488825',
|
||
operationalUnpricedCallCount: 0,
|
||
adminDirectUsageCount: 3,
|
||
adminDirectEstimatedCostCny: '37.488825',
|
||
knownAccountCostCny: '442.913331',
|
||
unresolvedTaskCount: 0,
|
||
pendingFinalBillTaskCount: 3,
|
||
approvedZeroCostTaskCount: 0,
|
||
officialEquivalentCostCny: '442.913331',
|
||
differenceCny: '0.000000',
|
||
officialPayableCny: '0.000000',
|
||
reconciliationStatus: 'incomplete',
|
||
});
|
||
expect(overview.models.map(model => model.model)).toEqual([
|
||
'Seedance 2.5', 'Seedance 2.0', 'Seedance 2.0 Fast', 'Seedance 2.0 Mini',
|
||
]);
|
||
expect(overview.models[1]).toMatchObject({
|
||
model: 'Seedance 2.0', taskCount: 3, operationalCallCount: 3,
|
||
estimatedCostCny: '442.913331', businessEstimatedCostCny: '405.424506',
|
||
operationalEstimatedCostCny: '37.488825', knownAccountCostCny: '442.913331',
|
||
operationalUnpricedCallCount: 0, averageTaskCostCny: '135.141502',
|
||
priceVersions: ['volcengine-seedance-2026-06-01', 'volcengine-seedance-2026-07-10'],
|
||
});
|
||
expect(overview.models.filter(model => model.model !== 'Seedance 2.0')).toEqual([
|
||
expect.objectContaining({
|
||
model: 'Seedance 2.5', taskCount: 0, operationalCallCount: 0, averageTaskCostCny: null,
|
||
}),
|
||
expect.objectContaining({
|
||
model: 'Seedance 2.0 Fast', taskCount: 0, operationalCallCount: 0, averageTaskCostCny: null,
|
||
}),
|
||
expect.objectContaining({
|
||
model: 'Seedance 2.0 Mini', taskCount: 0, operationalCallCount: 0, averageTaskCostCny: null,
|
||
}),
|
||
]);
|
||
expect(query).toHaveBeenCalledTimes(4);
|
||
expect(queryProbe.peakActive()).toBe(1);
|
||
const overviewQueries = query.mock.calls.map(call => String(call[0]));
|
||
const accountSyncSql = overviewQueries.find(sql => sql.includes('WITH latest_balance AS'))!;
|
||
const operationsSql = overviewQueries.find(sql => sql.includes('GROUP BY GROUPING SETS'))!;
|
||
const operationalSql = overviewQueries.find(sql => (
|
||
sql.includes('FROM operational_sources') && sql.includes('GROUP BY model')
|
||
))!;
|
||
const officialSnapshotSql = overviewQueries.find(sql => sql.includes('WITH selected_dates AS'))!;
|
||
expect(accountSyncSql).toContain(
|
||
"CASE WHEN completed_at > $2::timestamptz THEN 'running' ELSE status END",
|
||
);
|
||
expect(accountSyncSql).toMatch(
|
||
/max\(completed_at\) FILTER \(\s+WHERE status = 'success' AND completed_at <= \$2::timestamptz/,
|
||
);
|
||
expect(accountSyncSql).toMatch(
|
||
/max\(window_end\) FILTER \(\s+WHERE status = 'success' AND completed_at <= \$2::timestamptz/,
|
||
);
|
||
expect(overviewQueries.join('\n')).not.toMatch(
|
||
/AS local_start|provider_resource_packages|provider_pricing_rules|provider_reconciliation_buckets/,
|
||
);
|
||
expect(operationsSql).toContain("cases.evidence->>'resolutionKind' = 'zero-cost'");
|
||
expect(operationsSql).toContain('SELECT DISTINCT case_id, usage_record_id');
|
||
expect(operationsSql).toContain("cases.status IN ('open', 'approved')");
|
||
expect(operationsSql).toContain('attempt_count - known_usage_attempts - approved_zero_attempts');
|
||
expect(operationsSql).toContain('admin_direct_usage AS');
|
||
expect(operationsSql).toContain('model_price_versions AS');
|
||
expect(operationsSql).toContain('JOIN provider_usage_records version_usage');
|
||
expect(operationsSql).not.toContain('unnest(task.price_versions)');
|
||
expect(operationsSql.match(/FROM business_task_costs task/g)).toHaveLength(1);
|
||
expect(operationsSql).toContain('pur.initiated_by_admin_user_id IS NOT NULL');
|
||
expect(operationalSql).toContain('FROM provider_operational_usage_records usage');
|
||
expect(operationalSql).toContain('usage.task_id IS NULL');
|
||
expect(operationalSql).toContain('UNION ALL');
|
||
expect(officialSnapshotSql).toContain('selected_dates AS MATERIALIZED');
|
||
expect(officialSnapshotSql).toContain('valid_bill_runs AS MATERIALIZED');
|
||
expect(officialSnapshotSql).toContain('selected_run_candidates AS');
|
||
expect(officialSnapshotSql).toContain('PARTITION BY dates.day');
|
||
expect(officialSnapshotSql).toContain('runs.window_start <= $2::date');
|
||
expect(officialSnapshotSql).toContain('runs.window_end >= $1::date');
|
||
expect(officialSnapshotSql).toContain('EXISTS (\n SELECT 1 FROM provider_bill_sync_run_items evidence');
|
||
expect(officialSnapshotSql).not.toContain('evidenced_bill_runs');
|
||
});
|
||
|
||
it.each([
|
||
{ name: '官方账单日期覆盖不完整', coverageComplete: false, billAgeMs: 0, billStatus: 'success' },
|
||
{ name: '官方账单同步超过 30 分钟', coverageComplete: true, billAgeMs: 31 * 60_000, billStatus: 'success' },
|
||
{ name: '最新账单同步在截点后才完成', coverageComplete: true, billAgeMs: 0, billStatus: 'running' },
|
||
])('$name 时不把已知官方行伪装成完整对账', async ({ coverageComplete, billAgeMs, billStatus }) => {
|
||
const asOf = new Date().toISOString();
|
||
const billLastSuccessAt = new Date(new Date(asOf).getTime() - billAgeMs).toISOString();
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('WITH latest_balance AS')) {
|
||
const bill = { ...syncRun('bill', asOf), status: billStatus, last_success_at: billLastSuccessAt };
|
||
return rows([{
|
||
balance: {
|
||
provider: 'volcengine-ark', available_balance_cny: '0', cash_balance_cny: '0',
|
||
frozen_amount_cny: '0', arrears_balance_cny: '0', credit_limit_cny: '0',
|
||
currency: 'CNY', synced_at: asOf,
|
||
},
|
||
sync_runs: [syncRun('balance', asOf), bill, syncRun('packages', asOf)],
|
||
}]);
|
||
}
|
||
if (text.includes('GROUP BY GROUPING SETS')) return rows([{
|
||
model: null, total_row: true, task_count: 1, user_count: 1,
|
||
settled_credits: '100', held_credits: '0', refunded_credits: '0',
|
||
estimated_cost_cny: '46.000000', incomplete_task_count: 0,
|
||
timing_shift_cost_cny: '0',
|
||
}]);
|
||
if (text.includes('FROM operational_sources') && text.includes('GROUP BY model')) return rows([]);
|
||
if (text.includes('WITH selected_dates AS')) return rows([{
|
||
...officialRevision({
|
||
sourceDetailKey: 'usage-gate', billLineType: 'usage', recognizedModel: 'Seedance 2.0',
|
||
billableTokens: '1000000', deductionCount: '1000000', listEquivalentCostCny: '46.000000',
|
||
originalBillAmountCny: '0', payableAmountCny: '0', paidAmountCny: '0', unpaidAmountCny: '0',
|
||
expenseDate: '2026-06-30',
|
||
}),
|
||
coverage_complete: coverageComplete,
|
||
}]);
|
||
throw new Error(`门禁用例执行了未预期 SQL:${text}`);
|
||
});
|
||
|
||
const overview = await getAdminApiCostOverview({ query } as unknown as DbClient, {
|
||
range: 'month', month: '2026-06', asOf,
|
||
});
|
||
|
||
expect(overview.operations).toMatchObject({
|
||
estimatedCostCny: '46.000000',
|
||
officialEquivalentCostCny: null,
|
||
differenceCny: null,
|
||
officialPayableCny: null,
|
||
reconciliationStatus: 'unavailable',
|
||
});
|
||
});
|
||
|
||
it('经营对账区分完整对齐、已知小计、跨期归因和未解释差额', () => {
|
||
const resolve = (
|
||
incompleteTaskCount: number,
|
||
estimatedCostMicros: bigint,
|
||
officialEquivalentMicros: bigint | null,
|
||
timingShiftCostMicros = 0n,
|
||
) => resolveBusinessReconciliationStatus({
|
||
incompleteTaskCount, estimatedCostMicros, officialEquivalentMicros, timingShiftCostMicros,
|
||
});
|
||
|
||
expect(resolve(0, 305_773_290n, 305_773_290n)).toEqual({
|
||
difference: 0n, status: 'matched',
|
||
});
|
||
expect(resolve(2, 305_773_290n, 305_773_290n)).toEqual({
|
||
difference: 0n, status: 'incomplete',
|
||
});
|
||
expect(resolve(0, 304_608_616n, 305_773_290n, 1_164_674n)).toEqual({
|
||
difference: 1_164_674n, status: 'matched-adjusted',
|
||
});
|
||
expect(resolve(0, 300_000_000n, 305_773_290n)).toEqual({
|
||
difference: 5_773_290n, status: 'difference',
|
||
});
|
||
expect(resolve(0, 0n, null)).toEqual({ difference: null, status: 'unavailable' });
|
||
expect(resolve(2, 0n, null)).toEqual({ difference: null, status: 'unavailable' });
|
||
});
|
||
|
||
it('还原线上审计基线,小数抵扣量不伪造整数汇总,并保留各项零值', async () => {
|
||
const now = new Date().toISOString();
|
||
let unavailableUsageAttempts = 0;
|
||
let unpricedAttempts = 0;
|
||
let businessCostFinalized = true;
|
||
const query = vi.fn(async (sql: unknown, _params?: unknown[]) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return rows([{
|
||
local_start: '2026-07-01', local_end: '2026-07-13',
|
||
official_start: '2026-07-01', official_end: '2026-07-13',
|
||
local_intervals: [{ start: '2026-07-01', end: '2026-07-13' }],
|
||
official_intervals: [{ start: '2026-07-13', end: '2026-07-13' }],
|
||
}]);
|
||
if (text.includes('FROM provider_account_balance_snapshots')) return rows([{
|
||
provider: 'volcengine-ark', available_balance_cny: '0', cash_balance_cny: '0',
|
||
frozen_amount_cny: '0', arrears_balance_cny: '0', credit_limit_cny: '0', currency: 'CNY', synced_at: now,
|
||
}]);
|
||
if (text.includes('WITH selected_dates AS')) return rows([
|
||
officialRevision({
|
||
sourceDetailKey: 'usage-1', billLineType: 'usage', recognizedModel: 'Seedance 2.0',
|
||
billableTokens: '9227670', deductionCount: '72664.544077', listEquivalentCostCny: '305.773290',
|
||
originalBillAmountCny: '0', payableAmountCny: '0', paidAmountCny: '0', unpaidAmountCny: '0',
|
||
expenseDate: '2026-07-13',
|
||
}),
|
||
officialRevision({
|
||
sourceDetailKey: 'package-fast', billLineType: 'package-purchase', recognizedModel: 'Seedance 2.0 Fast',
|
||
billableTokens: null, deductionCount: null, listEquivalentCostCny: null,
|
||
originalBillAmountCny: '198', payableAmountCny: '198', paidAmountCny: '198', unpaidAmountCny: '0',
|
||
}),
|
||
officialRevision({
|
||
sourceDetailKey: 'package-mini', billLineType: 'package-purchase', recognizedModel: 'Seedance 2.0 Mini',
|
||
billableTokens: null, deductionCount: null, listEquivalentCostCny: null,
|
||
originalBillAmountCny: '196', payableAmountCny: '196', paidAmountCny: '196', unpaidAmountCny: '0',
|
||
}),
|
||
]);
|
||
if (text.includes('FROM provider_billing_sync_runs') && text.includes('WITH ranked AS')) return rows([
|
||
syncRun('balance', now), syncRun('bill', now), syncRun('packages', now),
|
||
]);
|
||
if (text.includes('today_estimated_cost_cny')) return rows([{
|
||
billable_tokens: '9227670', estimated_cost_cny: '305.773290',
|
||
raw_billable_tokens: '8042794', raw_estimated_cost_cny: '272.807868',
|
||
inferred_adjustment_tokens: '1184876', inferred_adjustment_cost_cny: '32.965422',
|
||
comparable_billable_tokens: '9227670', comparable_estimated_cost_cny: '305.773290',
|
||
comparable_raw_billable_tokens: '8042794', comparable_raw_estimated_cost_cny: '272.807868',
|
||
comparable_inferred_adjustment_tokens: '1184876',
|
||
comparable_inferred_adjustment_cost_cny: '32.965422',
|
||
unpriced_attempts: unpricedAttempts, unpriced_tokens: unpricedAttempts ? '108900' : '0',
|
||
unavailable_usage_attempts: unavailableUsageAttempts,
|
||
comparable_unpriced_attempts: unpricedAttempts,
|
||
comparable_unpriced_tokens: unpricedAttempts ? '108900' : '0',
|
||
comparable_unavailable_usage_attempts: unavailableUsageAttempts,
|
||
today_estimated_cost_cny: '0', today_unpriced_attempts: unpricedAttempts,
|
||
today_unavailable_usage_attempts: unavailableUsageAttempts,
|
||
month_estimated_cost_cny: '305.773290', month_unpriced_attempts: unpricedAttempts,
|
||
month_unavailable_usage_attempts: unavailableUsageAttempts,
|
||
}]);
|
||
if (text.includes('AS row_count')) return rows([{
|
||
row_count: 5, billable_tokens: '9227670', unpriced_rows: 0, unpriced_tokens: '0',
|
||
list_equivalent_cost_cny: '305.773290',
|
||
original_bill_amount_cny: '0', payable_amount_cny: '0', paid_amount_cny: '0',
|
||
unpaid_amount_cny: '0', deducted_tokens: null, all_final: false, any_final: false,
|
||
}]);
|
||
if (text.includes('all_account_payable_cny')) return rows([{
|
||
all_account_payable_cny: '0', seedance_payable_cny: '0', seedance_list_cost_cny: '305.773290',
|
||
}]);
|
||
if (text.includes('successful_estimated_cost_cny')) return rows([
|
||
localModel('Seedance 2.0', '171.072000', {
|
||
unpriced_tasks: unpricedAttempts, unavailable_usage_attempts: unavailableUsageAttempts,
|
||
}),
|
||
localModel('Seedance 2.0 Fast', '40.000000', {
|
||
unpriced_tasks: unpricedAttempts, unavailable_usage_attempts: unavailableUsageAttempts,
|
||
}),
|
||
localModel('Seedance 2.0 Mini', '61.735868', {
|
||
unpriced_tasks: unpricedAttempts, unavailable_usage_attempts: unavailableUsageAttempts,
|
||
}),
|
||
]);
|
||
if (text.includes('recognized_model AS model')) return rows([
|
||
officialModel('Seedance 2.0', '3955976', '171.072296'),
|
||
officialModel('Seedance 2.0 Fast', '1350238', '49.958806'),
|
||
officialModel('Seedance 2.0 Mini', '3921456', '84.742188'),
|
||
]);
|
||
if (text.includes('GROUP BY day')) return rows([{
|
||
day: '2026-07-13', billable_tokens: '9227670', estimated_cost_cny: '305.773290',
|
||
unpriced_attempts: unpricedAttempts, unavailable_usage_attempts: unavailableUsageAttempts,
|
||
}]);
|
||
if (text.includes('FROM provider_pricing_rules')) return rows([]);
|
||
if (text.includes('FROM provider_resource_packages') || text.includes('FROM provider_resource_package_snapshots')) return rows([]);
|
||
if (text.includes('package_purchase_amount_cny')) return rows([{
|
||
usage_original_amount_cny: '0', usage_payable_amount_cny: '0', usage_paid_amount_cny: '0',
|
||
usage_unpaid_amount_cny: '0', package_purchase_amount_cny: '394',
|
||
package_purchase_paid_amount_cny: '394', package_purchase_unpaid_amount_cny: '0',
|
||
}]);
|
||
if (text.includes('AS timing_tokens')) return rows([{ timing_tokens: '0', timing_cost_cny: '0' }]);
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([{
|
||
opening_prepaid_asset_cny: '0', acquisition_cost_cny: '394', amortized_cost_cny: '0',
|
||
expiry_loss_cny: '0', adjustment_cny: '0', ending_prepaid_asset_cny: '394',
|
||
incomplete_rows: 0, row_count: 1,
|
||
}]);
|
||
if (text.includes('FROM provider_operational_usage_records')) return rows([]);
|
||
if (text.includes('business_task_aggregates AS')) return rows([
|
||
businessTaskRow('305.773290', {
|
||
billable_tokens: '9227670', settled_credits: 120, inferred_attempts: 1,
|
||
cost_finalized: businessCostFinalized,
|
||
missing_usage_attempts: businessCostFinalized ? 0 : 1,
|
||
}),
|
||
]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
});
|
||
|
||
const dashboard = await getAdminApiCostDashboard({ query } as unknown as DbClient, {
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(() => endpointContracts.adminApiUsage.response.parse(dashboard)).not.toThrow();
|
||
expect(dashboard.account).toMatchObject({
|
||
availableBalanceCny: '0.000000', cashBalanceCny: '0.000000', status: 'fresh',
|
||
});
|
||
expect(dashboard.sync).toMatchObject({ status: 'normal', stale: false });
|
||
expect(dashboard.packages).toEqual([]);
|
||
expect(dashboard.coverage.comparable).toMatchObject({ status: 'partial' });
|
||
expect(dashboard.coverage.excludedPeriods).toEqual([{
|
||
start: '2026-07-01', end: '2026-07-12', reason: '不在本地与官方共同覆盖范围内',
|
||
}]);
|
||
expect(dashboard.daily.find(item => item.day === '2026-07-01')?.officialBillableTokens).toBeNull();
|
||
expect(dashboard.daily.find(item => item.day === '2026-07-13')?.officialBillableTokens).toBe(9_227_670);
|
||
expect(dashboard.summary).toMatchObject({
|
||
billStatus: 'provisional',
|
||
officialBillableTokens: 9_227_670,
|
||
officialUnpricedRows: 0,
|
||
officialUnpricedTokens: 0,
|
||
officialListEquivalentCostCny: '305.773290',
|
||
localBillableTokens: 9_227_670,
|
||
localEstimatedCostCny: '305.773290',
|
||
localEstimateCompleteness: 'complete',
|
||
localUnavailableUsageAttempts: 0,
|
||
tokenDelta: 0,
|
||
costDeltaCny: '0.000000',
|
||
localCostUnderstatementPercent: 0,
|
||
officialPayableAmountCny: '0.000000',
|
||
officialDiscountAmountCny: '305.773290',
|
||
resourcePackageDeductedTokens: null,
|
||
noPackageRunwayDays: 0,
|
||
noPackageProjectedDailyCostCny: '305.773290',
|
||
noPackageProjectionCoveredDays: 1,
|
||
noPackageProjectionStatus: 'partial',
|
||
});
|
||
expect(dashboard.combinedStatus).toMatchObject({
|
||
billState: 'provisional', matchState: 'adjusted-matched', label: '出账中 · 经调整后对齐',
|
||
});
|
||
expect(dashboard.operations).toMatchObject({
|
||
taskCount: 1, userCount: 1, settledCredits: 120, heldCredits: 0,
|
||
estimatedCostCny: '305.773290', estimateCompleteness: 'complete', incompleteTaskCount: 0,
|
||
officialEquivalentCostCny: null, differenceCny: null,
|
||
officialPayableCny: null, reconciliationStatus: 'unavailable',
|
||
});
|
||
expect(dashboard.operations.models.map(model => model.model)).toEqual([
|
||
'Seedance 2.5', 'Seedance 2.0', 'Seedance 2.0 Fast', 'Seedance 2.0 Mini',
|
||
]);
|
||
expect(dashboard.operations.models[1]).toMatchObject({
|
||
model: 'Seedance 2.0', taskCount: 1, operationalCallCount: 0,
|
||
businessEstimatedCostCny: '305.773290', operationalEstimatedCostCny: '0.000000',
|
||
knownAccountCostCny: '305.773290', averageTaskCostCny: '305.773290',
|
||
priceVersions: ['volcengine-seedance-2026-07-10'],
|
||
});
|
||
expect(dashboard.books).toMatchObject({
|
||
usage: {
|
||
rawLocalTokens: 8_042_794, inferredAdjustmentTokens: 1_184_876,
|
||
adjustedLocalTokens: 9_227_670, officialTokens: 9_227_670, residualTokens: 0,
|
||
},
|
||
valuation: {
|
||
localEstimateCompleteness: 'complete', officialEstimateCompleteness: 'complete',
|
||
rawLocalCostCny: '272.807868', residualCostCny: '0.000000',
|
||
},
|
||
settlement: { usagePayableAmountCny: '0.000000', packagePurchasePaidAmountCny: '394.000000' },
|
||
});
|
||
const officialUsageQueries = query.mock.calls
|
||
.map(([sql]) => String(sql))
|
||
.filter(sql => sql.includes('FROM provider_bill_details') && !sql.includes('all_account_payable_cny'));
|
||
expect(officialUsageQueries.some(sql => sql.includes("bill_line_type = 'usage'"))).toBe(true);
|
||
expect(query.mock.calls.every(([sql]) => !String(sql).includes('ListBillDetail'))).toBe(true);
|
||
const coverageSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('AS local_start'))!;
|
||
expect(coverageSql).toContain('latest_manifests AS MATERIALIZED');
|
||
expect(coverageSql).toContain('valid_bill_runs AS MATERIALIZED');
|
||
expect(coverageSql).toContain('EXISTS (\n SELECT 1 FROM provider_bill_sync_run_items evidence');
|
||
expect(coverageSql).not.toContain('evidenced_bill_runs');
|
||
|
||
unavailableUsageAttempts = 3;
|
||
businessCostFinalized = false;
|
||
const partial = await getAdminApiCostDashboard({ query } as unknown as DbClient, {
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(partial.summary).toMatchObject({
|
||
localEstimatedCostCny: '305.773290', localEstimateCompleteness: 'partial',
|
||
localUnpricedAttempts: 0, localUnpricedTokens: 0, localUnavailableUsageAttempts: 3,
|
||
monthEstimatedCostCny: '305.773290', monthEstimateCompleteness: 'partial',
|
||
monthUnavailableUsageAttempts: 3,
|
||
});
|
||
expect(partial.combinedStatus.matchState).toBe('cannot-reconcile');
|
||
expect(partial.operations).toMatchObject({
|
||
reconciliationStatus: 'unavailable', incompleteTaskCount: 1,
|
||
estimateCompleteness: 'partial', differenceCny: null,
|
||
});
|
||
expect(partial.books.valuation).toMatchObject({
|
||
localEstimateCompleteness: 'partial',
|
||
rawLocalCostCny: '272.807868', adjustedLocalCostCny: '305.773290',
|
||
officialListEquivalentCostCny: '305.773290', rawCostDeltaCny: null, residualCostCny: null,
|
||
});
|
||
expect(partial.models[1]).toMatchObject({
|
||
estimatedCostCny: '171.072000', unavailableUsageAttempts: 3,
|
||
estimateCompleteness: 'partial',
|
||
});
|
||
expect(partial.daily.find(item => item.day === '2026-07-13')).toMatchObject({
|
||
localEstimatedCostCny: '305.773290', localUnpricedAttempts: 0,
|
||
localUnavailableUsageAttempts: 3, localEstimateCompleteness: 'partial',
|
||
});
|
||
|
||
const localSummarySql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('today_estimated_cost_cny'))!;
|
||
expect(localSummarySql).toContain('WHERE billable_tokens > 0');
|
||
expect(localSummarySql).toContain('WHERE billable_tokens IS NULL');
|
||
expect(localSummarySql).not.toContain('WHERE (estimated_cost IS NULL');
|
||
const fullSyncSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('FROM provider_billing_sync_runs') && sql.includes('WITH ranked AS'))!;
|
||
expect(fullSyncSql).toContain(
|
||
"CASE WHEN completed_at > $1::timestamptz THEN 'running' ELSE status END",
|
||
);
|
||
expect(fullSyncSql).toMatch(
|
||
/max\(completed_at\) FILTER \(\s+WHERE status = 'success' AND completed_at <= \$1::timestamptz/,
|
||
);
|
||
expect(fullSyncSql).toMatch(
|
||
/max\(window_end\) FILTER \(\s+WHERE status = 'success' AND completed_at <= \$1::timestamptz/,
|
||
);
|
||
|
||
unavailableUsageAttempts = 0;
|
||
unpricedAttempts = 1;
|
||
const positiveUnpriced = await getAdminApiCostDashboard({ query } as unknown as DbClient, {
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(positiveUnpriced.summary).toMatchObject({
|
||
localEstimatedCostCny: '305.773290', localEstimateCompleteness: 'partial',
|
||
localUnpricedAttempts: 1, localUnpricedTokens: 108900,
|
||
localUnavailableUsageAttempts: 0,
|
||
});
|
||
expect(positiveUnpriced.models[1]).toMatchObject({
|
||
estimatedCostCny: '171.072000', unpricedTasks: 1,
|
||
unavailableUsageAttempts: 0, estimateCompleteness: 'partial',
|
||
});
|
||
});
|
||
|
||
it('最新官方批次零用量时仍保留 provisional/reopened 账单状态', async () => {
|
||
const asOf = '2026-07-13T06:00:00.000Z';
|
||
const projections: unknown[] = [];
|
||
const run = async (
|
||
billState: 'provisional' | 'reopened',
|
||
model: 'Seedance 2.0 Fast' | 'Seedance 2.0 Mini',
|
||
) => {
|
||
const resolveQuery = async (sql: unknown, params?: unknown[]) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) {
|
||
const isAccountCoverage = Array.isArray(params?.[1]) && params?.[1]?.length === 4;
|
||
return rows([{
|
||
local_start: '2026-07-13', local_end: '2026-07-13',
|
||
official_start: isAccountCoverage ? '2026-07-07' : '2026-07-13', official_end: '2026-07-13',
|
||
local_intervals: [{ start: '2026-07-13', end: '2026-07-13' }],
|
||
official_intervals: [{
|
||
start: isAccountCoverage ? '2026-07-07' : '2026-07-13',
|
||
end: '2026-07-13', isFinal: false, billState,
|
||
}],
|
||
}]);
|
||
}
|
||
if (text.includes('FROM provider_account_balance_snapshots')) return rows([]);
|
||
if (text.includes('FROM provider_billing_sync_runs') && text.includes('WITH ranked AS')) {
|
||
return rows([syncRun('bill', asOf)]);
|
||
}
|
||
if (text.includes('today_estimated_cost_cny')) return rows([{
|
||
billable_tokens: '0', estimated_cost_cny: '0', raw_billable_tokens: '0',
|
||
raw_estimated_cost_cny: '0', inferred_adjustment_tokens: '0',
|
||
inferred_adjustment_cost_cny: '0', today_estimated_cost_cny: '0',
|
||
comparable_billable_tokens: '0', comparable_estimated_cost_cny: '0',
|
||
comparable_raw_billable_tokens: '0', comparable_raw_estimated_cost_cny: '0',
|
||
comparable_inferred_adjustment_tokens: '0',
|
||
comparable_inferred_adjustment_cost_cny: '0',
|
||
month_estimated_cost_cny: '0',
|
||
}]);
|
||
if (text.includes('WITH selected_dates AS')) return rows([]);
|
||
if (text.includes('successful_estimated_cost_cny')) return rows([]);
|
||
if (text.includes('GROUP BY day')) return rows([]);
|
||
if (text.includes('FROM provider_pricing_rules')) return rows([]);
|
||
if (text.includes('FROM provider_resource_package_snapshots')) return rows([]);
|
||
if (text.includes('AS timing_tokens')) return rows([{ timing_tokens: '0', timing_cost_cny: '0' }]);
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([{
|
||
incomplete_rows: 0, row_count: 0, event_rows: 0, opening_catch_up_rows: 0,
|
||
}]);
|
||
if (text.includes('business_task_aggregates AS')) return rows([]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
};
|
||
const queryProbe = serialQueryProbe(resolveQuery);
|
||
const query = queryProbe.query;
|
||
|
||
const dashboard = await getAdminApiCostDashboard({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-13', model, asOf,
|
||
});
|
||
expect(queryProbe.peakActive()).toBe(1);
|
||
expect(dashboard.combinedStatus.billState).toBe(billState);
|
||
expect(dashboard.summary.officialPayableAmountCny).toBe('0.000000');
|
||
expect(dashboard.account.availableBalanceCny).toBeNull();
|
||
expect(dashboard.summary).toMatchObject({
|
||
noPackageProjectionCoveredDays: 7,
|
||
noPackageProjectionStatus: 'complete',
|
||
noPackageProjectedDailyCostCny: '0.000000',
|
||
});
|
||
projections.push({
|
||
coveredDays: dashboard.summary.noPackageProjectionCoveredDays,
|
||
status: dashboard.summary.noPackageProjectionStatus,
|
||
daily: dashboard.summary.noPackageProjectedDailyCostCny,
|
||
});
|
||
const coverageSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('AS local_start'))!;
|
||
expect(coverageSql).toContain('official_run_day_candidates AS');
|
||
expect(coverageSql).toContain('PARTITION BY days.day::date');
|
||
expect(coverageSql).toContain('official_run_days AS');
|
||
expect(coverageSql).toContain('WHERE run_rank = 1');
|
||
expect(coverageSql).not.toContain('JOIN LATERAL (');
|
||
expect(coverageSql).not.toContain('LIMIT 1');
|
||
expect(coverageSql).toContain('manifests.bill_state');
|
||
const officialSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('WITH selected_dates AS'))!;
|
||
expect(officialSql).toContain("bill_state = 'final' AS is_final");
|
||
expect(officialSql).toContain('WHERE run_rank = 1');
|
||
};
|
||
|
||
await run('provisional', 'Seedance 2.0 Mini');
|
||
await run('reopened', 'Seedance 2.0 Fast');
|
||
expect(projections[0]).toEqual(projections[1]);
|
||
});
|
||
|
||
it('官方尚未覆盖当天时仍保留本地摘要、模型、任务和用户成本', async () => {
|
||
const asOf = '2026-07-13T06:00:00.000Z';
|
||
const observedLocalOnly = rows([{
|
||
local_start: '2026-07-13', local_end: '2026-07-13',
|
||
official_start: null, official_end: null,
|
||
local_intervals: [{ start: '2026-07-13', end: '2026-07-13' }], official_intervals: [],
|
||
}]);
|
||
const dashboardQuery = vi.fn(async (sql: unknown, params?: unknown[]) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return observedLocalOnly;
|
||
if (text.includes('FROM provider_account_balance_snapshots')) return rows([]);
|
||
if (text.includes('FROM provider_billing_sync_runs') && text.includes('WITH ranked AS')) return rows([]);
|
||
if (text.includes('today_estimated_cost_cny')) {
|
||
expect(params?.[3]).toEqual(['2026-07-13']);
|
||
expect(params?.[4]).toEqual([]);
|
||
return rows([{
|
||
billable_tokens: '605700', estimated_cost_cny: '16.959600',
|
||
raw_billable_tokens: '605700', raw_estimated_cost_cny: '16.959600',
|
||
inferred_adjustment_tokens: '0', inferred_adjustment_cost_cny: '0',
|
||
comparable_billable_tokens: '0', comparable_estimated_cost_cny: '0',
|
||
comparable_raw_billable_tokens: '0', comparable_raw_estimated_cost_cny: '0',
|
||
comparable_inferred_adjustment_tokens: '0', comparable_inferred_adjustment_cost_cny: '0',
|
||
today_estimated_cost_cny: '16.959600', month_estimated_cost_cny: '16.959600',
|
||
}]);
|
||
}
|
||
if (text.includes('WITH selected_dates AS')) return rows([]);
|
||
if (text.includes('successful_estimated_cost_cny')) return rows([localModel('Seedance 2.0', '16.959600', {
|
||
successful_tasks: 2, successful_video_count: 1, successful_output_seconds: '5',
|
||
})]);
|
||
if (text.includes('GROUP BY day')) return rows([{
|
||
day: '2026-07-13', billable_tokens: '605700', estimated_cost_cny: '16.959600',
|
||
}]);
|
||
if (text.includes('FROM provider_pricing_rules')) return rows([]);
|
||
if (text.includes('FROM provider_resource_package_snapshots')) return rows([]);
|
||
if (text.includes('AS timing_tokens')) return rows([{ timing_tokens: '0', timing_cost_cny: '0' }]);
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([{
|
||
incomplete_rows: 0, row_count: 0, event_rows: 0, opening_catch_up_rows: 0,
|
||
}]);
|
||
if (text.includes('FROM provider_operational_usage_records')) return rows([]);
|
||
if (text.includes('business_task_aggregates AS')) return rows([]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
});
|
||
const dashboard = await getAdminApiCostDashboard(
|
||
{ query: dashboardQuery } as unknown as DbClient,
|
||
{ range: 'date', date: '2026-07-13', asOf },
|
||
);
|
||
expect(() => endpointContracts.adminApiUsage.response.parse(dashboard)).not.toThrow();
|
||
expect(dashboard.coverage).toMatchObject({
|
||
local: { status: 'complete' }, official: { status: 'unavailable' },
|
||
comparable: { status: 'unavailable' },
|
||
});
|
||
expect(dashboard.summary).toMatchObject({
|
||
localBillableTokens: 605_700, localEstimatedCostCny: '16.959600',
|
||
officialBillableTokens: null, officialListEquivalentCostCny: null,
|
||
tokenDelta: null, costDeltaCny: null, localCostUnderstatementPercent: null,
|
||
});
|
||
expect(dashboard.books.usage).toMatchObject({
|
||
rawLocalTokens: null, adjustedLocalTokens: null, officialTokens: null, residualTokens: null,
|
||
});
|
||
expect(dashboard.books.valuation).toMatchObject({
|
||
rawLocalCostCny: null, adjustedLocalCostCny: null,
|
||
officialListEquivalentCostCny: null, residualCostCny: null,
|
||
});
|
||
expect(dashboard.models.map(model => model.model)).toEqual([
|
||
'Seedance 2.5', 'Seedance 2.0', 'Seedance 2.0 Fast', 'Seedance 2.0 Mini',
|
||
]);
|
||
expect(dashboard.models[1]).toMatchObject({
|
||
model: 'Seedance 2.0', estimatedCostCny: '16.959600', officialBillableTokens: null,
|
||
successfulOutputSeconds: 5, costPerSuccessfulVideoCny: '16.959600',
|
||
costPerOutputSecondCny: '3.391920',
|
||
});
|
||
expect(dashboard.models[0]).toMatchObject({
|
||
successfulVideoCount: 0, averageSuccessfulTaskCostCny: null,
|
||
costPerSuccessfulVideoCny: null, costPerOutputSecondCny: null,
|
||
});
|
||
const modelSql = dashboardQuery.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('successful_output_seconds'))!;
|
||
expect(modelSql).toContain('output_asset.duration_seconds');
|
||
expect(modelSql).toContain("output_asset.generation_meta->>'durationSource' = 'media-probe'");
|
||
expect(modelSql).toContain('output_asset.created_at <= $4');
|
||
expect(modelSql).not.toContain("success_task.config->>'durationSeconds'");
|
||
expect(modelSql).toContain('count(outputs.duration_seconds) = count(*)');
|
||
expect(dashboard.daily).toEqual([expect.objectContaining({
|
||
day: '2026-07-13', localBillableTokens: 605_700,
|
||
localEstimatedCostCny: '16.959600', officialBillableTokens: null,
|
||
})]);
|
||
|
||
const taskQuery = vi.fn(async (sql: unknown, params?: unknown[]) => {
|
||
if (String(sql).includes('AS local_start')) return observedLocalOnly;
|
||
expect(params?.slice(0, 4)).toEqual([
|
||
'volcengine-ark', ['Seedance 2.5', 'Seedance 2.0', 'Seedance 2.0 Fast', 'Seedance 2.0 Mini'],
|
||
'2026-07-13', '2026-07-13',
|
||
]);
|
||
return rows([businessTaskRow('16.959600')]);
|
||
});
|
||
const tasks = await listAdminApiCostTasks({ query: taskQuery } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-13', limit: 20, asOf,
|
||
});
|
||
expect(tasks.items).toHaveLength(1);
|
||
expect(tasks.selection).toMatchObject({ rangeStart: '2026-07-13', rangeEnd: '2026-07-13' });
|
||
|
||
const userQuery = vi.fn(async (sql: unknown, params?: unknown[]) => {
|
||
if (String(sql).includes('AS local_start')) return observedLocalOnly;
|
||
expect(params?.[2]).toBe('2026-07-13');
|
||
return rows([{
|
||
id: userId, user_id: userId, user_name: '测试用户', email: 'user@example.com',
|
||
task_count: 1, successful_tasks: 1, failed_tasks: 0, canceled_tasks: 0,
|
||
settled_credits: 100, refunded_credits: 0, billable_tokens: '605700',
|
||
estimated_cost_cny: '16.959600', business_estimated_cost_cny: '16.959600',
|
||
operational_estimated_cost_cny: '0.000000', operational_call_count: 0,
|
||
operational_unpriced_call_count: 0, incomplete_task_count: 0,
|
||
last_task_at: '2026-07-13T06:00:00.000Z',
|
||
last_activity_at: '2026-07-13T06:00:00.000Z',
|
||
__pagination_total_count: null,
|
||
__pagination_cursor_timestamp: '2026-07-13T06:00:00.000000Z',
|
||
}]);
|
||
});
|
||
const users = await listAdminApiCostUsers({ query: userQuery } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-13', limit: 20, asOf,
|
||
});
|
||
expect(users.items).toEqual([expect.objectContaining({
|
||
userId, billableTokens: 605_700, estimatedCostCny: '16.959600',
|
||
businessEstimatedCostCny: '16.959600', operationalCallCount: 0,
|
||
lastActivityAt: '2026-07-13T06:00:00.000Z',
|
||
})]);
|
||
});
|
||
|
||
it('官方已覆盖但本地未覆盖时分开官方账单状态与正式对账状态', async () => {
|
||
const asOf = '2026-07-13T06:00:00.000Z';
|
||
const official = officialRevision({
|
||
sourceDetailKey: 'official-only-usage', billLineType: 'usage', recognizedModel: 'Seedance 2.0',
|
||
billableTokens: '100', deductionCount: '100', listEquivalentCostCny: '0.004600',
|
||
originalBillAmountCny: '0', payableAmountCny: '0', paidAmountCny: '0', unpaidAmountCny: '0',
|
||
expenseDate: '2026-07-13',
|
||
});
|
||
const renamedCandidate = officialRevision({
|
||
sourceDetailKey: 'official-renamed-seedance', billLineType: 'usage', recognizedModel: null,
|
||
seedanceCandidate: true, billableTokens: '50', deductionCount: '50', listEquivalentCostCny: null,
|
||
originalBillAmountCny: '0', payableAmountCny: '0', paidAmountCny: '0', unpaidAmountCny: '0',
|
||
expenseDate: '2026-07-13',
|
||
});
|
||
const resolveQuery = async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return rows([{
|
||
local_start: null, local_end: null, official_start: '2026-07-13', official_end: '2026-07-13',
|
||
local_intervals: [], official_intervals: [{
|
||
start: '2026-07-13', end: '2026-07-13', billState: 'provisional', isFinal: false,
|
||
}],
|
||
}]);
|
||
if (text.includes('FROM provider_account_balance_snapshots')) return rows([]);
|
||
if (text.includes('FROM provider_billing_sync_runs') && text.includes('WITH ranked AS')) {
|
||
return rows([syncRun('bill', asOf)]);
|
||
}
|
||
if (text.includes('today_estimated_cost_cny')) return rows([{
|
||
billable_tokens: '0', estimated_cost_cny: '0', raw_billable_tokens: '0',
|
||
raw_estimated_cost_cny: '0', inferred_adjustment_tokens: '0', inferred_adjustment_cost_cny: '0',
|
||
comparable_billable_tokens: '0', comparable_estimated_cost_cny: '0',
|
||
comparable_raw_billable_tokens: '0', comparable_raw_estimated_cost_cny: '0',
|
||
comparable_inferred_adjustment_tokens: '0', comparable_inferred_adjustment_cost_cny: '0',
|
||
today_estimated_cost_cny: '0', month_estimated_cost_cny: '0',
|
||
}]);
|
||
if (text.includes('WITH selected_dates AS')) return rows([official, renamedCandidate]);
|
||
if (text.includes('successful_estimated_cost_cny')) return rows([]);
|
||
if (text.includes('GROUP BY day')) return rows([]);
|
||
if (text.includes('FROM provider_pricing_rules')) return rows([]);
|
||
if (text.includes('FROM provider_resource_package_snapshots')) return rows([]);
|
||
if (text.includes('AS timing_tokens')) return rows([{ timing_tokens: '0', timing_cost_cny: '0' }]);
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([{
|
||
incomplete_rows: 0, row_count: 0, event_rows: 0, opening_catch_up_rows: 0,
|
||
uncertain_time_rows: 0,
|
||
}]);
|
||
if (text.includes('FROM provider_operational_usage_records')) return rows([]);
|
||
if (text.includes('business_task_aggregates AS')) return rows([]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
};
|
||
const queryProbe = serialQueryProbe(resolveQuery);
|
||
const query = queryProbe.query;
|
||
|
||
const dashboard = await getAdminApiCostDashboard({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-13', asOf,
|
||
});
|
||
expect(queryProbe.peakActive()).toBe(1);
|
||
expect(() => endpointContracts.adminApiUsage.response.parse(dashboard)).not.toThrow();
|
||
expect(dashboard.combinedStatus).toMatchObject({
|
||
billState: 'unavailable', matchState: 'cannot-reconcile', coverageState: 'unavailable',
|
||
});
|
||
expect(dashboard.summary).toMatchObject({
|
||
billStatus: 'provisional', localBillableTokens: null, localEstimatedCostCny: null,
|
||
todayEstimatedCostCny: null, monthEstimatedCostCny: null,
|
||
officialBillableTokens: 150, officialUnpricedRows: 1, officialUnpricedTokens: 50,
|
||
officialListEquivalentCostCny: null,
|
||
officialPayableAmountCny: '0.000000', tokenDelta: null, costDeltaCny: null,
|
||
});
|
||
expect(dashboard.models.map(model => model.model)).toEqual([
|
||
'Seedance 2.5', 'Seedance 2.0', 'Seedance 2.0 Fast', 'Seedance 2.0 Mini',
|
||
]);
|
||
expect(dashboard.models[1]).toMatchObject({
|
||
model: 'Seedance 2.0', billableTokens: null, estimatedCostCny: null,
|
||
successfulVideoCount: null, successfulOutputSeconds: null,
|
||
costPerSuccessfulVideoCny: null, costPerOutputSecondCny: null,
|
||
officialBillableTokens: 100,
|
||
});
|
||
expect(dashboard.daily).toEqual([expect.objectContaining({
|
||
localBillableTokens: null, officialBillableTokens: 150,
|
||
})]);
|
||
expect(dashboard.summary.noPackageProjectionStatus).toBe('unpriced');
|
||
const officialSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('WITH selected_dates AS'))!;
|
||
expect(officialSql).toContain("normalized_payload->>'seedanceCandidate' = 'true'");
|
||
expect(officialSql).toContain("LIKE '%seedance%'");
|
||
});
|
||
|
||
it('attempt 接口按北京时间过滤、成本服务端排序并返回固定六位金额及游标', async () => {
|
||
const query = vi.fn(async (sql: unknown, _params?: unknown[]) => {
|
||
if (String(sql).includes('AS local_start')) return coverageRows('2026-07-13', '2026-07-13');
|
||
return rows([taskRow('16.959600'), taskRow('12.021300', {
|
||
id: '00000000-0000-4000-8000-000000000004',
|
||
task_id: '00000000-0000-4000-8000-000000000005',
|
||
model: 'Seedance 2.0 Fast',
|
||
})]);
|
||
});
|
||
const client = { query } as unknown as DbClient;
|
||
const filters = {
|
||
from: '2026-07-13', to: '2026-07-13', search: '测试', limit: 1,
|
||
} as const;
|
||
const first = await listAdminApiCostAttempts(client, filters);
|
||
|
||
expect(first.items).toEqual([expect.objectContaining({
|
||
taskId,
|
||
attempt: -1,
|
||
completionTokens: 605700,
|
||
totalTokens: 610000,
|
||
billableTokens: 605700,
|
||
billableBasis: 'raw_units_legacy',
|
||
unitPriceCnyPerMillion: '28.000000',
|
||
estimatedCostCny: '16.959600',
|
||
estimateStatus: 'legacy-inferred',
|
||
usageEvidenceOrigin: 'legacy-raw',
|
||
pricingRuleStatus: 'priced',
|
||
})]);
|
||
expect(first.pagination).toMatchObject({
|
||
limit: 1, hasPrevious: false, hasNext: true, previousCursor: null,
|
||
});
|
||
const taskCall = query.mock.calls.find(([sql]) => String(sql).includes('SELECT pur.id'))!;
|
||
const [sql, params] = taskCall;
|
||
expect(String(sql)).toContain("AT TIME ZONE 'Asia/Shanghai'");
|
||
expect(String(sql)).toContain('ORDER BY coalesce(pur.estimated_cost');
|
||
expect(String(sql)).toContain('count(*) OVER()::bigint AS __pagination_total_count');
|
||
expect(String(sql)).not.toMatch(/\bOFFSET\b/i);
|
||
const cursorTuple = String(sql).split('), page_attempts AS (')[1]?.split('ORDER BY')[0] ?? '';
|
||
expect(cursorTuple.match(/coalesce\(pur\.estimated_cost/g)).toHaveLength(1);
|
||
expect(cursorTuple.match(/coalesce\(pur\.billable_tokens/g)).toHaveLength(1);
|
||
expect(cursorTuple).toContain('pur.id');
|
||
expect(params?.slice(0, 2)).toEqual([['2026-07-13'], '测试']);
|
||
expect(params?.at(-1)).toBe(2);
|
||
|
||
const second = await listAdminApiCostAttempts(client, {
|
||
...filters, position: 'next', cursor: first.pagination.nextCursor!,
|
||
});
|
||
const taskCalls = query.mock.calls.filter(([sql]) => String(sql).includes('SELECT pur.id'));
|
||
expect(taskCalls[1]?.[1]?.at(-1)).toBe(2);
|
||
expect(second.pagination).toMatchObject({ hasPrevious: true, previousCursor: expect.any(String) });
|
||
await listAdminApiCostAttempts(client, {
|
||
...filters, position: 'previous', cursor: second.pagination.previousCursor!,
|
||
});
|
||
const previousSql = query.mock.calls
|
||
.filter(([sql]) => String(sql).includes('SELECT pur.id'))
|
||
.at(-1)?.[0];
|
||
expect(String(previousSql)).toContain(') > (');
|
||
expect(String(previousSql)).toContain('pur.id ASC');
|
||
|
||
query.mockClear();
|
||
const last = await listAdminApiCostAttempts(client, { ...filters, position: 'last' });
|
||
const lastCalls = query.mock.calls.filter(([sql]) => String(sql).includes('SELECT pur.id'));
|
||
expect(lastCalls).toHaveLength(1);
|
||
expect(String(lastCalls[0]?.[0])).toContain('pur.id ASC');
|
||
expect(last.pagination).toMatchObject({ hasNext: false, nextCursor: null });
|
||
await expect(listAdminApiCostAttempts(client, {
|
||
...filters, position: 'next', cursor: first.pagination.nextCursor!, asOf: 'not-a-date',
|
||
})).rejects.toMatchObject({ code: 'INVALID_AS_OF', statusCode: 400 });
|
||
await expect(listAdminApiCostAttempts(client, {
|
||
...filters, search: '其他筛选', position: 'next', cursor: first.pagination.nextCursor!,
|
||
})).rejects.toMatchObject({ code: 'INVALID_CURSOR', statusCode: 400 });
|
||
await expect(listAdminApiCostAttempts(client, {
|
||
...filters, cursor: first.pagination.nextCursor!,
|
||
})).rejects.toMatchObject({ code: 'INVALID_CURSOR', statusCode: 400 });
|
||
await expect(listAdminApiCostAttempts(client, {
|
||
...filters, position: 'next',
|
||
})).rejects.toMatchObject({ code: 'INVALID_CURSOR', statusCode: 400 });
|
||
const cursorEnvelope = JSON.parse(
|
||
Buffer.from(first.pagination.nextCursor!, 'base64url').toString('utf8'),
|
||
) as { payload: string; signature: string };
|
||
const tamperedCursor = Buffer.from(JSON.stringify({
|
||
...cursorEnvelope,
|
||
signature: `${cursorEnvelope.signature.slice(0, -1)}${cursorEnvelope.signature.endsWith('0') ? '1' : '0'}`,
|
||
}), 'utf8').toString('base64url');
|
||
await expect(listAdminApiCostAttempts(client, {
|
||
...filters, position: 'next', cursor: tamperedCursor,
|
||
})).rejects.toMatchObject({ code: 'INVALID_CURSOR', statusCode: 400 });
|
||
});
|
||
|
||
it('attempt 接口把无站内任务的 Provider 调用归属到管理员用户账本', async () => {
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
if (String(sql).includes('AS local_start')) return coverageRows('2026-07-26', '2026-07-26');
|
||
return rows([taskRow('12.496275', {
|
||
task_id: null,
|
||
origin: 'admin-direct',
|
||
task_status: null,
|
||
provider_task_id: 'cgt-20260726032941-6xzlt',
|
||
attempt: 0,
|
||
email: 'admin@lindyson.com',
|
||
completion_tokens: '245025',
|
||
total_tokens: '245025',
|
||
billable_tokens: '245025',
|
||
billable_basis: 'completion_tokens',
|
||
resolution: '1080p',
|
||
has_video_input: false,
|
||
unit_price_per_million: '51',
|
||
pricing_status: 'priced',
|
||
usage_evidence_origin: 'provider-reported',
|
||
pricing_input_class: 'without-video',
|
||
pricing_resolution_band: '1080p',
|
||
attribution_confidence: 'direct',
|
||
local_accounting_date: '2026-07-26',
|
||
})]);
|
||
});
|
||
|
||
const result = await listAdminApiCostAttempts({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-26', limit: 20,
|
||
});
|
||
|
||
expect(() => endpointContracts.adminApiUsageAttempts.response.parse(result)).not.toThrow();
|
||
expect(result.items[0]).toMatchObject({
|
||
taskId: null,
|
||
origin: 'admin-direct',
|
||
providerTaskId: 'cgt-20260726032941-6xzlt',
|
||
email: 'admin@lindyson.com',
|
||
billableTokens: 245025,
|
||
estimatedCostCny: '12.496275',
|
||
});
|
||
const attemptSql = query.mock.calls.map(([sql]) => String(sql)).find(sql => sql.includes('SELECT pur.id'))!;
|
||
expect(attemptSql).toContain('coalesce(gt.user_id, pur.initiated_by_admin_user_id)');
|
||
expect(attemptSql).toContain('pur.initiated_by_admin_user_id IS NOT NULL');
|
||
});
|
||
|
||
it.each([
|
||
{ total: 0, expected: 0 },
|
||
{ total: 10, expected: 10 },
|
||
{ total: 20, expected: 20 },
|
||
{ total: 21, expected: 1 },
|
||
{ total: 40, expected: 20 },
|
||
{ total: 50, expected: 10 },
|
||
])('标准尾页矩阵:$total 条返回 $expected 条', async ({ total, expected }) => {
|
||
let pageSql = '';
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-13', '2026-07-13');
|
||
pageSql = text;
|
||
const resultRows = Array.from({ length: Math.min(total, 21) }, (_, index) => (
|
||
paginationAttemptRow(total - index, String(total))
|
||
));
|
||
return rows(resultRows);
|
||
});
|
||
|
||
const result = await listAdminApiCostAttempts({ query } as unknown as DbClient, {
|
||
from: '2026-07-13', to: '2026-07-13', position: 'last', limit: 20,
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
|
||
expect(result.items).toHaveLength(expected);
|
||
expect(result.pagination).toMatchObject({
|
||
limit: 20, hasPrevious: total > expected, hasNext: false, nextCursor: null,
|
||
});
|
||
expect(pageSql).toContain('count(*) OVER()::bigint AS __pagination_total_count');
|
||
expect(pageSql).not.toMatch(/\bOFFSET\b/i);
|
||
});
|
||
|
||
it('50/20 从尾页连续向前得到 10、20、20 且无重复遗漏乱序', async () => {
|
||
const ordered = Array.from({ length: 50 }, (_, index) => paginationAttemptRow(index + 1, null));
|
||
const pageResults = [
|
||
[...ordered].reverse().slice(0, 21).map(row => ({ ...row, __pagination_total_count: '50' })),
|
||
ordered.slice(0, 40).reverse().slice(0, 21),
|
||
ordered.slice(0, 20).reverse(),
|
||
];
|
||
let pageCall = 0;
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-13', '2026-07-13');
|
||
return rows(pageResults[pageCall++] ?? []);
|
||
});
|
||
const client = { query } as unknown as DbClient;
|
||
const filters = {
|
||
from: '2026-07-13', to: '2026-07-13', limit: 20,
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
} as const;
|
||
|
||
const last = await listAdminApiCostAttempts(client, { ...filters, position: 'last' });
|
||
const middle = await listAdminApiCostAttempts(client, {
|
||
...filters, position: 'previous', cursor: last.pagination.previousCursor!,
|
||
});
|
||
const first = await listAdminApiCostAttempts(client, {
|
||
...filters, position: 'previous', cursor: middle.pagination.previousCursor!,
|
||
});
|
||
|
||
expect([last.items.length, middle.items.length, first.items.length]).toEqual([10, 20, 20]);
|
||
const ids = [...first.items, ...middle.items, ...last.items].map(item => item.id);
|
||
expect(ids).toEqual(ordered.map(row => row.id));
|
||
expect(new Set(ids).size).toBe(50);
|
||
expect(first.pagination.hasPrevious).toBe(false);
|
||
expect(last.pagination.hasNext).toBe(false);
|
||
});
|
||
|
||
it('attempt 游标保留同毫秒内的 PostgreSQL 微秒,并用 UUID 继续解开同时间边界', async () => {
|
||
const boundaryId = paginationUuid(2);
|
||
let pageCall = 0;
|
||
let secondParams: unknown[] = [];
|
||
let firstSql = '';
|
||
const boundaryRows = [
|
||
taskRow('16.959600', {
|
||
id: boundaryId,
|
||
__pagination_total_count: null,
|
||
__pagination_cursor_timestamp: '2026-07-13T06:00:00.123456Z',
|
||
}),
|
||
taskRow('16.959600', {
|
||
id: paginationUuid(1),
|
||
__pagination_total_count: null,
|
||
__pagination_cursor_timestamp: '2026-07-13T06:00:00.123456Z',
|
||
}),
|
||
];
|
||
const query = vi.fn(async (sql: unknown, params?: unknown[]) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-13', '2026-07-13');
|
||
pageCall += 1;
|
||
if (pageCall === 1) {
|
||
firstSql = text;
|
||
return rows(boundaryRows);
|
||
}
|
||
secondParams = params ?? [];
|
||
return rows([taskRow('1.000000', {
|
||
id: paginationUuid(3),
|
||
__pagination_total_count: null,
|
||
__pagination_cursor_timestamp: '2026-07-13T05:59:59.999999Z',
|
||
})]);
|
||
});
|
||
const client = { query } as unknown as DbClient;
|
||
const filters = {
|
||
from: '2026-07-13', to: '2026-07-13', limit: 1,
|
||
asOf: '2026-07-13T06:00:01.000Z',
|
||
} as const;
|
||
const first = await listAdminApiCostAttempts(client, filters);
|
||
await listAdminApiCostAttempts(client, {
|
||
...filters, position: 'next', cursor: first.pagination.nextCursor!,
|
||
});
|
||
|
||
expect(secondParams.slice(-5, -1)).toEqual([
|
||
'16.959600', '605700', '2026-07-13T06:00:00.123456Z', boundaryId,
|
||
]);
|
||
expect(firstSql).toContain("to_char(coalesce(pur.completed_at, pur.created_at) AT TIME ZONE 'UTC'");
|
||
expect(firstSql).toContain('coalesce(pur.completed_at, pur.created_at),');
|
||
});
|
||
|
||
it('首次成本快照在旧测试行未回填内部总数时仍建立精确单页元数据', async () => {
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
if (String(sql).includes('AS local_start')) return coverageRows('2026-07-13', '2026-07-13');
|
||
return rows([paginationAttemptRow(1, null)]);
|
||
});
|
||
|
||
const result = await listAdminApiCostAttempts({ query } as unknown as DbClient, {
|
||
from: '2026-07-13', to: '2026-07-13', position: 'last', limit: 20,
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(result.pagination).toMatchObject({
|
||
currentPage: 1, totalPages: 1, totalItems: 1,
|
||
snapshotCursor: expect.any(String),
|
||
});
|
||
});
|
||
|
||
it('tasks、users、reconciliation、settlement 首次尾页快照都精确计数并使用标准余数', async () => {
|
||
const asOf = '2026-07-13T06:00:00.000Z';
|
||
const lastSql: string[] = [];
|
||
const taskQuery = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-13');
|
||
lastSql.push(text);
|
||
return rows(Array.from({ length: 21 }, (_, index) => businessTaskRow('1.000000', {
|
||
id: paginationUuid(100 + index),
|
||
__pagination_total_count: '50',
|
||
__pagination_cursor_timestamp: `2026-07-13T05:00:${String(index).padStart(2, '0')}.000000Z`,
|
||
})));
|
||
});
|
||
const tasks = await listAdminApiCostTasks({ query: taskQuery } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-13', position: 'last', limit: 20, asOf,
|
||
});
|
||
|
||
const userQuery = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-13');
|
||
lastSql.push(text);
|
||
return rows(Array.from({ length: 21 }, (_, index) => businessUserRow(index, '50')));
|
||
});
|
||
const users = await listAdminApiCostUsers({ query: userQuery } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-13', position: 'last', limit: 20, asOf,
|
||
});
|
||
|
||
const reconciliationQuery = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-13');
|
||
lastSql.push(text);
|
||
return rows(Array.from({ length: 21 }, (_, index) => ({
|
||
...reconciliationBucket(paginationUuid(200 + index)),
|
||
__pagination_total_count: '50',
|
||
})));
|
||
});
|
||
const reconciliation = await listAdminApiCostReconciliation(
|
||
{ query: reconciliationQuery } as unknown as DbClient,
|
||
{ range: 'date', date: '2026-07-13', position: 'last', limit: 20, asOf },
|
||
);
|
||
|
||
const settlementQuery = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-13');
|
||
if (text.includes('page_rows AS')) {
|
||
lastSql.push(text);
|
||
const details = Array.from({ length: 21 }, (_, index) => officialRevision({
|
||
sourceDetailKey: `usage-tail-${String(index).padStart(2, '0')}`,
|
||
billLineType: 'usage', recognizedModel: 'Seedance 2.0', billableTokens: '100',
|
||
deductionCount: '100', listEquivalentCostCny: '0.004600', originalBillAmountCny: '0',
|
||
payableAmountCny: String(21 - index), paidAmountCny: '0', unpaidAmountCny: String(21 - index),
|
||
expenseDate: '2026-07-13',
|
||
}));
|
||
return settlementSnapshotRows(details, { __pagination_total_count: '50' });
|
||
}
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([emptyPackageLedger()]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
});
|
||
const settlement = await listAdminApiCostSettlement(
|
||
{ query: settlementQuery } as unknown as DbClient,
|
||
{ range: 'date', date: '2026-07-13', position: 'last', limit: 20, asOf },
|
||
);
|
||
|
||
expect([tasks.items.length, users.items.length, reconciliation.items.length, settlement.items.length])
|
||
.toEqual([10, 10, 10, 10]);
|
||
expect(lastSql).toHaveLength(4);
|
||
for (const sql of lastSql) {
|
||
expect(sql).toContain('count(*) OVER()::bigint AS __pagination_total_count');
|
||
expect(sql).not.toMatch(/\bOFFSET\b/i);
|
||
}
|
||
});
|
||
|
||
it('经营任务接口按创建日汇总 attempt、积分和已知成本小计', async () => {
|
||
const asOf = '2026-07-13T06:00:00.000Z';
|
||
const olderTaskId = '00000000-0000-4000-8000-000000000004';
|
||
const query = vi.fn(async (sql: unknown, params?: unknown[]) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-13');
|
||
expect(text).toContain("gt.created_at >= ($3::date::timestamp AT TIME ZONE 'Asia/Shanghai')");
|
||
expect(text).toContain("gt.created_at < (($4::date + 1)::timestamp AT TIME ZONE 'Asia/Shanghai')");
|
||
expect(text).toContain('ORDER BY task.created_at DESC, task.id DESC');
|
||
expect(text).not.toContain('ORDER BY task.estimated_cost_cny');
|
||
expect(text).toContain("to_char(task.created_at AT TIME ZONE 'UTC'");
|
||
expect(text).not.toMatch(/\bOFFSET\b/i);
|
||
expect(params?.slice(2, 5)).toEqual(['2026-07-01', '2026-07-13', asOf]);
|
||
return rows([
|
||
businessTaskRow('16.959600', {
|
||
attempt_count: 2, missing_usage_attempts: 1, cost_finalized: false,
|
||
}),
|
||
businessTaskRow('99.000000', {
|
||
id: olderTaskId, created_at: '2026-07-12T05:00:00.000Z',
|
||
}),
|
||
]);
|
||
});
|
||
const filters = {
|
||
range: 'custom', from: '2026-07-01', to: '2026-07-13', limit: 1, asOf,
|
||
} as const;
|
||
const result = await listAdminApiCostTasks({ query } as unknown as DbClient, filters);
|
||
expect(() => endpointContracts.adminApiUsageTasks.response.parse(result)).not.toThrow();
|
||
expect(result.selection).toMatchObject({
|
||
range: 'custom', rangeStart: '2026-07-01', rangeEnd: '2026-07-13', grain: 'day',
|
||
});
|
||
expect(result.items[0]).toMatchObject({
|
||
id: taskId, taskId, attemptCount: 2, configuredCredits: 100,
|
||
priceVersions: ['volcengine-seedance-2026-07-10'],
|
||
settledCredits: 100, billableTokens: 605700, estimatedCostCny: '16.959600',
|
||
missingUsageAttempts: 1, estimateCompleteness: 'partial', costFinalized: false,
|
||
});
|
||
expect(result.pagination).toMatchObject({
|
||
limit: 1, hasPrevious: false, hasNext: true, previousCursor: null,
|
||
});
|
||
await listAdminApiCostTasks({ query } as unknown as DbClient, {
|
||
...filters, position: 'next', cursor: result.pagination.nextCursor!,
|
||
});
|
||
const taskCalls = query.mock.calls.filter(([sql]) => String(sql).includes('business_task_costs task'));
|
||
expect(String(taskCalls[0]?.[0])).toContain('count(*) OVER()::bigint AS __pagination_total_count');
|
||
expect(String(taskCalls[1]?.[0])).toContain('NULL::bigint AS __pagination_total_count');
|
||
expect(taskCalls[1]?.[1]?.slice(5, 7)).toEqual([
|
||
'2026-07-13T05:00:00.000000Z', taskId,
|
||
]);
|
||
expect(taskCalls[1]?.[1]?.at(-1)).toBe(2);
|
||
});
|
||
|
||
it('最终账单批准的零成本案件补全任务成本但不伪造 Token', async () => {
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
expect(text).toContain("cases.evidence->>'resolutionKind' = 'zero-cost'");
|
||
expect(text).toContain("cases.status IN ('open', 'approved')");
|
||
expect(text).toContain('attempt_count - known_usage_attempts - approved_zero_attempts');
|
||
expect(text).toContain('pending_final_bill_case_id');
|
||
return rows([businessTaskRow('0.000000', {
|
||
status: 'failed', billable_tokens: '0', settled_credits: 0, refunded_credits: 100,
|
||
credit_status: 'refunded', missing_usage_attempts: 0, cost_finalized: true,
|
||
cost_resolution_status: 'approved-zero', cost_resolution_case_id: zeroCostCaseId,
|
||
})]);
|
||
});
|
||
|
||
const result = await listAdminApiCostTasks({ query } as unknown as DbClient, {
|
||
range: 'custom', from: '2026-07-01', to: '2026-07-13', limit: 20,
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
|
||
expect(() => endpointContracts.adminApiUsageTasks.response.parse(result)).not.toThrow();
|
||
expect(result.items[0]).toMatchObject({
|
||
billableTokens: 0,
|
||
estimatedCostCny: '0.000000',
|
||
estimateCompleteness: 'complete',
|
||
costFinalized: true,
|
||
costResolutionStatus: 'approved-zero',
|
||
costResolutionCaseId: zeroCostCaseId,
|
||
missingUsageAttempts: 0,
|
||
});
|
||
});
|
||
|
||
it('任务成本状态筛选在聚合完成后执行,unfinalized 同时覆盖待建案与待终账', async () => {
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
expect(text).toContain('FROM business_task_costs task');
|
||
expect(text).toContain("task.cost_resolution_status <> 'recorded'");
|
||
expect(text).toContain("task.cost_resolution_status <> 'approved-zero'");
|
||
expect(text.indexOf('business_task_costs AS')).toBeLessThan(text.indexOf('page_tasks AS'));
|
||
return rows([]);
|
||
});
|
||
|
||
await listAdminApiCostTasks({ query } as unknown as DbClient, {
|
||
range: 'month', month: '2026-07', costState: 'unfinalized', limit: 20,
|
||
asOf: '2026-07-26T10:00:00.000Z',
|
||
});
|
||
|
||
expect(query).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it('高级 attempt 保留零成本案件状态和证据,正 Token 证据优先记为 recorded', async () => {
|
||
const openUsageId = '00000000-0000-4000-8000-000000000010';
|
||
const approvedUsageId = '00000000-0000-4000-8000-000000000011';
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-13', '2026-07-13');
|
||
expect(text).toContain('SELECT DISTINCT case_id');
|
||
expect(text).toContain("cases.evidence->>'resolutionKind' = 'zero-cost'");
|
||
return rows([
|
||
taskRow('0.000000', {
|
||
id: openUsageId, completion_tokens: null, total_tokens: null, billable_tokens: null,
|
||
billable_basis: null, estimated_cost: null, pricing_status: 'unpriced',
|
||
cost_resolution_case_id: zeroCostCaseId, cost_resolution_case_status: 'open',
|
||
cost_resolution_evidence: {
|
||
resolutionKind: 'zero-cost',
|
||
review: { status: 'pending-final-bill' },
|
||
},
|
||
}),
|
||
taskRow('0.000000', {
|
||
id: approvedUsageId, completion_tokens: null, total_tokens: null, billable_tokens: null,
|
||
billable_basis: null, estimated_cost: null, pricing_status: 'unpriced',
|
||
cost_resolution_case_id: zeroCostCaseId, cost_resolution_case_status: 'approved',
|
||
cost_resolution_evidence: {
|
||
resolutionKind: 'zero-cost',
|
||
review: { status: 'approved-zero' },
|
||
},
|
||
}),
|
||
taskRow('16.959600', {
|
||
cost_resolution_case_id: zeroCostCaseId, cost_resolution_case_status: 'superseded',
|
||
cost_resolution_evidence: {
|
||
resolutionKind: 'zero-cost',
|
||
review: { status: 'reopened' },
|
||
},
|
||
}),
|
||
]);
|
||
});
|
||
|
||
const result = await listAdminApiCostAttempts({ query } as unknown as DbClient, {
|
||
from: '2026-07-13', to: '2026-07-13', limit: 20,
|
||
});
|
||
|
||
expect(() => endpointContracts.adminApiUsageAttempts.response.parse(result)).not.toThrow();
|
||
expect(result.items.map(item => item.costResolutionStatus)).toEqual([
|
||
'pending-final-bill', 'approved-zero', 'recorded',
|
||
]);
|
||
expect(result.items[0]).toMatchObject({
|
||
costResolutionCaseId: zeroCostCaseId,
|
||
costResolutionCaseStatus: 'open',
|
||
costResolutionEvidence: {
|
||
resolutionKind: 'zero-cost',
|
||
review: { status: 'pending-final-bill' },
|
||
},
|
||
});
|
||
expect(result.items[2]).toMatchObject({
|
||
billableTokens: 605700,
|
||
costResolutionCaseStatus: 'superseded',
|
||
costResolutionStatus: 'recorded',
|
||
});
|
||
});
|
||
|
||
it('用户接口按任务创建范围汇总积分、成本和完整性', async () => {
|
||
const query = vi.fn(async (sql: unknown, _params?: unknown[]) => {
|
||
if (String(sql).includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-13');
|
||
return rows([{
|
||
id: userId, user_id: userId, user_name: '测试用户', email: 'user@example.com',
|
||
task_count: 2, successful_tasks: 1, failed_tasks: 1, canceled_tasks: 0,
|
||
settled_credits: 100, refunded_credits: 50,
|
||
billable_tokens: '930600', estimated_cost_cny: '28.980900',
|
||
business_estimated_cost_cny: '28.980900', operational_estimated_cost_cny: '0.000000',
|
||
operational_call_count: 0, operational_unpriced_call_count: 0, incomplete_task_count: 1,
|
||
last_task_at: '2026-07-13T06:00:00.000Z',
|
||
last_activity_at: '2026-07-13T06:00:00.000Z',
|
||
__pagination_total_count: null,
|
||
__pagination_cursor_timestamp: '2026-07-13T06:00:00.000000Z',
|
||
}]);
|
||
});
|
||
const result = await listAdminApiCostUsers({ query } as unknown as DbClient, {
|
||
from: '2026-07-01', to: '2026-07-13', model: 'Seedance 2.0', limit: 20,
|
||
});
|
||
|
||
expect(() => endpointContracts.adminApiUsageUsers.response.parse(result)).not.toThrow();
|
||
expect(result.items[0]).toMatchObject({
|
||
userId, name: '测试用户', taskCount: 2, successfulTasks: 1, failedTasks: 1,
|
||
settledCredits: 100, refundedCredits: 50,
|
||
billableTokens: 930600, estimatedCostCny: '28.980900',
|
||
businessEstimatedCostCny: '28.980900', operationalEstimatedCostCny: '0.000000',
|
||
knownAccountCostCny: '28.980900', operationalCallCount: 0,
|
||
operationalUnpricedCallCount: 0,
|
||
averageTaskCostCny: '14.490450', incompleteTaskCount: 1, estimateCompleteness: 'partial',
|
||
lastActivityAt: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
const userSql = query.mock.calls.map(([sql]) => String(sql)).find(sql => sql.includes('aggregated_users AS'))!;
|
||
expect(userSql).toContain("gt.created_at >= ($3::date::timestamp AT TIME ZONE 'Asia/Shanghai')");
|
||
expect(userSql).toContain('operational_sources AS');
|
||
expect(userSql).toContain('usage.initiated_by_admin_user_id AS user_id');
|
||
expect(userSql).toContain('FROM provider_operational_usage_records usage');
|
||
expect(userSql).toContain('UNION ALL');
|
||
expect(userSql).toContain('FULL OUTER JOIN operational_user_costs operational');
|
||
expect(userSql).toContain('count(*) FILTER (WHERE NOT task.cost_finalized)');
|
||
expect(userSql).toContain('ORDER BY users.estimated_cost_cny DESC');
|
||
expect(userSql).toContain('count(*) OVER()::bigint AS __pagination_total_count');
|
||
expect(userSql).toContain("to_char(users.last_activity_at AT TIME ZONE 'UTC'");
|
||
expect(userSql).not.toMatch(/\bOFFSET\b/i);
|
||
});
|
||
|
||
it('管理员普通用户汇总可精确承载三次首页 Seedance Provider 用量', async () => {
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
if (String(sql).includes('AS local_start')) return coverageRows('2026-07-26', '2026-07-26');
|
||
return rows([{
|
||
id: userId, user_id: userId, user_name: '管理员', email: 'admin@lindyson.com',
|
||
task_count: 0, successful_tasks: 0, failed_tasks: 0, canceled_tasks: 0,
|
||
settled_credits: 0, refunded_credits: 0,
|
||
billable_tokens: '735075', estimated_cost_cny: '37.488825',
|
||
business_estimated_cost_cny: '0.000000', operational_estimated_cost_cny: '37.488825',
|
||
operational_call_count: 3, operational_unpriced_call_count: 0, incomplete_task_count: 0,
|
||
last_task_at: null, last_activity_at: '2026-07-25T19:33:45.000Z',
|
||
__pagination_total_count: null,
|
||
__pagination_cursor_timestamp: '2026-07-25T19:33:45.000000Z',
|
||
}]);
|
||
});
|
||
|
||
const result = await listAdminApiCostUsers({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-26', model: 'Seedance 2.0', limit: 20,
|
||
});
|
||
|
||
expect(result.items).toEqual([expect.objectContaining({
|
||
email: 'admin@lindyson.com', taskCount: 0, successfulTasks: 0, operationalCallCount: 3,
|
||
settledCredits: 0, refundedCredits: 0,
|
||
billableTokens: 735075, estimatedCostCny: '37.488825',
|
||
businessEstimatedCostCny: '0.000000', operationalEstimatedCostCny: '37.488825',
|
||
knownAccountCostCny: '37.488825', operationalUnpricedCallCount: 0,
|
||
averageTaskCostCny: null, estimateCompleteness: 'complete', lastTaskAt: '-',
|
||
lastActivityAt: '2026-07-25T19:33:45.000Z',
|
||
})]);
|
||
});
|
||
|
||
it('对账详情按完整计价维度列出本地、官方与调整证据,并复用跨日调整口径', async () => {
|
||
const bucketId = '00000000-0000-4000-8000-000000000006';
|
||
const revisionId = '00000000-0000-4000-8000-000000000007';
|
||
const adjustmentId = '00000000-0000-4000-8000-000000000008';
|
||
const official = officialRevision({
|
||
sourceDetailKey: 'Detail-cross-date', billLineType: 'usage', recognizedModel: 'Seedance 2.0 Mini',
|
||
billableTokens: '50638', deductionCount: '50638', listEquivalentCostCny: '1.164674',
|
||
originalBillAmountCny: '0', payableAmountCny: '0', paidAmountCny: '0', unpaidAmountCny: '0',
|
||
});
|
||
official.revision_id = revisionId;
|
||
official.normalized_payload.normalizedUnitPricePerMillion = '23';
|
||
const resolveQuery = async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('WHERE b.id = $1')) return rows([reconciliationBucket(bucketId)]);
|
||
if (text.includes('links.allocated_tokens * shifted.direction')) {
|
||
return rows([{ timing_tokens: '50638', timing_cost_cny: '1.164674' }]);
|
||
}
|
||
if (text.includes("'local'::text AS side")) return rows([{
|
||
id: usageId, side: 'local', usage_record_id: usageId, bill_detail_revision_id: null,
|
||
task_id: taskId, provider_task_id: 'provider-task-1', source_detail_key: null,
|
||
allocated_tokens: '100', allocated_cost_cny: '0.002300', cause: null,
|
||
confidence: 'direct', evidence: { usageEvidenceOrigin: 'provider-reported' },
|
||
}]);
|
||
if (text.includes("'zero-cost-candidate'::text AS side")) return rows([{
|
||
id: '00000000-0000-4000-8000-000000000012', side: 'zero-cost-candidate',
|
||
usage_record_id: usageId, bill_detail_revision_id: null,
|
||
task_id: taskId, provider_task_id: null, source_detail_key: null,
|
||
allocated_tokens: '0', allocated_cost_cny: '0',
|
||
cause: 'provider-rejected-before-billing', confidence: 'high',
|
||
evidence: { caseStatus: 'open', auditScope: 'account-billing-bucket' },
|
||
}]);
|
||
if (text.includes("'adjustment'::text AS side")) return rows([{
|
||
id: adjustmentId, side: 'adjustment', usage_record_id: usageId,
|
||
bill_detail_revision_id: revisionId, task_id: taskId, provider_task_id: 'provider-task-1',
|
||
source_detail_key: 'Detail-cross-date', allocated_tokens: '50638',
|
||
allocated_cost_cny: '1.164674', cause: 'provider-accounting-cutoff',
|
||
confidence: 'high', evidence: { note: '仅移动对账日期' },
|
||
}, {
|
||
id: '00000000-0000-4000-8000-000000000011', side: 'adjustment', usage_record_id: null,
|
||
bill_detail_revision_id: revisionId, task_id: null, provider_task_id: null,
|
||
source_detail_key: 'Detail-official-only', allocated_tokens: '100',
|
||
allocated_cost_cny: '0.002300', cause: 'official-only-adjustment',
|
||
confidence: 'unallocated', evidence: { note: '仅有官方不可变修订' },
|
||
}]);
|
||
if (text.includes('WITH selected_dates AS')) return rows([official]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
};
|
||
const queryProbe = serialQueryProbe(resolveQuery);
|
||
const query = queryProbe.query;
|
||
|
||
const detail = await getAdminApiCostReconciliationDetail(
|
||
{ query } as unknown as DbClient,
|
||
bucketId,
|
||
{ asOf: '2026-07-13T06:00:00.000Z' },
|
||
);
|
||
expect(queryProbe.peakActive()).toBe(1);
|
||
|
||
expect(() => endpointContracts.adminApiUsageReconciliationDetail.response.parse(detail)).not.toThrow();
|
||
expect(detail.bucket).toMatchObject({
|
||
timingShiftTokens: 50_638, residualTokens: 0, timingShiftCostCny: '1.164674',
|
||
residualCostCny: '0.000000', matchState: 'adjusted-matched', attributionConfidence: 'high',
|
||
});
|
||
expect(detail.evidence.map(item => item.side)).toEqual([
|
||
'local', 'zero-cost-candidate', 'official', 'adjustment', 'adjustment',
|
||
]);
|
||
expect(detail.evidence[1]).toMatchObject({
|
||
taskId, allocatedTokens: 0, cause: 'provider-rejected-before-billing',
|
||
evidence: { caseStatus: 'open', auditScope: 'account-billing-bucket' },
|
||
});
|
||
expect(detail.evidence[2]).toMatchObject({
|
||
billDetailRevisionId: revisionId, sourceDetailKey: 'Detail-cross-date', allocatedTokens: 50_638,
|
||
});
|
||
expect(detail.evidence[4]).toMatchObject({ usageRecordId: null, billDetailRevisionId: revisionId });
|
||
const adjustmentSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes("'adjustment'::text AS side"))!;
|
||
expect(adjustmentSql).toContain('provider_bill_detail_revisions revisions');
|
||
expect(adjustmentSql).toContain("coalesce(usage.model, revisions.normalized_payload->>'recognizedModel')");
|
||
});
|
||
|
||
it('结算 unknown 筛选与展示使用同一支付状态规则', async () => {
|
||
const official = officialRevision({
|
||
sourceDetailKey: 'usage-unknown-payment', billLineType: 'usage', recognizedModel: 'Seedance 2.0',
|
||
billableTokens: '100', deductionCount: '0', listEquivalentCostCny: null,
|
||
originalBillAmountCny: '10', payableAmountCny: '10', paidAmountCny: '0', unpaidAmountCny: '0',
|
||
});
|
||
const query = vi.fn(async (sql: unknown, _params?: unknown[]) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-01');
|
||
if (text.includes('WITH selected_dates AS')) return settlementSnapshotRows([official], {
|
||
usage_original_amount_cny: '10', usage_payable_amount_cny: '10',
|
||
usage_paid_amount_cny: '0', usage_unpaid_amount_cny: '0',
|
||
net_official_payable_cny: '10', list_price_status: 'unpriced',
|
||
});
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([{
|
||
opening_prepaid_asset_cny: '10', acquisition_cost_cny: '0', amortized_cost_cny: '0',
|
||
opening_catch_up_cny: '5', expiry_loss_cny: '0', adjustment_cny: '0', ending_prepaid_asset_cny: '5',
|
||
incomplete_rows: 0, row_count: 1, event_rows: 1, opening_catch_up_rows: 1,
|
||
}]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
});
|
||
|
||
const settlement = await listAdminApiCostSettlement({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-01', paymentStatus: 'unknown', limit: 20,
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(() => endpointContracts.adminApiUsageSettlement.response.parse(settlement)).not.toThrow();
|
||
expect(settlement.items).toEqual([expect.objectContaining({
|
||
sourceDetailKey: 'usage-unknown-payment', paymentStatus: 'unknown', payableAmountCny: '10.000000',
|
||
})]);
|
||
expect(settlement.summary).toMatchObject({
|
||
allocationStatus: 'complete', openingPrepaidAssetCny: '10.000000', amortizedCostCny: '0.000000',
|
||
openingCatchUpCny: '5.000000', endingPrepaidAssetCny: '5.000000',
|
||
timeAttributionStatus: 'partial',
|
||
listPriceOffsetStatus: 'unpriced', listPriceOffsetValueCny: null,
|
||
});
|
||
const settlementCall = query.mock.calls.find(([sql]) => String(sql).includes('page_rows AS'))!;
|
||
const settlementSql = String(settlementCall[0]);
|
||
const settlementParams = settlementCall[1] as unknown[];
|
||
expect(settlementSql).toContain('FROM summary');
|
||
expect(settlementSql).toContain('rows.payment_status = $9');
|
||
expect(settlementSql).toContain('$12::uuid');
|
||
expect(settlementSql).toContain('rows.revision_id');
|
||
expect(settlementSql).not.toContain('rows.source_detail_key COLLATE');
|
||
expect(settlementSql).toContain('LIMIT $13');
|
||
expect(settlementSql).toContain('count(*) OVER()::bigint AS __pagination_total_count');
|
||
expect(settlementSql).not.toMatch(/\bOFFSET\b/i);
|
||
expect(settlementSql).toContain('selected_dates AS MATERIALIZED');
|
||
expect(settlementSql).toContain('valid_bill_runs AS MATERIALIZED');
|
||
expect(settlementSql).toContain('selected_run_candidates AS');
|
||
expect(settlementSql).toContain('runs.window_start <= $2::date');
|
||
expect(settlementSql).toContain('runs.window_end >= $1::date');
|
||
expect(settlementSql).not.toContain('evidenced_bill_runs');
|
||
expect(settlementParams.slice(7)).toEqual([
|
||
null, 'unknown', null, null, null, 21,
|
||
]);
|
||
});
|
||
|
||
it('本地零用量但已有对账快照的日期仍进入 comparable 并保留官方残差', async () => {
|
||
const bucketId = '00000000-0000-4000-8000-000000000009';
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-01');
|
||
if (text.includes('WITH timing_adjustments AS')) return rows([{
|
||
...reconciliationBucket(bucketId),
|
||
raw_local_tokens: '0', adjusted_local_tokens: '0', official_tokens: '100',
|
||
raw_local_cost_cny: '0', adjusted_local_cost_cny: '0', official_cost_cny: '0.002300',
|
||
residual_tokens: '100', residual_cost_cny: '0.002300', status: 'residual',
|
||
}]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
});
|
||
|
||
const result = await listAdminApiCostReconciliation({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-01', limit: 20, asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(() => endpointContracts.adminApiUsageReconciliation.response.parse(result)).not.toThrow();
|
||
expect(result.items).toEqual([expect.objectContaining({
|
||
rawLocalTokens: 0, officialTokens: 100, residualTokens: 100, matchState: 'residual',
|
||
})]);
|
||
expect(String(query.mock.calls[0]?.[0])).toContain('local_cutoff <= $3');
|
||
const reconciliationSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('WITH timing_adjustments AS'))!;
|
||
expect(reconciliationSql).toContain('selected_runs AS');
|
||
expect(reconciliationSql).toContain('JOIN provider_reconciliation_buckets b ON b.reconciliation_run_id = selected.id');
|
||
expect(reconciliationSql).not.toContain('PARTITION BY b.provider');
|
||
expect(reconciliationSql).toContain('count(*) OVER()::bigint AS __pagination_total_count');
|
||
expect(reconciliationSql).not.toMatch(/\bOFFSET\b/i);
|
||
expect(reconciliationSql).toContain(
|
||
'ORDER BY coalesce(computed.expense_date, computed.period_start) DESC',
|
||
);
|
||
expect(reconciliationSql).toContain(
|
||
'coalesce(computed.expense_date, computed.period_start), abs(computed.residual_cost_cny)',
|
||
);
|
||
});
|
||
|
||
it('资源包购置发生退款修订后不再误报为已关联', async () => {
|
||
const revisionId = '00000000-0000-4000-8000-000000000010';
|
||
const official = officialRevision({
|
||
sourceDetailKey: 'package-revised', billLineType: 'package-purchase', recognizedModel: 'Seedance 2.0 Fast',
|
||
billableTokens: null, deductionCount: null, listEquivalentCostCny: null,
|
||
originalBillAmountCny: '198', payableAmountCny: '198', paidAmountCny: '198', unpaidAmountCny: '0',
|
||
});
|
||
official.revision_id = revisionId;
|
||
const query = vi.fn(async (sql: unknown, _params?: unknown[]) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return coverageRows('2026-07-01', '2026-07-01');
|
||
if (text.includes('WITH selected_dates AS')) return settlementSnapshotRows([official], {
|
||
package_purchase_amount_cny: '198', package_purchase_paid_amount_cny: '198',
|
||
package_purchase_unpaid_amount_cny: '0', refund_amount_cny: '-50',
|
||
billing_adjustment_amount_cny: '2', net_official_payable_cny: '150',
|
||
});
|
||
if (text.includes('SELECT bill_source_detail_key')) return rows([{
|
||
bill_source_detail_key: 'package-revised', package_id: 'package-fast-1',
|
||
current_revision_id: revisionId, cost_basis_status: 'revised',
|
||
}]);
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([{
|
||
opening_prepaid_asset_cny: '0', acquisition_cost_cny: '0', amortized_cost_cny: '0',
|
||
expiry_loss_cny: '0', adjustment_cny: '0', ending_prepaid_asset_cny: '0',
|
||
incomplete_rows: 1, row_count: 1, event_rows: 1, opening_catch_up_rows: 0,
|
||
uncertain_time_rows: 1,
|
||
}]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
});
|
||
|
||
const settlement = await listAdminApiCostSettlement({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-01', billLineType: 'package-purchase', limit: 20,
|
||
asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(settlement.items).toEqual([expect.objectContaining({
|
||
sourceDetailKey: 'package-revised', packageId: null, acquisitionLinkStatus: 'missing',
|
||
})]);
|
||
expect(settlement.summary).toMatchObject({
|
||
allocationStatus: 'partial', openingPrepaidAssetCny: null, amortizedCostCny: null,
|
||
endingPrepaidAssetCny: null, refundAmountCny: '-50.000000',
|
||
billingAdjustmentAmountCny: '2.000000', netOfficialPayableCny: '150.000000',
|
||
timeAttributionStatus: 'partial',
|
||
timeAttributionReason: '存在仅能证明额度已归零的终态证据,无法区分真实消费与到期损失',
|
||
});
|
||
const ledgerSql = query.mock.calls.map(([sql]) => String(sql))
|
||
.find(sql => sql.includes('opening_prepaid_asset_cny'))!;
|
||
expect(ledgerSql).toContain('acquisition_date <= $3::date');
|
||
expect(ledgerSql).toContain('event_date <= $3::date');
|
||
expect(ledgerSql).toContain('event_date BETWEEN $2::date AND $3::date');
|
||
const settlementCall = query.mock.calls.find(([sql]) => String(sql).includes('page_rows AS'))!;
|
||
expect(String(settlementCall[0])).toContain('rows.bill_line_type = $8');
|
||
expect((settlementCall[1] as unknown[]).slice(7)).toEqual([
|
||
'package-purchase', null, null, null, null, 21,
|
||
]);
|
||
});
|
||
|
||
it('官方从未同步时结算金额保持不可用而不是回退为零', async () => {
|
||
const query = vi.fn(async (sql: unknown) => {
|
||
const text = String(sql);
|
||
if (text.includes('AS local_start')) return rows([{
|
||
local_start: null, local_end: null, official_start: null, official_end: null,
|
||
local_intervals: [], official_intervals: [],
|
||
}]);
|
||
if (text.includes('WITH selected_dates AS')) return settlementSnapshotRows([]);
|
||
if (text.includes('opening_prepaid_asset_cny')) return rows([{
|
||
incomplete_rows: 0, row_count: 0, event_rows: 0, opening_catch_up_rows: 0,
|
||
}]);
|
||
throw new Error(`未预期 SQL:${text}`);
|
||
});
|
||
const settlement = await listAdminApiCostSettlement({ query } as unknown as DbClient, {
|
||
range: 'date', date: '2026-07-01', limit: 20, asOf: '2026-07-13T06:00:00.000Z',
|
||
});
|
||
expect(settlement.summary).toMatchObject({
|
||
coverageStatus: 'unavailable', usagePayableAmountCny: null, packagePurchaseAmountCny: null,
|
||
listPriceOffsetValueCny: null, listPriceOffsetStatus: 'unavailable', refundAmountCny: null,
|
||
billingAdjustmentAmountCny: null, netOfficialPayableCny: null,
|
||
});
|
||
});
|
||
});
|
||
|
||
function syncRun(kind: string, now: string) {
|
||
return {
|
||
kind, status: 'success', window_end: '2026-07-13', error: null, started_at: now, completed_at: now,
|
||
last_success_at: now, last_success_window_end: '2026-07-13',
|
||
};
|
||
}
|
||
|
||
function localModel(model: string, estimatedCost: string, patch: Record<string, unknown> = {}) {
|
||
return {
|
||
model, billable_tokens: '100', estimated_cost_cny: estimatedCost, successful_tasks: 1, failed_tasks: 0,
|
||
attempt_count: 1, task_count: 1, successful_video_count: 1,
|
||
failed_billable_tokens: '0', failed_estimated_cost_cny: '0', successful_estimated_cost_cny: estimatedCost,
|
||
successful_output_seconds: null,
|
||
without_video_cost_cny: estimatedCost, with_video_cost_cny: '0', unpriced_tasks: 0,
|
||
unavailable_usage_attempts: 0, ...patch,
|
||
};
|
||
}
|
||
|
||
function officialModel(model: string, tokens: string, cost: string) {
|
||
return { model, billable_tokens: tokens, list_equivalent_cost_cny: cost, payable_amount_cny: '0' };
|
||
}
|
||
|
||
let officialRevisionSequence = 500;
|
||
|
||
function officialRevision(input: {
|
||
sourceDetailKey: string;
|
||
billLineType: string;
|
||
recognizedModel: string | null;
|
||
seedanceCandidate?: boolean;
|
||
billableTokens: string | null;
|
||
deductionCount: string | null;
|
||
listEquivalentCostCny: string | null;
|
||
originalBillAmountCny: string;
|
||
payableAmountCny: string;
|
||
paidAmountCny: string;
|
||
unpaidAmountCny: string;
|
||
expenseDate?: string;
|
||
}) {
|
||
return {
|
||
revision_id: paginationUuid(officialRevisionSequence++),
|
||
source_detail_key: input.sourceDetailKey,
|
||
is_final: false,
|
||
normalized_payload: {
|
||
billPeriod: '2026-07', expenseDate: input.expenseDate ?? '2026-07-01', product: 'ark_bd',
|
||
billLineType: input.billLineType, recognizedModel: input.recognizedModel,
|
||
seedanceCandidate: input.seedanceCandidate ?? input.recognizedModel !== null,
|
||
pricingResolution: '480p/720p', pricingInputHasVideo: false,
|
||
normalizedUnitPricePerMillion: '46', billableTokens: input.billableTokens,
|
||
deductionCount: input.deductionCount, listEquivalentCostCny: input.listEquivalentCostCny,
|
||
originalBillAmountCny: input.originalBillAmountCny, preferentialBillAmountCny: '0',
|
||
discountBillAmountCny: '0', couponAmountCny: '0', payableAmountCny: input.payableAmountCny,
|
||
paidAmountCny: input.paidAmountCny, unpaidAmountCny: input.unpaidAmountCny,
|
||
currency: 'CNY', instanceNo: input.billLineType === 'package-purchase' ? input.sourceDetailKey : null,
|
||
},
|
||
};
|
||
}
|
||
|
||
function reconciliationBucket(id: string) {
|
||
return {
|
||
id, period_start: '2026-07-01', period_end: '2026-07-13', expense_date: '2026-07-01',
|
||
bill_line_type: 'usage', recognized_model: 'Seedance 2.0 Mini', pricing_resolution_band: '480p-720p',
|
||
pricing_input_class: 'without-video', unit_price_per_million: '23', currency: 'CNY',
|
||
raw_local_tokens: '50638', inferred_adjustment_tokens: '0', adjusted_local_tokens: '50638',
|
||
official_tokens: '50638', timing_adjustment_tokens: '0', residual_tokens: '50638',
|
||
raw_local_cost_cny: '1.164674', inferred_adjustment_cost_cny: '0', adjusted_local_cost_cny: '1.164674',
|
||
official_cost_cny: '1.164674', timing_adjustment_cost_cny: '0', rate_variance_cny: '0',
|
||
rounding_variance_cny: '0', residual_cost_cny: '1.164674', status: 'residual',
|
||
attribution_confidence: 'direct',
|
||
__pagination_total_count: null,
|
||
};
|
||
}
|
||
|
||
function taskRow(cost: string, patch: Record<string, unknown> = {}) {
|
||
return {
|
||
id: usageId, task_id: taskId, provider_task_id: 'provider-task-1', attempt: -1, user_id: userId,
|
||
origin: 'generation-task',
|
||
user_name: '测试用户', email: 'user@example.com', provider: 'volcengine-ark', model: 'Seedance 2.0',
|
||
resolution: '720p', has_video_input: true, task_status: 'completed', usage_status: 'success',
|
||
completion_tokens: '605700', total_tokens: '610000', billable_tokens: '605700', billable_basis: 'legacy_raw_units',
|
||
unit_price_per_million: '28', estimated_cost: cost, price_version: 'volcengine-seedance-2026-07-10',
|
||
pricing_status: 'legacy-inferred', created_at: '2026-07-13T05:00:00.000Z',
|
||
usage_evidence_origin: 'legacy-raw', pricing_rule_status: 'priced',
|
||
pricing_input_class: 'with-video', pricing_resolution_band: '480p-720p', attribution_confidence: 'medium',
|
||
local_accounting_date: '2026-07-13', provider_terminal_at: null, usage_observed_at: '2026-07-13T06:00:00.000Z',
|
||
completed_at: '2026-07-13T06:00:00.000Z', error: null,
|
||
cost_resolution_case_id: null, cost_resolution_case_status: null,
|
||
cost_resolution_evidence: null, __pagination_total_count: '2',
|
||
__pagination_cursor_timestamp: '2026-07-13T06:00:00.000000Z', ...patch,
|
||
};
|
||
}
|
||
|
||
function businessTaskRow(cost: string, patch: Record<string, unknown> = {}) {
|
||
return {
|
||
id: taskId, user_id: userId, user_name: '测试用户', email: 'user@example.com',
|
||
model: 'Seedance 2.0', status: 'completed', resolution: '720p',
|
||
price_versions: ['volcengine-seedance-2026-07-10', 'volcengine-seedance-2026-07-10'],
|
||
configured_credits: 100, reserved_credits: 100, settled_credits: 100,
|
||
refunded_credits: 0, credit_status: 'settled', attempt_count: 1,
|
||
billable_tokens: '605700', estimated_cost_cny: cost, unpriced_attempts: 0,
|
||
missing_usage_attempts: 0, inferred_attempts: 0, cost_finalized: true,
|
||
cost_resolution_status: 'recorded', cost_resolution_case_id: null,
|
||
created_at: '2026-07-13T05:00:00.000Z', completed_at: '2026-07-13T06:00:00.000Z',
|
||
__pagination_total_count: null,
|
||
__pagination_cursor_timestamp: '2026-07-13T05:00:00.000000Z',
|
||
...patch,
|
||
};
|
||
}
|
||
|
||
function paginationUuid(index: number) {
|
||
return `00000000-0000-4000-8000-${String(index).padStart(12, '0')}`;
|
||
}
|
||
|
||
function paginationAttemptRow(index: number, totalCount: string | null) {
|
||
const second = String(59 - Math.max(0, Math.min(index, 59))).padStart(2, '0');
|
||
return taskRow('16.959600', {
|
||
id: paginationUuid(index),
|
||
task_id: paginationUuid(1_000 + index),
|
||
created_at: `2026-07-13T05:00:${second}.000Z`,
|
||
completed_at: `2026-07-13T06:00:${second}.123Z`,
|
||
__pagination_total_count: totalCount,
|
||
__pagination_cursor_timestamp: `2026-07-13T06:00:${second}.123456Z`,
|
||
});
|
||
}
|
||
|
||
function businessUserRow(index: number, totalCount: string | null) {
|
||
const second = String(index).padStart(2, '0');
|
||
const id = paginationUuid(300 + index);
|
||
return {
|
||
id, user_id: id, user_name: `用户${index}`, email: `user${index}@example.com`,
|
||
task_count: 1, successful_tasks: 1, failed_tasks: 0, canceled_tasks: 0,
|
||
settled_credits: 100, refunded_credits: 0, billable_tokens: '100',
|
||
estimated_cost_cny: '1.000000', business_estimated_cost_cny: '1.000000',
|
||
operational_estimated_cost_cny: '0.000000', operational_call_count: 0,
|
||
operational_unpriced_call_count: 0, incomplete_task_count: 0,
|
||
last_task_at: `2026-07-13T05:00:${second}.000Z`,
|
||
last_activity_at: `2026-07-13T05:00:${second}.000Z`,
|
||
__pagination_total_count: totalCount,
|
||
__pagination_cursor_timestamp: `2026-07-13T05:00:${second}.000000Z`,
|
||
};
|
||
}
|
||
|
||
function emptyPackageLedger() {
|
||
return {
|
||
opening_prepaid_asset_cny: '0', acquisition_cost_cny: '0', amortized_cost_cny: '0',
|
||
opening_catch_up_cny: '0', expiry_loss_cny: '0', adjustment_cny: '0',
|
||
ending_prepaid_asset_cny: '0', incomplete_rows: 0, row_count: 0, event_rows: 0,
|
||
opening_catch_up_rows: 0, uncertain_time_rows: 0, stale_gap_time_rows: 0,
|
||
};
|
||
}
|
||
|
||
function serialQueryProbe(
|
||
resolver: (sql: unknown, params?: unknown[]) => Promise<{ rows: unknown[]; rowCount: number }>,
|
||
) {
|
||
let active = 0;
|
||
let peak = 0;
|
||
const query = vi.fn(async (sql: unknown, params?: unknown[]) => {
|
||
active += 1;
|
||
peak = Math.max(peak, active);
|
||
try {
|
||
// 主动让出一个微任务:若调用方同时提交多个 client.query,这里会稳定观测到并发。
|
||
await Promise.resolve();
|
||
return await resolver(sql, params);
|
||
} finally {
|
||
active -= 1;
|
||
}
|
||
});
|
||
return { query, peakActive: () => peak };
|
||
}
|
||
|
||
function rows<T>(items: T[]) {
|
||
return { rows: items, rowCount: items.length };
|
||
}
|
||
|
||
function settlementSnapshotRows(
|
||
pageRows: ReturnType<typeof officialRevision>[],
|
||
summaryPatch: Record<string, unknown> = {},
|
||
) {
|
||
const summary = {
|
||
usage_original_amount_cny: '0', usage_payable_amount_cny: '0',
|
||
usage_paid_amount_cny: '0', usage_unpaid_amount_cny: '0',
|
||
package_purchase_amount_cny: '0', package_purchase_paid_amount_cny: '0',
|
||
package_purchase_unpaid_amount_cny: '0', refund_amount_cny: '0',
|
||
billing_adjustment_amount_cny: '0', net_official_payable_cny: '0',
|
||
list_price_cost_cny: null, list_price_status: 'unavailable',
|
||
...summaryPatch,
|
||
};
|
||
if (pageRows.length === 0) {
|
||
return rows([{
|
||
revision_id: null, source_detail_key: null, normalized_payload: null, is_final: null,
|
||
...summary,
|
||
}]);
|
||
}
|
||
return rows(pageRows.map(row => ({ ...row, ...summary })));
|
||
}
|
||
|
||
function coverageRows(start: string, end: string) {
|
||
return rows([{
|
||
local_start: start, local_end: end, official_start: start, official_end: end,
|
||
local_intervals: [{ start, end }], official_intervals: [{ start, end }],
|
||
}]);
|
||
}
|