package service import ( "encoding/json" "strings" "testing" "oci-portal/internal/aiwire" ) func respReq(t *testing.T, raw string) aiwire.RespRequest { t.Helper() var req aiwire.RespRequest if err := json.Unmarshal([]byte(raw), &req); err != nil { t.Fatalf("unmarshal req: %v", err) } return req } func deref(p *int) int { if p == nil { return 0 } return *p } func TestRespPassthroughValidate(t *testing.T) { prev := "resp_1" bg := true tests := []struct { name string req aiwire.RespRequest wantErr bool }{ {"合法", aiwire.RespRequest{Model: "m", Tools: []aiwire.RespTool{{Type: "web_search"}}}, false}, {"混用function", aiwire.RespRequest{Model: "m", Tools: []aiwire.RespTool{{Type: "web_search"}, {Type: "function", Name: "f"}}}, false}, {"缺model", aiwire.RespRequest{Tools: []aiwire.RespTool{{Type: "web_search"}}}, true}, {"服务端工具流式放行", aiwire.RespRequest{Model: "m", Stream: true, Tools: []aiwire.RespTool{{Type: "web_search"}}}, false}, {"无工具流式放行", aiwire.RespRequest{Model: "m", Stream: true}, false}, {"function工具流式放行", aiwire.RespRequest{Model: "m", Stream: true, Tools: []aiwire.RespTool{{Type: "function", Name: "f"}}}, false}, {"code_interpreter放行", aiwire.RespRequest{Model: "m", Tools: []aiwire.RespTool{{Type: "code_interpreter"}}}, false}, {"mcp流式放行", aiwire.RespRequest{Model: "m", Stream: true, Tools: []aiwire.RespTool{{Type: "mcp"}}}, false}, {"有状态拒绝", aiwire.RespRequest{Model: "m", PreviousResponseID: prev}, true}, {"background拒绝", aiwire.RespRequest{Model: "m", Background: &bg}, true}, {"未知工具拒绝", aiwire.RespRequest{Model: "m", Tools: []aiwire.RespTool{{Type: "web_search"}, {Type: "file_search"}}}, true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { err := RespPassthroughValidate(test.req) if (err != nil) != test.wantErr { t.Fatalf("err = %v, wantErr %v", err, test.wantErr) } }) } } func TestRespPassthroughBody(t *testing.T) { raw := []byte(`{"model":"m","input":"hi","stream":true,"store":true,"max_output_tokens":128,"custom_field":{"a":1.5}}`) out, err := RespPassthroughBody(raw) if err != nil { t.Fatalf("RespPassthroughBody: %v", err) } var body map[string]any if err := json.Unmarshal(out, &body); err != nil { t.Fatalf("unmarshal out: %v", err) } if body["store"] != false { t.Errorf("store 应强制 false, got %v", body["store"]) } if body["stream"] != true { t.Errorf("stream 应原样保留, got %v", body["stream"]) } if string(out) == "" || !strings.Contains(string(out), `"max_output_tokens":128`) { t.Errorf("数值字段应保真: %s", out) } if !strings.Contains(string(out), `"custom_field"`) { t.Errorf("未知字段应保留: %s", out) } if _, err := RespPassthroughBody([]byte("not-json")); err == nil { t.Error("非法 JSON 应报错") } } func TestRespPassthroughUsage(t *testing.T) { tests := []struct { name string payload string wantNil bool prompt, cached int }{ {"完整", `{"usage":{"input_tokens":9,"output_tokens":3,"total_tokens":12,"input_tokens_details":{"cached_tokens":5}}}`, false, 9, 5}, {"无细分", `{"usage":{"input_tokens":9,"output_tokens":3,"total_tokens":12}}`, false, 9, 0}, {"无usage", `{"id":"resp_1"}`, true, 0, 0}, {"非法JSON", `xx`, true, 0, 0}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { usage := RespPassthroughUsage([]byte(test.payload)) if (usage == nil) != test.wantNil { t.Fatalf("usage = %v, wantNil %v", usage, test.wantNil) } if usage == nil { return } if usage.PromptTokens != test.prompt || usage.CachedTokens() != test.cached { t.Fatalf("prompt=%d cached=%d, want %d/%d", usage.PromptTokens, usage.CachedTokens(), test.prompt, test.cached) } }) } } // TestRespStreamCompletedUsage 断言流式 usage 只从 completed 事件提取。 func TestRespStreamCompletedUsage(t *testing.T) { completed := []byte(`{"type":"response.completed","response":{"usage":{"input_tokens":10,"output_tokens":5,"total_tokens":15,"input_tokens_details":{"cached_tokens":4}}}}`) if u := RespStreamCompletedUsage(completed); u == nil || u.PromptTokens != 10 || u.CompletionTokens != 5 || u.PromptTokensDetails == nil || u.PromptTokensDetails.CachedTokens != 4 { t.Fatalf("completed 事件 usage 解析失败: %+v", u) } for _, data := range []string{ `{"type":"response.output_text.delta","delta":"hi"}`, `{"type":"response.completed"}`, `not-json`, } { if u := RespStreamCompletedUsage([]byte(data)); u != nil { t.Fatalf("非 completed 或缺 response 应返回 nil: %s", data) } } }