Files
easyai-ai-gateway/apps/web/src/pages/PlaygroundPage.pricing.test.ts
T
chengcheng 8beb8501fa
ci / verify (pull_request) Failing after 8s
fix(billing): 封住发布前计费竞态
阻止上游提交状态不明的任务被租约接管后重复执行,并为人工复核保留可操作的结算记录。

将生产提交绑定到当前估价签名,统一复用预处理快照,并补强规则形状、定点溢出与历史规则兼容校验。

已通过 PostgreSQL 16 集成测试、Go 全量测试与静态检查、前端测试与构建、OpenAPI、依赖审计、镜像、迁移、流水线和 SemVer 门禁。
2026-07-21 10:23:58 +08:00

58 lines
2.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { GatewayPricingEstimate } from '@easyai-ai-gateway/contracts';
import {
MEDIA_ESTIMATE_DEBOUNCE_MS,
billingEstimateSignature,
mediaEstimateAllowsProduction,
mediaEstimateFromResponse,
} from './PlaygroundPage';
describe('playground pricing estimate', () => {
it('uses the specified 350ms debounce interval', () => {
expect(MEDIA_ESTIMATE_DEBOUNCE_MS).toBe(350);
});
it('only changes its signature for billing-relevant parameters', () => {
const first = billingEstimateSignature({
kind: 'images.generations', model: 'image-model', prompt: 'first',
count: 1, quality: 'high', resolution: '2K',
});
const promptOnly = billingEstimateSignature({
kind: 'images.generations', model: 'image-model', prompt: 'second',
count: 1, quality: 'high', resolution: '2K',
});
const changedCount = billingEstimateSignature({
kind: 'images.generations', model: 'image-model', prompt: 'second',
count: 2, quality: 'high', resolution: '2K',
});
expect(promptOnly).toBe(first);
expect(changedCount).not.toBe(first);
});
it('distinguishes explicit free and exposes the reservation ceiling', () => {
const response: GatewayPricingEstimate = {
candidateCount: 2,
currency: 'resource',
items: [{ amount: 0, currency: 'resource', isFree: true }],
pricingVersion: 'effective-pricing-v2',
requestFingerprint: 'fingerprint',
reservationAmount: 0,
resolver: 'effective-pricing-v2',
totalAmount: 0,
};
expect(mediaEstimateFromResponse(response)).toMatchObject({
amount: 0,
candidateCount: 2,
reservationAmount: 0,
status: 'free',
});
});
it('only allows production submission for the current pricing signature', () => {
expect(mediaEstimateAllowsProduction({ status: 'ready', signature: 'current' }, 'current')).toBe(true);
expect(mediaEstimateAllowsProduction({ status: 'free', signature: 'current' }, 'current')).toBe(true);
expect(mediaEstimateAllowsProduction({ status: 'ready', signature: 'stale' }, 'current')).toBe(false);
expect(mediaEstimateAllowsProduction({ status: 'loading', signature: 'current' }, 'current')).toBe(false);
});
});