import test from 'node:test'; import assert from 'node:assert/strict'; import { enrichTokenStateForBilling, estimateAccumulatedCostUsd, loadCostEstimateConfig, pickSessionAccumulatedCost, resolveBillingTokenState, } from './billing-token-state.mjs'; import { computeDeltaCostCents } from './billing.mjs'; test('pickSessionAccumulatedCost reads snake_case and camelCase', () => { assert.equal(pickSessionAccumulatedCost({ accumulated_cost: 0.12 }), 0.12); assert.equal(pickSessionAccumulatedCost({ accumulatedCost: 0.34 }), 0.34); assert.equal(pickSessionAccumulatedCost({}), null); }); test('estimateAccumulatedCostUsd uses DeepSeek-ish defaults', () => { const estimate = estimateAccumulatedCostUsd( { accumulatedInputTokens: 1_000_000, accumulatedOutputTokens: 10_000 }, { enabled: true, inputUsdPer1M: 0.27, outputUsdPer1M: 1.1 }, ); assert.equal(estimate, 0.281); }); test('enrichTokenStateForBilling prefers upstream cost over estimate', () => { const enriched = enrichTokenStateForBilling( { accumulatedInputTokens: 1000, accumulatedOutputTokens: 100, accumulatedCost: 0.05 }, {}, { H5_USE_BACKEND_COST: '1' }, ); assert.equal(enriched.accumulatedCost, 0.05); }); test('enrichTokenStateForBilling fills session cost when Finish omits it', () => { const enriched = enrichTokenStateForBilling( { accumulatedInputTokens: 1000, accumulatedOutputTokens: 100 }, { sessionCost: { accumulated_cost: 0.08 } }, { H5_USE_BACKEND_COST: '1' }, ); assert.equal(enriched.accumulatedCost, 0.08); }); test('enrichTokenStateForBilling estimates when cost mode enabled and upstream missing', () => { const enriched = enrichTokenStateForBilling( { accumulatedInputTokens: 1_000_000, accumulatedOutputTokens: 0 }, {}, { H5_USE_BACKEND_COST: '1', H5_COST_ESTIMATE_FROM_TOKENS: '1', H5_COST_ESTIMATE_INPUT_USD_PER_1M: '0.27', H5_COST_ESTIMATE_OUTPUT_USD_PER_1M: '1.1', }, ); assert.equal(enriched.accumulatedCost, 0.27); }); test('loadCostEstimateConfig disables estimate when cost mode off', () => { const cfg = loadCostEstimateConfig({ H5_USE_BACKEND_COST: '0' }); assert.equal(cfg.enabled, false); }); test('resolveBillingTokenState fetches session before estimating', async () => { let fetched = false; const resolved = await resolveBillingTokenState( { accumulatedInputTokens: 1000, accumulatedOutputTokens: 100 }, { sessionId: '20260710_11', fetchSession: async (sessionId) => { fetched = true; assert.equal(sessionId, '20260710_11'); return { accumulated_cost: 0.42 }; }, }, { H5_USE_BACKEND_COST: '1' }, ); assert.equal(fetched, true); assert.equal(resolved.accumulatedCost, 0.42); }); test('enriched cost drives 1.2x billing instead of flat fallback', () => { const previous = { lastInputTokens: 224853, lastOutputTokens: 6685, lastAccumulatedCost: null }; const tokenState = enrichTokenStateForBilling( { accumulatedInputTokens: 2908661, accumulatedOutputTokens: 15349 }, {}, { H5_USE_BACKEND_COST: '1', H5_COST_ESTIMATE_FROM_TOKENS: '1', H5_COST_ESTIMATE_INPUT_USD_PER_1M: '0.27', H5_COST_ESTIMATE_OUTPUT_USD_PER_1M: '1.1', }, ); const previousCost = estimateAccumulatedCostUsd( { accumulatedInputTokens: 224853, accumulatedOutputTokens: 6685 }, loadCostEstimateConfig({ H5_USE_BACKEND_COST: '1', H5_COST_ESTIMATE_FROM_TOKENS: '1', H5_COST_ESTIMATE_INPUT_USD_PER_1M: '0.27', H5_COST_ESTIMATE_OUTPUT_USD_PER_1M: '1.1', }), ); const costCents = computeDeltaCostCents( { ...previous, lastAccumulatedCost: previousCost }, tokenState, { useBackendCost: true, usdCnyRate: 7.2, marginMultiplier: 1.2, inputCentsPer1k: 2, outputCentsPer1k: 6, minBillCents: 1, }, ); assert.ok(costCents < 1000, `expected cost-mode charge under ¥10, got ${costCents} cents`); });