若干更新迭代
This commit is contained in:
1 parent
deb9cc5bcf
commit
4ac786aafb
64 files changed
+3251
-291
No files matched your search
@@ -124,6 +124,104 @@ func TestSettlementProcessorSettlesSuccessfulMinimaxH3Usage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettlementProcessorDistinguishesMissingMinimaxImageUsageFromExplicitZero(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name, usageJSON, billingJSON, requestJSON string
|
||||
wantAmount, wantDelta int64
|
||||
wantImageCount float64
|
||||
}{
|
||||
{
|
||||
name: "missing usage falls back to quoted count", usageJSON: `{"outputSeconds":5}`,
|
||||
billingJSON: `{"status":"charged","amountFen":576,"standardUnitPriceFen":80,"markupMultiplier":1.2,"settlementStatus":"pending","parameters":{"resolution":"2k","inputImageCount":9}}`,
|
||||
wantAmount: 576, wantImageCount: 9,
|
||||
},
|
||||
{
|
||||
name: "missing usage falls back to request materials", usageJSON: `{"outputSeconds":5}`,
|
||||
billingJSON: `{"status":"charged","amountFen":576,"standardUnitPriceFen":80,"markupMultiplier":1.2,"settlementStatus":"pending","parameters":{"resolution":"2k"}}`,
|
||||
requestJSON: `{"materials":[{"type":"image"},{"type":"image"},{"type":"image"},{"type":"image"},{"type":"image"},{"type":"image"},{"type":"image"},{"type":"image"},{"type":"image"}]}`,
|
||||
wantAmount: 576, wantImageCount: 9,
|
||||
},
|
||||
{
|
||||
name: "explicit zero overrides quote", usageJSON: `{"outputSeconds":5,"inputImageCount":0}`,
|
||||
billingJSON: `{"status":"charged","amountFen":576,"standardUnitPriceFen":80,"markupMultiplier":1.2,"settlementStatus":"pending","parameters":{"resolution":"2k","inputImageCount":9}}`,
|
||||
wantAmount: 480, wantDelta: -96, wantImageCount: 0,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
state := &settlementStateStub{}
|
||||
ledger := &settlementLedgerStub{}
|
||||
job := jobs.Job{
|
||||
ID: "minimax-multiref", Provider: "minimax", ReqKey: billing.MinimaxH3Model, Capability: "video.generate", Status: jobs.StatusSucceeded,
|
||||
ResponsePayload: json.RawMessage(`{"usage":` + test.usageJSON + `}`),
|
||||
RequestPayload: json.RawMessage(test.requestJSON), Billing: json.RawMessage(test.billingJSON),
|
||||
UsageContext: json.RawMessage(`{"organizationId":"org-1","accountId":"account-1"}`),
|
||||
}
|
||||
got, err := NewSettlementProcessor(settlementProcessorStub{job: job}, ledger, state, time.Now).Advance(context.Background(), jobs.Job{ID: job.ID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var snapshot map[string]any
|
||||
_ = json.Unmarshal(got.Billing, &snapshot)
|
||||
usage := snapshot["providerUsage"].(map[string]any)
|
||||
if snapshot["amountFen"] != float64(test.wantAmount) || usage["inputImageCount"] != test.wantImageCount || state.calls != 1 {
|
||||
t.Fatalf("billing=%#v state.calls=%d", snapshot, state.calls)
|
||||
}
|
||||
if test.wantDelta == 0 && ledger.calls != 0 || test.wantDelta != 0 && (ledger.calls != 1 || ledger.request.DeltaFen != test.wantDelta) {
|
||||
t.Fatalf("ledger=%#v calls=%d", ledger.request, ledger.calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettlementProcessorSettlesWan30FromFractionalOutputDuration(t *testing.T) {
|
||||
state := &settlementStateStub{}
|
||||
ledger := &settlementLedgerStub{posting: billing.WalletPosting{LedgerID: "wan30-settlement"}}
|
||||
advanced := jobs.Job{
|
||||
ID: "wan30-job", Provider: "bailian", ReqKey: billing.BailianWan30VideoModel, Capability: "video.generate", Status: jobs.StatusSucceeded,
|
||||
ResponsePayload: json.RawMessage(`{"usage":{"outputMilliseconds":4201,"outputSeconds":5}}`),
|
||||
Billing: json.RawMessage(`{"status":"charged","amountFen":360,"reservedAmountFen":360,"standardUnitPriceFen":60,"markupMultiplier":1.2,"settlementStatus":"pending","parameters":{"resolution":"720p"}}`),
|
||||
UsageContext: json.RawMessage(`{"organizationId":"org-1","accountId":"account-1"}`),
|
||||
}
|
||||
processor := NewSettlementProcessor(settlementProcessorStub{job: advanced}, ledger, state, time.Now)
|
||||
got, err := processor.Advance(context.Background(), jobs.Job{ID: advanced.ID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 4.201 seconds * 60 fen * 1.2 = 302.472 fen, rounded once to 303.
|
||||
if ledger.calls != 1 || ledger.request.DeltaFen != -57 || ledger.request.Metadata["outputSeconds"] != 4.201 {
|
||||
t.Fatalf("ledger=%#v calls=%d", ledger.request, ledger.calls)
|
||||
}
|
||||
var snapshot map[string]any
|
||||
_ = json.Unmarshal(got.Billing, &snapshot)
|
||||
if snapshot["amountFen"] != float64(303) || snapshot["settlementStatus"] != "settled" || snapshot["settlementLedgerEntryId"] != "wan30-settlement" {
|
||||
t.Fatalf("billing=%#v", snapshot)
|
||||
}
|
||||
processor.inner = settlementProcessorStub{job: got}
|
||||
_, err = processor.Advance(context.Background(), got)
|
||||
if err != nil || ledger.calls != 1 || state.calls != 1 {
|
||||
t.Fatalf("repeat calls ledger=%d state=%d err=%v", ledger.calls, state.calls, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettlementProcessorKeepsWan30EstimateWhenUsageMissing(t *testing.T) {
|
||||
state := &settlementStateStub{}
|
||||
advanced := jobs.Job{
|
||||
ID: "wan30-job", Provider: "bailian", ReqKey: billing.BailianWan30VideoModel, Capability: "video.generate", Status: jobs.StatusSucceeded,
|
||||
ResponsePayload: json.RawMessage(`{"usage":{}}`),
|
||||
Billing: json.RawMessage(`{"status":"charged","amountFen":360,"standardUnitPriceFen":60,"markupMultiplier":1.2,"settlementStatus":"pending"}`),
|
||||
UsageContext: json.RawMessage(`{"organizationId":"org-1"}`),
|
||||
}
|
||||
got, err := NewSettlementProcessor(settlementProcessorStub{job: advanced}, &settlementLedgerStub{}, state, time.Now).Advance(context.Background(), jobs.Job{ID: advanced.ID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var snapshot map[string]any
|
||||
_ = json.Unmarshal(got.Billing, &snapshot)
|
||||
if snapshot["amountFen"] != float64(360) || snapshot["settlementStatus"] != "estimated" || snapshot["settlementReason"] != "provider_usage_unavailable" {
|
||||
t.Fatalf("billing=%#v", snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettlementProcessorRefundsUnusedSeedreamLayerReservation(t *testing.T) {
|
||||
state := &settlementStateStub{}
|
||||
ledger := &settlementLedgerStub{posting: billing.WalletPosting{LedgerID: "layer-settlement", CreatedAt: time.Date(2026, 8, 21, 8, 0, 0, 0, time.UTC)}}
|
||||
|
||||
Reference in new issue
Block a user