package main import ( "encoding/json" "testing" ) func TestFilterEnv(t *testing.T) { env := []string{ "PATH=/usr/bin", "HOME=/root", "CLAUDECODE=1", "OTHER=value", } filtered := filterEnv(env, "CLAUDECODE") if len(filtered) != 3 { t.Fatalf("got %d entries, want 3", len(filtered)) } for _, e := range filtered { if e == "CLAUDECODE=1" { t.Error("CLAUDECODE should be filtered out") } } } func TestFilterEnvMulti(t *testing.T) { env := []string{ "PATH=/usr/bin", "HOME=/root", "CLAUDECODE=1", "CLAUDE_CODE_ENTRYPOINT=cli", "OTHER=value", } filtered := filterEnvMulti(env, []string{"CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT"}) if len(filtered) != 3 { t.Fatalf("got %d entries, want 3", len(filtered)) } for _, e := range filtered { if e == "CLAUDECODE=1" || e == "CLAUDE_CODE_ENTRYPOINT=cli" { t.Errorf("should be filtered: %s", e) } } } func TestFilterEnvNotPresent(t *testing.T) { env := []string{"PATH=/usr/bin", "HOME=/root"} filtered := filterEnv(env, "CLAUDECODE") if len(filtered) != 2 { t.Fatalf("got %d entries, want 2", len(filtered)) } } func TestTruncate(t *testing.T) { tests := []struct { input string n int want string }{ {"hello", 10, "hello"}, {"hello world", 5, "hello..."}, {"", 5, ""}, } for _, tt := range tests { got := truncate(tt.input, tt.n) if got != tt.want { t.Errorf("truncate(%q, %d) = %q, want %q", tt.input, tt.n, got, tt.want) } } } func TestCLIEventParsing(t *testing.T) { t.Run("system init", func(t *testing.T) { raw := `{"type":"system","subtype":"init","session_id":"abc-123","cwd":"/"}` var event CLIEvent if err := json.Unmarshal([]byte(raw), &event); err != nil { t.Fatalf("unmarshal: %v", err) } if event.Type != "system" { t.Errorf("type = %q", event.Type) } if event.Subtype != "init" { t.Errorf("subtype = %q", event.Subtype) } if event.SessionID != "abc-123" { t.Errorf("session_id = %q", event.SessionID) } }) t.Run("assistant message", func(t *testing.T) { raw := `{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"Hello!"}]}}` var event CLIEvent if err := json.Unmarshal([]byte(raw), &event); err != nil { t.Fatalf("unmarshal: %v", err) } if event.Type != "assistant" { t.Errorf("type = %q", event.Type) } if event.Message == nil { t.Fatal("message is nil") } if len(event.Message.Content) != 1 { t.Fatalf("content length = %d", len(event.Message.Content)) } if event.Message.Content[0].Text != "Hello!" { t.Errorf("text = %q", event.Message.Content[0].Text) } }) t.Run("stream_event content_block_delta", func(t *testing.T) { raw := `{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"chunk"}}}` var event CLIEvent if err := json.Unmarshal([]byte(raw), &event); err != nil { t.Fatalf("unmarshal: %v", err) } if event.Type != "stream_event" { t.Errorf("type = %q", event.Type) } if event.Event == nil { t.Fatal("event is nil") } if event.Event.Type != "content_block_delta" { t.Errorf("event.type = %q", event.Event.Type) } if event.Event.Delta == nil { t.Fatal("delta is nil") } if event.Event.Delta.Text != "chunk" { t.Errorf("text = %q", event.Event.Delta.Text) } }) t.Run("stream_event content_block_start", func(t *testing.T) { raw := `{"type":"stream_event","event":{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}}` var event CLIEvent if err := json.Unmarshal([]byte(raw), &event); err != nil { t.Fatalf("unmarshal: %v", err) } if event.Event.Type != "content_block_start" { t.Errorf("event.type = %q", event.Event.Type) } }) t.Run("result event object", func(t *testing.T) { raw := `{"type":"result","subtype":"success","result":{"duration_ms":1234.5,"num_turns":3,"total_cost_usd":0.05,"session_id":"abc123"}}` var event CLIEvent if err := json.Unmarshal([]byte(raw), &event); err != nil { t.Fatalf("unmarshal: %v", err) } // Simulate readOutput parsing of RawResult if len(event.RawResult) > 0 { var result CLIResult if err := json.Unmarshal(event.RawResult, &result); err == nil { event.Result = &result } } if event.Type != "result" { t.Errorf("type = %q", event.Type) } if event.Subtype != "success" { t.Errorf("subtype = %q", event.Subtype) } if event.Result == nil { t.Fatal("result is nil") } if event.Result.SessionID != "abc123" { t.Errorf("session_id = %q", event.Result.SessionID) } }) t.Run("result event string", func(t *testing.T) { raw := `{"type":"result","subtype":"success","total_cost_usd":0.046,"result":"hello world"}` var event CLIEvent if err := json.Unmarshal([]byte(raw), &event); err != nil { t.Fatalf("unmarshal: %v", err) } // Simulate readOutput parsing — string result won't parse as CLIResult if len(event.RawResult) > 0 { var result CLIResult if err := json.Unmarshal(event.RawResult, &result); err == nil { event.Result = &result } if event.Result == nil && event.TotalCostUSD > 0 { event.Result = &CLIResult{CostUSD: event.TotalCostUSD} } } if event.Result == nil { t.Fatal("result should not be nil for string result with cost") } if event.Result.CostUSD != 0.046 { t.Errorf("cost = %f, want 0.046", event.Result.CostUSD) } }) t.Run("tool_use content", func(t *testing.T) { raw := `{"type":"assistant","message":{"role":"assistant","content":[{"type":"tool_use","name":"Read","id":"tool1","input":{"file_path":"/tmp/test.go"}}]}}` var event CLIEvent if err := json.Unmarshal([]byte(raw), &event); err != nil { t.Fatalf("unmarshal: %v", err) } if event.Message.Content[0].Type != "tool_use" { t.Errorf("content type = %q", event.Message.Content[0].Type) } if event.Message.Content[0].Name != "Read" { t.Errorf("name = %q", event.Message.Content[0].Name) } }) } func TestCLIInputMessage(t *testing.T) { msg := CLIInputMessage{ Type: "user", Message: CLIInputContent{ Role: "user", Content: "hello world", }, } data, err := json.Marshal(msg) if err != nil { t.Fatalf("marshal: %v", err) } expected := `{"type":"user","message":{"role":"user","content":"hello world"}}` if string(data) != expected { t.Errorf("got %s, want %s", string(data), expected) } }