2026-01-13 16:47:35 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_ToolContinuationPreservesInput(t *testing.T) {
|
2026-01-14 09:17:58 +08:00
|
|
|
|
// 续链场景:保留 item_reference 与 id,但不再强制 store=true。
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.2",
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"type": "item_reference", "id": "ref1", "text": "x"},
|
|
|
|
|
|
map[string]any{"type": "function_call_output", "call_id": "call_1", "output": "ok", "id": "o1"},
|
|
|
|
|
|
},
|
|
|
|
|
|
"tool_choice": "auto",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
2026-01-14 09:17:58 +08:00
|
|
|
|
// 未显式设置 store=true,默认为 false。
|
2026-01-13 16:47:35 +08:00
|
|
|
|
store, ok := reqBody["store"].(bool)
|
|
|
|
|
|
require.True(t, ok)
|
2026-01-14 09:17:58 +08:00
|
|
|
|
require.False(t, store)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 2)
|
|
|
|
|
|
|
2026-01-13 17:22:57 +08:00
|
|
|
|
// 校验 input[0] 为 map,避免断言失败导致测试中断。
|
|
|
|
|
|
first, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
require.Equal(t, "item_reference", first["type"])
|
2026-03-14 13:47:01 +08:00
|
|
|
|
require.Equal(t, "ref1", first["id"])
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
2026-01-13 17:22:57 +08:00
|
|
|
|
// 校验 input[1] 为 map,确保后续字段断言安全。
|
|
|
|
|
|
second, ok := input[1].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
2026-03-14 13:47:01 +08:00
|
|
|
|
require.Equal(t, "o1", second["id"])
|
|
|
|
|
|
require.Equal(t, "fc1", second["call_id"])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_ToolContinuationPreservesNativeMessageAndReasoningIDs(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.2",
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"type": "message", "id": "msg_0", "role": "user", "content": "hi"},
|
|
|
|
|
|
map[string]any{"type": "item_reference", "id": "rs_123"},
|
|
|
|
|
|
},
|
|
|
|
|
|
"tool_choice": "auto",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 2)
|
|
|
|
|
|
|
|
|
|
|
|
first, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "msg_0", first["id"])
|
|
|
|
|
|
|
|
|
|
|
|
second, ok := input[1].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "rs_123", second["id"])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_ToolContinuationNormalizesToolReferenceIDsOnly(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.2",
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"type": "item_reference", "id": "call_1"},
|
|
|
|
|
|
map[string]any{"type": "function_call_output", "call_id": "call_1", "output": "ok"},
|
|
|
|
|
|
},
|
|
|
|
|
|
"tool_choice": "auto",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 2)
|
|
|
|
|
|
|
|
|
|
|
|
first, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "fc1", first["id"])
|
|
|
|
|
|
|
|
|
|
|
|
second, ok := input[1].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "fc1", second["call_id"])
|
2026-01-13 16:47:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 09:17:58 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_ExplicitStoreFalsePreserved(t *testing.T) {
|
|
|
|
|
|
// 续链场景:显式 store=false 不再强制为 true,保持 false。
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
"store": false,
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"type": "function_call_output", "call_id": "call_1"},
|
|
|
|
|
|
},
|
|
|
|
|
|
"tool_choice": "auto",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
store, ok := reqBody["store"].(bool)
|
|
|
|
|
|
require.True(t, ok)
|
2026-01-14 09:17:58 +08:00
|
|
|
|
require.False(t, store)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 09:17:58 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_ExplicitStoreTrueForcedFalse(t *testing.T) {
|
|
|
|
|
|
// 显式 store=true 也会强制为 false。
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
"store": true,
|
2026-01-14 09:17:58 +08:00
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"type": "function_call_output", "call_id": "call_1"},
|
|
|
|
|
|
},
|
|
|
|
|
|
"tool_choice": "auto",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
2026-01-14 09:17:58 +08:00
|
|
|
|
|
|
|
|
|
|
store, ok := reqBody["store"].(bool)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.False(t, store)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_CompactForcesNonStreaming(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1-codex",
|
|
|
|
|
|
"store": true,
|
|
|
|
|
|
"stream": true,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result := applyCodexOAuthTransform(reqBody, true, true)
|
|
|
|
|
|
|
|
|
|
|
|
_, hasStore := reqBody["store"]
|
|
|
|
|
|
require.False(t, hasStore)
|
|
|
|
|
|
_, hasStream := reqBody["stream"]
|
|
|
|
|
|
require.False(t, hasStream)
|
|
|
|
|
|
require.True(t, result.Modified)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 09:17:58 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_NonContinuationDefaultsStoreFalseAndStripsIDs(t *testing.T) {
|
|
|
|
|
|
// 非续链场景:未设置 store 时默认 false,并移除 input 中的 id。
|
|
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
2026-01-13 16:47:35 +08:00
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"type": "text", "id": "t1", "text": "hi"},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
store, ok := reqBody["store"].(bool)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.False(t, store)
|
|
|
|
|
|
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 1)
|
2026-01-13 17:22:57 +08:00
|
|
|
|
// 校验 input[0] 为 map,避免类型不匹配触发 errcheck。
|
|
|
|
|
|
item, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
_, hasID := item["id"]
|
|
|
|
|
|
require.False(t, hasID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestFilterCodexInput_RemovesItemReferenceWhenNotPreserved(t *testing.T) {
|
|
|
|
|
|
input := []any{
|
|
|
|
|
|
map[string]any{"type": "item_reference", "id": "ref1"},
|
|
|
|
|
|
map[string]any{"type": "text", "id": "t1", "text": "hi"},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
filtered := filterCodexInput(input, false)
|
|
|
|
|
|
require.Len(t, filtered, 1)
|
2026-01-13 17:22:57 +08:00
|
|
|
|
// 校验 filtered[0] 为 map,确保字段检查可靠。
|
|
|
|
|
|
item, ok := filtered[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
require.Equal(t, "text", item["type"])
|
|
|
|
|
|
_, hasID := item["id"]
|
|
|
|
|
|
require.False(t, hasID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-17 11:00:07 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_NormalizeCodexTools_PreservesResponsesFunctionTools(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
"tools": []any{
|
|
|
|
|
|
map[string]any{
|
|
|
|
|
|
"type": "function",
|
|
|
|
|
|
"name": "bash",
|
|
|
|
|
|
"description": "desc",
|
|
|
|
|
|
"parameters": map[string]any{"type": "object"},
|
|
|
|
|
|
},
|
|
|
|
|
|
map[string]any{
|
|
|
|
|
|
"type": "function",
|
|
|
|
|
|
"function": nil,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
2026-01-17 11:00:07 +08:00
|
|
|
|
|
|
|
|
|
|
tools, ok := reqBody["tools"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, tools, 1)
|
|
|
|
|
|
|
|
|
|
|
|
first, ok := tools[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "function", first["type"])
|
|
|
|
|
|
require.Equal(t, "bash", first["name"])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-13 16:47:35 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_EmptyInput(t *testing.T) {
|
|
|
|
|
|
// 空 input 应保持为空且不触发异常。
|
|
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
"input": []any{},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
2026-01-13 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 07:14:46 +08:00
|
|
|
|
func TestNormalizeCodexModel_Gpt53(t *testing.T) {
|
|
|
|
|
|
cases := map[string]string{
|
2026-03-06 08:14:04 +08:00
|
|
|
|
"gpt-5.4": "gpt-5.4",
|
|
|
|
|
|
"gpt-5.4-high": "gpt-5.4",
|
|
|
|
|
|
"gpt-5.4-chat-latest": "gpt-5.4",
|
|
|
|
|
|
"gpt 5.4": "gpt-5.4",
|
2026-02-13 09:28:07 +08:00
|
|
|
|
"gpt-5.3": "gpt-5.3-codex",
|
|
|
|
|
|
"gpt-5.3-codex": "gpt-5.3-codex",
|
|
|
|
|
|
"gpt-5.3-codex-xhigh": "gpt-5.3-codex",
|
|
|
|
|
|
"gpt-5.3-codex-spark": "gpt-5.3-codex",
|
|
|
|
|
|
"gpt-5.3-codex-spark-high": "gpt-5.3-codex",
|
|
|
|
|
|
"gpt-5.3-codex-spark-xhigh": "gpt-5.3-codex",
|
|
|
|
|
|
"gpt 5.3 codex": "gpt-5.3-codex",
|
2026-02-06 07:14:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for input, expected := range cases {
|
|
|
|
|
|
require.Equal(t, expected, normalizeCodexModel(input))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 21:22:33 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_CodexCLI_PreservesExistingInstructions(t *testing.T) {
|
|
|
|
|
|
// Codex CLI 场景:已有 instructions 时不修改
|
|
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
"instructions": "existing instructions",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
result := applyCodexOAuthTransform(reqBody, true, false) // isCodexCLI=true
|
2026-02-03 21:22:33 +08:00
|
|
|
|
|
|
|
|
|
|
instructions, ok := reqBody["instructions"].(string)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "existing instructions", instructions)
|
|
|
|
|
|
// Modified 仍可能为 true(因为其他字段被修改),但 instructions 应保持不变
|
|
|
|
|
|
_ = result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_CodexCLI_SuppliesDefaultWhenEmpty(t *testing.T) {
|
|
|
|
|
|
// Codex CLI 场景:无 instructions 时补充默认值
|
|
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
// 没有 instructions 字段
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-06 18:50:28 +08:00
|
|
|
|
result := applyCodexOAuthTransform(reqBody, true, false) // isCodexCLI=true
|
2026-02-03 21:22:33 +08:00
|
|
|
|
|
|
|
|
|
|
instructions, ok := reqBody["instructions"].(string)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.NotEmpty(t, instructions)
|
|
|
|
|
|
require.True(t, result.Modified)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-07 13:39:47 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_NonCodexCLI_PreservesExistingInstructions(t *testing.T) {
|
|
|
|
|
|
// 非 Codex CLI 场景:已有 instructions 时保留客户端的值,不再覆盖
|
2026-02-03 21:22:33 +08:00
|
|
|
|
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
"instructions": "old instructions",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-07 13:39:47 +08:00
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false) // isCodexCLI=false
|
2026-02-03 21:22:33 +08:00
|
|
|
|
|
|
|
|
|
|
instructions, ok := reqBody["instructions"].(string)
|
|
|
|
|
|
require.True(t, ok)
|
2026-03-07 13:39:47 +08:00
|
|
|
|
require.Equal(t, "old instructions", instructions)
|
2026-02-03 21:22:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-10 23:31:54 +08:00
|
|
|
|
func TestApplyCodexOAuthTransform_StringInputConvertedToArray(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{"model": "gpt-5.4", "input": "Hello, world!"}
|
|
|
|
|
|
result := applyCodexOAuthTransform(reqBody, false, false)
|
|
|
|
|
|
require.True(t, result.Modified)
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 1)
|
|
|
|
|
|
msg, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "message", msg["type"])
|
|
|
|
|
|
require.Equal(t, "user", msg["role"])
|
|
|
|
|
|
require.Equal(t, "Hello, world!", msg["content"])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_EmptyStringInputBecomesEmptyArray(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{"model": "gpt-5.4", "input": ""}
|
|
|
|
|
|
result := applyCodexOAuthTransform(reqBody, false, false)
|
|
|
|
|
|
require.True(t, result.Modified)
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_WhitespaceStringInputBecomesEmptyArray(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{"model": "gpt-5.4", "input": " "}
|
|
|
|
|
|
result := applyCodexOAuthTransform(reqBody, false, false)
|
|
|
|
|
|
require.True(t, result.Modified)
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_StringInputWithToolsField(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.4",
|
|
|
|
|
|
"input": "Run the tests",
|
|
|
|
|
|
"tools": []any{map[string]any{"type": "function", "name": "bash"}},
|
|
|
|
|
|
}
|
|
|
|
|
|
applyCodexOAuthTransform(reqBody, false, false)
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 21:20:46 +08:00
|
|
|
|
func TestExtractSystemMessagesFromInput(t *testing.T) {
|
|
|
|
|
|
t.Run("no system messages", func(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"role": "user", "content": "hello"},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
result := extractSystemMessagesFromInput(reqBody)
|
|
|
|
|
|
require.False(t, result)
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 1)
|
|
|
|
|
|
_, hasInstructions := reqBody["instructions"]
|
|
|
|
|
|
require.False(t, hasInstructions)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("string content system message", func(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"role": "system", "content": "You are an assistant."},
|
|
|
|
|
|
map[string]any{"role": "user", "content": "hello"},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
result := extractSystemMessagesFromInput(reqBody)
|
|
|
|
|
|
require.True(t, result)
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 1)
|
|
|
|
|
|
msg, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "user", msg["role"])
|
|
|
|
|
|
require.Equal(t, "You are an assistant.", reqBody["instructions"])
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("array content system message", func(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{
|
|
|
|
|
|
"role": "system",
|
|
|
|
|
|
"content": []any{
|
|
|
|
|
|
map[string]any{"type": "text", "text": "Be helpful."},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
result := extractSystemMessagesFromInput(reqBody)
|
|
|
|
|
|
require.True(t, result)
|
|
|
|
|
|
require.Equal(t, "Be helpful.", reqBody["instructions"])
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 0)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("multiple system messages concatenated", func(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"role": "system", "content": "First."},
|
|
|
|
|
|
map[string]any{"role": "system", "content": "Second."},
|
|
|
|
|
|
map[string]any{"role": "user", "content": "hi"},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
result := extractSystemMessagesFromInput(reqBody)
|
|
|
|
|
|
require.True(t, result)
|
|
|
|
|
|
require.Equal(t, "First.\n\nSecond.", reqBody["instructions"])
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 1)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("mixed system and non-system preserves non-system", func(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"role": "user", "content": "hello"},
|
|
|
|
|
|
map[string]any{"role": "system", "content": "Sys prompt."},
|
|
|
|
|
|
map[string]any{"role": "assistant", "content": "Hi there"},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
result := extractSystemMessagesFromInput(reqBody)
|
|
|
|
|
|
require.True(t, result)
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 2)
|
|
|
|
|
|
first, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "user", first["role"])
|
|
|
|
|
|
second, ok := input[1].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "assistant", second["role"])
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("existing instructions prepended", func(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"role": "system", "content": "Extracted."},
|
|
|
|
|
|
map[string]any{"role": "user", "content": "hi"},
|
|
|
|
|
|
},
|
|
|
|
|
|
"instructions": "Existing instructions.",
|
|
|
|
|
|
}
|
|
|
|
|
|
result := extractSystemMessagesFromInput(reqBody)
|
|
|
|
|
|
require.True(t, result)
|
|
|
|
|
|
require.Equal(t, "Extracted.\n\nExisting instructions.", reqBody["instructions"])
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestApplyCodexOAuthTransform_ExtractsSystemMessages(t *testing.T) {
|
|
|
|
|
|
reqBody := map[string]any{
|
|
|
|
|
|
"model": "gpt-5.1",
|
|
|
|
|
|
"input": []any{
|
|
|
|
|
|
map[string]any{"role": "system", "content": "You are a coding assistant."},
|
|
|
|
|
|
map[string]any{"role": "user", "content": "Write a function."},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result := applyCodexOAuthTransform(reqBody, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
require.True(t, result.Modified)
|
|
|
|
|
|
|
|
|
|
|
|
input, ok := reqBody["input"].([]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Len(t, input, 1)
|
|
|
|
|
|
msg, ok := input[0].(map[string]any)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "user", msg["role"])
|
|
|
|
|
|
|
|
|
|
|
|
instructions, ok := reqBody["instructions"].(string)
|
|
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
require.Equal(t, "You are a coding assistant.", instructions)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 21:22:33 +08:00
|
|
|
|
func TestIsInstructionsEmpty(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
reqBody map[string]any
|
|
|
|
|
|
expected bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{"missing field", map[string]any{}, true},
|
|
|
|
|
|
{"nil value", map[string]any{"instructions": nil}, true},
|
|
|
|
|
|
{"empty string", map[string]any{"instructions": ""}, true},
|
|
|
|
|
|
{"whitespace only", map[string]any{"instructions": " "}, true},
|
|
|
|
|
|
{"non-string", map[string]any{"instructions": 123}, true},
|
|
|
|
|
|
{"valid string", map[string]any{"instructions": "hello"}, false},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
result := isInstructionsEmpty(tt.reqBody)
|
|
|
|
|
|
require.Equal(t, tt.expected, result)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|