Some checks failed
ci / verify (pull_request) Failing after 8s
阻止上游提交状态不明的任务被租约接管后重复执行,并为人工复核保留可操作的结算记录。 将生产提交绑定到当前估价签名,统一复用预处理快照,并补强规则形状、定点溢出与历史规则兼容校验。 已通过 PostgreSQL 16 集成测试、Go 全量测试与静态检查、前端测试与构建、OpenAPI、依赖审计、镜像、迁移、流水线和 SemVer 门禁。
149 lines
5.4 KiB
Go
149 lines
5.4 KiB
Go
package runner
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
|
)
|
|
|
|
func TestFixedAmountPreservesNineDecimalPlaces(t *testing.T) {
|
|
amount, err := parseFixedAmount("0.123456789")
|
|
if err != nil {
|
|
t.Fatalf("parse fixed amount: %v", err)
|
|
}
|
|
if got, want := amount.String(), "0.123456789"; got != want {
|
|
t.Fatalf("amount.String()=%q, want %q", got, want)
|
|
}
|
|
|
|
price := mustFixedAmount(t, "0.000000001")
|
|
product, err := multiplyFixedAmountByInt(price, 3)
|
|
if err != nil {
|
|
t.Fatalf("multiply fixed amount: %v", err)
|
|
}
|
|
if got, want := product.String(), "0.000000003"; got != want {
|
|
t.Fatalf("nano amount multiplication=%q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFixedAmountOperationsRejectOverflow(t *testing.T) {
|
|
maximum := fixedAmount(math.MaxInt64)
|
|
if _, err := multiplyFixedAmountByInt(maximum, 2); !errors.Is(err, errFixedAmountOverflow) {
|
|
t.Fatalf("multiply overflow error=%v", err)
|
|
}
|
|
if _, err := addFixedAmounts(maximum, 1); !errors.Is(err, errFixedAmountOverflow) {
|
|
t.Fatalf("add overflow error=%v", err)
|
|
}
|
|
pricing := resolvedPricing{RuleSetID: "overflow-rule"}
|
|
if _, err := pricing.calculate("image", maximum, []int{2}); !isPricingUnavailable(err) {
|
|
t.Fatalf("pricing overflow should be unavailable: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEstimatedOutputTokensUsesAliasesAndCapabilityFallback(t *testing.T) {
|
|
candidate := store.RuntimeModelCandidate{
|
|
ModelType: "text_generate",
|
|
Capabilities: map[string]any{
|
|
"text_generate": map[string]any{"max_output_tokens": 8192},
|
|
},
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
body map[string]any
|
|
want int
|
|
}{
|
|
{name: "max completion has highest priority", body: map[string]any{"max_completion_tokens": 700, "max_output_tokens": 600, "max_tokens": 500}, want: 700},
|
|
{name: "max output precedes legacy max tokens", body: map[string]any{"max_output_tokens": 600, "max_tokens": 500}, want: 600},
|
|
{name: "legacy max tokens", body: map[string]any{"max_tokens": 500}, want: 500},
|
|
{name: "model capability fallback", body: map[string]any{}, want: 8192},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if got := estimatedOutputTokens(test.body, candidate); got != test.want {
|
|
t.Fatalf("estimatedOutputTokens()=%d, want %d", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
if got := estimatedOutputTokens(nil, store.RuntimeModelCandidate{}); got != 4096 {
|
|
t.Fatalf("missing capability fallback=%d, want 4096", got)
|
|
}
|
|
}
|
|
|
|
func TestBuildEstimateResultUsesPreferredTotalAndMaximumReservation(t *testing.T) {
|
|
result, err := buildEstimateResult([]candidateEstimate{
|
|
{Items: []any{map[string]any{"amount": 1.25, "currency": "resource"}}, Amount: mustFixedAmount(t, "1.25")},
|
|
{Items: []any{map[string]any{"amount": 2.75, "currency": "resource"}}, Amount: mustFixedAmount(t, "2.75")},
|
|
}, "fingerprint")
|
|
if err != nil {
|
|
t.Fatalf("build estimate result: %v", err)
|
|
}
|
|
if result.TotalAmount != 1.25 {
|
|
t.Fatalf("preferred total=%v, want 1.25", result.TotalAmount)
|
|
}
|
|
if result.ReservationAmount != 2.75 {
|
|
t.Fatalf("reservation=%v, want 2.75", result.ReservationAmount)
|
|
}
|
|
if result.CandidateCount != 2 || result.PricingVersion != pricingVersionV2 || result.RequestFingerprint != "fingerprint" {
|
|
t.Fatalf("unexpected estimate metadata: %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestPricingAvailabilityRequiresExplicitFree(t *testing.T) {
|
|
paid := resolvedPricing{Config: map[string]any{"imageBase": 1.5}, Currency: "resource"}
|
|
if _, err := paid.requiredPrice("image", "imageBase", "basePrice"); err != nil {
|
|
t.Fatalf("positive price should be available: %v", err)
|
|
}
|
|
|
|
missing := resolvedPricing{Config: map[string]any{}, Currency: "resource"}
|
|
if _, err := missing.requiredPrice("image", "imageBase", "basePrice"); !isPricingUnavailable(err) {
|
|
t.Fatalf("missing price should be unavailable, got %v", err)
|
|
}
|
|
|
|
ordinaryZero := resolvedPricing{Config: map[string]any{"imageBase": 0}, Currency: "resource"}
|
|
if _, err := ordinaryZero.requiredPrice("image", "imageBase", "basePrice"); !isPricingUnavailable(err) {
|
|
t.Fatalf("ordinary zero should be unavailable, got %v", err)
|
|
}
|
|
|
|
explicitFree := resolvedPricing{
|
|
Config: map[string]any{"imageBase": 0},
|
|
Currency: "resource",
|
|
FreeResource: map[string]bool{"image": true},
|
|
}
|
|
price, err := explicitFree.requiredPrice("image", "imageBase", "basePrice")
|
|
if err != nil || !price.IsZero() {
|
|
t.Fatalf("explicit free should resolve to zero: price=%s err=%v", price.String(), err)
|
|
}
|
|
}
|
|
|
|
func TestPricingWeightsUseFixedPrecisionAndRejectInvalidValues(t *testing.T) {
|
|
pricing := resolvedPricing{Config: map[string]any{
|
|
"image": map[string]any{
|
|
"dynamicWeight": map[string]any{
|
|
"qualityFactors": map[string]any{"high": "1.123456789", "broken": 0},
|
|
},
|
|
},
|
|
}}
|
|
weight, err := pricing.weight("image", "qualityWeights", "high")
|
|
if err != nil || weight.String() != "1.123456789" {
|
|
t.Fatalf("exact weight=%s err=%v", weight.String(), err)
|
|
}
|
|
if _, err := pricing.weight("image", "qualityWeights", "broken"); !isPricingUnavailable(err) {
|
|
t.Fatalf("invalid configured weight should make pricing unavailable: %v", err)
|
|
}
|
|
defaultWeight, err := pricing.weight("image", "qualityWeights", "unconfigured")
|
|
if err != nil || defaultWeight.String() != "1.000000000" {
|
|
t.Fatalf("default weight=%s err=%v", defaultWeight.String(), err)
|
|
}
|
|
}
|
|
|
|
func mustFixedAmount(t *testing.T, value string) fixedAmount {
|
|
t.Helper()
|
|
amount, err := parseFixedAmount(value)
|
|
if err != nil {
|
|
t.Fatalf("parse %q: %v", value, err)
|
|
}
|
|
return amount
|
|
}
|