Fix sandbox integration tests for claude-proxy architecture

- Update TestClaudeCommandBuilding to expect new command format
- Rename TestClaudeCCRConfigBuilding to TestClaudeProxyConfigBuilding
- Update assertions for claude-proxy env vars instead of CCR
- Apply gofmt formatting to proxy/types.go

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Max 2026-01-31 14:36:01 +08:00
parent 4e88c7ae78
commit 019b606b13
2 changed files with 62 additions and 60 deletions

View file

@ -78,28 +78,30 @@ func TestClaudeCommandBuilding(t *testing.T) {
require.NoError(t, err)
// Verify command structure
// Command is now: ["bash", "-c", "nohup ccr start ... && ccr code ..."]
// Command is now: ["bash", "-c", "cat << 'INPUTEOF' | claude -p ... INPUTEOF"]
assert.NotEmpty(t, cmd)
assert.Equal(t, "bash", cmd[0], "Command should start with bash")
assert.Equal(t, "-c", cmd[1], "Second arg should be -c")
assert.Contains(t, cmd[2], "ccr code", "Bash command should contain ccr code")
assert.Contains(t, cmd[2], "claude -p", "Bash command should contain claude -p")
assert.Contains(t, cmd[2], "--permission-mode", "Should include permission mode")
assert.Contains(t, cmd[2], "--input-format", "Should include input-format flag")
assert.Contains(t, cmd[2], "--output-format", "Should include output-format flag")
assert.Contains(t, cmd[2], "--verbose", "Should include verbose flag")
assert.Contains(t, cmd[2], "stream-json", "Should use stream-json format")
assert.Contains(t, cmd[2], "INPUTEOF", "Should use heredoc for input")
t.Logf("Built command: %v", cmd)
// Verify environment variables
// Verify environment variables (claude-proxy mode)
assert.NotEmpty(t, env)
assert.Equal(t, "https://ark.cn-beijing.volces.com/api/v3", env["CCR_API_BASE"])
assert.Equal(t, "test-api-key", env["CCR_API_KEY"])
assert.Equal(t, "ep-xxxxx", env["CCR_MODEL"])
assert.Equal(t, "http://127.0.0.1:3456", env["ANTHROPIC_BASE_URL"], "Should set proxy base URL")
assert.Equal(t, "dummy", env["ANTHROPIC_API_KEY"], "Should set dummy API key for proxy")
assert.Equal(t, "10", env["CLAUDE_MAX_TURNS"])
assert.Equal(t, "acceptEdits", env["CLAUDE_PERMISSION_MODE"])
assert.Equal(t, "stream-json", env["CLAUDE_OUTPUT_FORMAT"])
assert.Contains(t, env["CLAUDE_SYSTEM_PROMPT"], "You are a helpful coding assistant")
t.Logf("Built environment: %v", env)
}
// TestClaudeCCRConfigBuilding tests that CCR config is correctly built
func TestClaudeCCRConfigBuilding(t *testing.T) {
// TestClaudeProxyConfigBuilding tests that claude-proxy config is correctly built
func TestClaudeProxyConfigBuilding(t *testing.T) {
test.Prepare(t, config.Conf)
defer test.Clean()
@ -109,20 +111,20 @@ func TestClaudeCCRConfigBuilding(t *testing.T) {
Model: "ep-xxxxx",
}
configJSON, err := claude.BuildCCRConfig(opts)
configJSON, err := claude.BuildProxyConfig(opts)
require.NoError(t, err)
require.NotEmpty(t, configJSON)
t.Logf("CCR config: %s", string(configJSON))
t.Logf("Proxy config: %s", string(configJSON))
// Verify the JSON contains expected fields (CCR uses snake_case)
assert.Contains(t, string(configJSON), "api_base_url")
// Verify the JSON contains expected fields for claude-proxy
assert.Contains(t, string(configJSON), "backend")
assert.Contains(t, string(configJSON), "api_key")
assert.Contains(t, string(configJSON), "models")
// Verify CCR format fields
assert.Contains(t, string(configJSON), "Providers")
assert.Contains(t, string(configJSON), "Router")
assert.Contains(t, string(configJSON), "volcengine")
assert.Contains(t, string(configJSON), "model")
assert.Contains(t, string(configJSON), "test-api-key")
assert.Contains(t, string(configJSON), "ep-xxxxx")
// Backend URL should end with /chat/completions
assert.Contains(t, string(configJSON), "/chat/completions")
}
// TestDefaultImageSelection tests that default images are correctly selected

View file

@ -6,18 +6,18 @@ package proxy
// AnthropicRequest represents a request to the Anthropic Messages API
type AnthropicRequest struct {
Model string `json:"model"`
Messages []AnthropicMsg `json:"messages"`
System interface{} `json:"system,omitempty"` // string or []SystemBlock
MaxTokens int `json:"max_tokens"`
Stream bool `json:"stream,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
TopK *int `json:"top_k,omitempty"`
StopSequences []string `json:"stop_sequences,omitempty"`
Tools []AnthropicTool `json:"tools,omitempty"`
Model string `json:"model"`
Messages []AnthropicMsg `json:"messages"`
System interface{} `json:"system,omitempty"` // string or []SystemBlock
MaxTokens int `json:"max_tokens"`
Stream bool `json:"stream,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
TopK *int `json:"top_k,omitempty"`
StopSequences []string `json:"stop_sequences,omitempty"`
Tools []AnthropicTool `json:"tools,omitempty"`
ToolChoice *AnthropicToolChoice `json:"tool_choice,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// AnthropicMsg represents a message in Anthropic format
@ -42,14 +42,14 @@ type ContentBlock struct {
Input interface{} `json:"input,omitempty"`
// For tool_result blocks
ToolUseID string `json:"tool_use_id,omitempty"`
ToolUseID string `json:"tool_use_id,omitempty"`
Content interface{} `json:"content,omitempty"` // string or []ContentBlock
IsError bool `json:"is_error,omitempty"`
IsError bool `json:"is_error,omitempty"`
}
// ImageSource represents an image source in Anthropic format
type ImageSource struct {
Type string `json:"type"` // "base64" or "url"
Type string `json:"type"` // "base64" or "url"
MediaType string `json:"media_type,omitempty"` // e.g., "image/jpeg"
Data string `json:"data,omitempty"` // base64 encoded data
URL string `json:"url,omitempty"` // URL for url type
@ -94,12 +94,12 @@ type Usage struct {
// AnthropicStreamEvent represents an SSE event in Anthropic format
type AnthropicStreamEvent struct {
Type string `json:"type"`
Index int `json:"index,omitempty"`
Type string `json:"type"`
Index int `json:"index,omitempty"`
Message *AnthropicResponse `json:"message,omitempty"`
ContentBlock *ContentBlock `json:"content_block,omitempty"`
Delta *DeltaContent `json:"delta,omitempty"`
Usage *Usage `json:"usage,omitempty"`
ContentBlock *ContentBlock `json:"content_block,omitempty"`
Delta *DeltaContent `json:"delta,omitempty"`
Usage *Usage `json:"usage,omitempty"`
}
// DeltaContent represents delta content in streaming
@ -116,16 +116,16 @@ type DeltaContent struct {
// OpenAIRequest represents a request to OpenAI Chat Completions API
type OpenAIRequest struct {
Model string `json:"model"`
Messages []OpenAIMsg `json:"messages"`
MaxTokens int `json:"max_tokens,omitempty"`
Stream bool `json:"stream,omitempty"`
StreamOptions *StreamOptions `json:"stream_options,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
Stop []string `json:"stop,omitempty"`
Tools []OpenAITool `json:"tools,omitempty"`
ToolChoice interface{} `json:"tool_choice,omitempty"` // "auto", "none", "required", or object
Model string `json:"model"`
Messages []OpenAIMsg `json:"messages"`
MaxTokens int `json:"max_tokens,omitempty"`
Stream bool `json:"stream,omitempty"`
StreamOptions *StreamOptions `json:"stream_options,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
Stop []string `json:"stop,omitempty"`
Tools []OpenAITool `json:"tools,omitempty"`
ToolChoice interface{} `json:"tool_choice,omitempty"` // "auto", "none", "required", or object
}
// StreamOptions represents stream options in OpenAI format
@ -144,8 +144,8 @@ type OpenAIMsg struct {
// OpenAIContent represents content in OpenAI messages (for multimodal)
type OpenAIContent struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
Type string `json:"type"`
Text string `json:"text,omitempty"`
ImageURL *OpenAIImageURL `json:"image_url,omitempty"`
}
@ -170,10 +170,10 @@ type OpenAIFunction struct {
// OpenAIToolCall represents a tool call in OpenAI format
type OpenAIToolCall struct {
ID string `json:"id"`
Type string `json:"type"`
Function OpenAIFunctionCall `json:"function"`
Index *int `json:"index,omitempty"` // For streaming
ID string `json:"id"`
Type string `json:"type"`
Function OpenAIFunctionCall `json:"function"`
Index *int `json:"index,omitempty"` // For streaming
}
// OpenAIFunctionCall represents a function call in OpenAI format
@ -194,9 +194,9 @@ type OpenAIResponse struct {
// OpenAIChoice represents a choice in OpenAI response
type OpenAIChoice struct {
Index int `json:"index"`
Message OpenAIMsg `json:"message"`
FinishReason string `json:"finish_reason"`
Index int `json:"index"`
Message OpenAIMsg `json:"message"`
FinishReason string `json:"finish_reason"`
}
// OpenAIUsage represents usage statistics in OpenAI format
@ -218,9 +218,9 @@ type OpenAIStreamChunk struct {
// OpenAIStreamChoice represents a choice in OpenAI streaming response
type OpenAIStreamChoice struct {
Index int `json:"index"`
Delta OpenAIStreamDelta `json:"delta"`
FinishReason string `json:"finish_reason,omitempty"`
Index int `json:"index"`
Delta OpenAIStreamDelta `json:"delta"`
FinishReason string `json:"finish_reason,omitempty"`
}
// OpenAIStreamDelta represents delta content in OpenAI streaming