91 lines
5.0 KiB
Go
91 lines
5.0 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"infinite-canvas/backend/internal/model"
|
|
)
|
|
|
|
func TestArkVideoUsageRequiresValidFinalCompletionTokens(t *testing.T) {
|
|
for _, path := range []string{"/api/v3/contents/generations/tasks/video-1", "/api/plan/v3/contents/generations/tasks/video-1"} {
|
|
for _, test := range []struct {
|
|
name string
|
|
payload string
|
|
want int64
|
|
}{
|
|
{name: "completion", payload: `{"status":"succeeded","usage":{"completion_tokens":108900}}`, want: 108900},
|
|
{name: "completion precedes output and total", payload: `{"usage":{"completion_tokens":108900,"output_tokens":900000,"total_tokens":200000}}`, want: 108900},
|
|
{name: "total when completion missing", payload: `{"usage":{"total_tokens":35800}}`, want: 35800},
|
|
{name: "total precedes output alias", payload: `{"usage":{"output_tokens":900000,"total_tokens":35800}}`, want: 35800},
|
|
{name: "nested response", payload: `{"data":{"status":"succeeded","usage":{"completion_tokens":35800}}}`, want: 35800},
|
|
{name: "integer beyond float precision", payload: `{"usage":{"completion_tokens":9007199254740993}}`, want: 9007199254740993},
|
|
{name: "largest int64", payload: `{"usage":{"completion_tokens":9223372036854775807}}`, want: 9223372036854775807},
|
|
{name: "processing", payload: `{"status":"running","usage":{"completion_tokens":108900}}`},
|
|
{name: "queued", payload: `{"status":"queued","usage":{"completion_tokens":108900}}`},
|
|
{name: "failed", payload: `{"status":"failed","usage":{"completion_tokens":108900}}`},
|
|
{name: "invalid status", payload: `{"status":true,"usage":{"completion_tokens":108900}}`},
|
|
{name: "missing usage", payload: `{"status":"succeeded"}`},
|
|
{name: "empty usage", payload: `{"status":"succeeded","usage":{}}`},
|
|
{name: "output alias only", payload: `{"usage":{"output_tokens":108900}}`},
|
|
{name: "unrelated usage metadata", payload: `{"usageMetadata":{"promptTokenCount":1,"candidatesTokenCount":108900,"cachedContentTokenCount":1}}`},
|
|
} {
|
|
t.Run(path+"/"+test.name, func(t *testing.T) {
|
|
log := &model.ApiCallLog{Capability: "video", Path: path, InputTokens: 1, CachedTokens: 1, OutputTokens: 1, UsageAvailable: true}
|
|
(&Service{}).EnrichAPICallLog(log, []byte(test.payload))
|
|
if log.InputTokens != 0 || log.CachedTokens != 0 || log.OutputTokens != test.want || log.UsageAvailable != (test.want > 0) {
|
|
t.Fatalf("usage = input:%d cached:%d output:%d available:%v, want output:%d", log.InputTokens, log.CachedTokens, log.OutputTokens, log.UsageAvailable, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestArkVideoUsageDoesNotMaskMalformedCompletionWithTotal(t *testing.T) {
|
|
for _, invalid := range []string{"0", "-1", "1.5", "1.0", `"108900"`, "9223372036854775808", "1e100", "null", "true", "{}"} {
|
|
for _, field := range []string{"completion_tokens", "total_tokens"} {
|
|
t.Run(field+"/"+invalid, func(t *testing.T) {
|
|
usage := fmt.Sprintf(`{"%s":%s}`, field, invalid)
|
|
if field == "completion_tokens" {
|
|
usage = fmt.Sprintf(`{"completion_tokens":%s,"total_tokens":108900}`, invalid)
|
|
}
|
|
log := &model.ApiCallLog{Capability: "video", Path: "/api/v3/contents/generations/tasks/video-1"}
|
|
(&Service{}).EnrichAPICallLog(log, []byte(`{"status":"succeeded","usage":`+usage+`}`))
|
|
if log.UsageAvailable || log.OutputTokens != 0 {
|
|
t.Fatalf("accepted invalid usage %s: output:%d available:%v", usage, log.OutputTokens, log.UsageAvailable)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestArkVideoUsageNeverAddsInputOrCachedTokens(t *testing.T) {
|
|
log := &model.ApiCallLog{Capability: "video", Path: "/api/v3/contents/generations/tasks/video-1"}
|
|
(&Service{}).EnrichAPICallLog(log, []byte(`{"status":"succeeded","usage":{"completion_tokens":108900,"input_tokens":999,"cached_tokens":50,"prompt_tokens_details":{"cached_tokens":100}},"usageMetadata":{"promptTokenCount":888,"candidatesTokenCount":777,"cachedContentTokenCount":200}}`))
|
|
if !log.UsageAvailable || log.InputTokens != 0 || log.CachedTokens != 0 || log.OutputTokens != 108900 {
|
|
t.Fatalf("video usage was contaminated by text usage: %#v", log)
|
|
}
|
|
}
|
|
|
|
func TestGenericVideoUsageRequiresPositiveFinalIntegerTokens(t *testing.T) {
|
|
for _, test := range []struct {
|
|
payload string
|
|
want int64
|
|
}{
|
|
{`{"data":{"status":"completed","usage":{"completion_tokens":108000}}}`, 108000},
|
|
{`{"status":"SUCCESS","usage":{"output_tokens":100000,"input_tokens":20}}`, 100000},
|
|
{`{"status":"succeeded","usage":{"total_tokens":90000}}`, 90000},
|
|
{`{"status":"running","usage":{"completion_tokens":108000}}`, 0},
|
|
{`{"status":"failed","usage":{"output_tokens":108000}}`, 0},
|
|
{`{"usage":{"completion_tokens":1.5,"total_tokens":108000}}`, 0},
|
|
{`{"usage":{"output_tokens":-1,"total_tokens":108000}}`, 0},
|
|
{`{"usage":{"input_tokens":108000}}`, 0},
|
|
} {
|
|
log := &model.ApiCallLog{Capability: "video", Path: "/v1/video/generations/task-1"}
|
|
(&Service{}).EnrichAPICallLog(log, []byte(test.payload))
|
|
if log.OutputTokens != test.want || log.UsageAvailable != (test.want > 0) || log.InputTokens != 0 || log.CachedTokens != 0 {
|
|
t.Fatalf("payload=%s log=%#v", test.payload, log)
|
|
}
|
|
}
|
|
}
|