package main import ( "bufio" "encoding/json" "os" "strings" "testing" ) func TestFormatContextInfo_NilUsage(t *testing.T) { result := formatContextInfo(200000, nil) expected := "0/200k" if result != expected { t.Errorf("formatContextInfo(200000, nil) = %q, want %q", result, expected) } } func TestFormatContextInfo_WithUsage(t *testing.T) { usage := &TokenUsage{ InputTokens: 8500, CacheCreationTokens: 5000, CacheReadInputTokens: 2000, } result := formatContextInfo(200000, usage) expected := "15k/200k" if result != expected { t.Errorf("formatContextInfo(200000, usage) = %q, want %q", result, expected) } } func TestFormatContextInfo_SmallValues(t *testing.T) { usage := &TokenUsage{ InputTokens: 500, CacheCreationTokens: 0, CacheReadInputTokens: 0, } result := formatContextInfo(100000, usage) expected := "0k/100k" if result != expected { t.Errorf("formatContextInfo(100000, usage) = %q, want %q", result, expected) } } func TestStripANSI(t *testing.T) { tests := []struct { name string input string expected string }{ { name: "no ansi", input: "hello world", expected: "hello world", }, { name: "single color", input: "\033[32mgreen\033[0m", expected: "green", }, { name: "multiple colors", input: "\033[31mred\033[0m \033[32mgreen\033[0m", expected: "red green", }, { name: "bold", input: "\033[1mbold\033[0m", expected: "bold", }, { name: "complex", input: "\033[32m●\033[0m \033[35mOpus\033[0m \033[1;32m➜\033[0m", expected: "● Opus ➜", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := stripANSI(tt.input) if result != tt.expected { t.Errorf("stripANSI(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } func TestStatusInputParsing(t *testing.T) { jsonData := `{ "model": {"display_name": "Opus 4.5"}, "workspace": {"current_dir": "/root/projects/statusline"}, "context_window": { "context_window_size": 200000, "current_usage": { "input_tokens": 5000, "cache_creation_input_tokens": 1000, "cache_read_input_tokens": 500 } } }` var data StatusInput err := json.Unmarshal([]byte(jsonData), &data) if err != nil { t.Fatalf("Failed to parse JSON: %v", err) } if data.Model.DisplayName != "Opus 4.5" { t.Errorf("Model.DisplayName = %q, want %q", data.Model.DisplayName, "Opus 4.5") } if data.Workspace.CurrentDir != "/root/projects/statusline" { t.Errorf("Workspace.CurrentDir = %q, want %q", data.Workspace.CurrentDir, "/root/projects/statusline") } if data.ContextWindow.ContextWindowSize != 200000 { t.Errorf("ContextWindow.ContextWindowSize = %d, want %d", data.ContextWindow.ContextWindowSize, 200000) } if data.ContextWindow.CurrentUsage == nil { t.Fatal("ContextWindow.CurrentUsage is nil") } if data.ContextWindow.CurrentUsage.InputTokens != 5000 { t.Errorf("CurrentUsage.InputTokens = %d, want %d", data.ContextWindow.CurrentUsage.InputTokens, 5000) } } func TestStatusInputParsing_NilUsage(t *testing.T) { jsonData := `{ "model": {"display_name": "Sonnet"}, "workspace": {"current_dir": "/tmp"}, "context_window": { "context_window_size": 100000 } }` var data StatusInput err := json.Unmarshal([]byte(jsonData), &data) if err != nil { t.Fatalf("Failed to parse JSON: %v", err) } if data.ContextWindow.CurrentUsage != nil { t.Errorf("ContextWindow.CurrentUsage should be nil, got %+v", data.ContextWindow.CurrentUsage) } } func TestGetGitInfo_CurrentRepo(t *testing.T) { cwd, err := os.Getwd() if err != nil { t.Skipf("Could not get working directory: %v", err) } result := getGitInfo(cwd) // Should return something like " git:(master)" or " git:(master) ✗" if result == "" { t.Skip("Not in a git repository") } if !contains(result, "git:(") { t.Errorf("getGitInfo(%q) = %q, expected to contain 'git:('", cwd, result) } } func TestGetGitInfo_NonRepo(t *testing.T) { result := getGitInfo("/tmp") // /tmp is unlikely to be a git repo if result != "" && !contains(result, "git:(") { t.Errorf("getGitInfo(/tmp) = %q, expected empty or valid git info", result) } } func TestGetGiteaStatus(t *testing.T) { result := getGiteaStatus() // Should return either green or red dot greenDot := green + "●" + reset redDot := red + "●" + reset if result != greenDot && result != redDot { t.Errorf("getGiteaStatus() = %q, expected green or red dot", result) } } func contains(s, substr string) bool { return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsHelper(s, substr)) } func containsHelper(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false } func TestGetTerminalWidth(t *testing.T) { // getTerminalWidth should return a positive integer // In test environment (no TTY), it should fall back to 80 width := getTerminalWidth() if width <= 0 { t.Errorf("getTerminalWidth() = %d, expected positive value", width) } } func TestGetTerminalWidth_DefaultFallback(t *testing.T) { // When not connected to a terminal, should return 80 width := getTerminalWidth() // In CI/test environments, this typically returns 80 if width != 80 && width < 40 { t.Errorf("getTerminalWidth() = %d, expected 80 or reasonable terminal width", width) } } func TestGetGitInfo_InvalidPath(t *testing.T) { result := getGitInfo("/nonexistent/path/that/does/not/exist") if result != "" { t.Errorf("getGitInfo(invalid) = %q, expected empty string", result) } } func TestGetGitInfo_RootDir(t *testing.T) { // Root directory is unlikely to be a git repo result := getGitInfo("/") if result != "" && !contains(result, "git:(") { t.Errorf("getGitInfo(/) = %q, expected empty or valid git info", result) } } func TestFormatContextInfo_ZeroContextSize(t *testing.T) { result := formatContextInfo(0, nil) expected := "0/0k" if result != expected { t.Errorf("formatContextInfo(0, nil) = %q, want %q", result, expected) } } func TestFormatContextInfo_LargeValues(t *testing.T) { usage := &TokenUsage{ InputTokens: 150000, CacheCreationTokens: 25000, CacheReadInputTokens: 10000, } result := formatContextInfo(200000, usage) expected := "185k/200k" if result != expected { t.Errorf("formatContextInfo(200000, large usage) = %q, want %q", result, expected) } } func TestFormatContextInfo_ExactThousand(t *testing.T) { usage := &TokenUsage{ InputTokens: 1000, CacheCreationTokens: 0, CacheReadInputTokens: 0, } result := formatContextInfo(100000, usage) expected := "1k/100k" if result != expected { t.Errorf("formatContextInfo(100000, 1000 tokens) = %q, want %q", result, expected) } } func TestStripANSI_Empty(t *testing.T) { result := stripANSI("") if result != "" { t.Errorf("stripANSI(\"\") = %q, want empty", result) } } func TestStripANSI_OnlyANSI(t *testing.T) { result := stripANSI("\033[31m\033[0m") if result != "" { t.Errorf("stripANSI(only codes) = %q, want empty", result) } } func TestStripANSI_NestedCodes(t *testing.T) { input := "\033[1m\033[31mbold red\033[0m\033[0m" result := stripANSI(input) expected := "bold red" if result != expected { t.Errorf("stripANSI(%q) = %q, want %q", input, result, expected) } } func TestStatusInputParsing_EmptyJSON(t *testing.T) { jsonData := `{}` var data StatusInput err := json.Unmarshal([]byte(jsonData), &data) if err != nil { t.Fatalf("Failed to parse empty JSON: %v", err) } if data.Model.DisplayName != "" { t.Errorf("Expected empty DisplayName, got %q", data.Model.DisplayName) } } func TestStatusInputParsing_PartialJSON(t *testing.T) { jsonData := `{"model": {"display_name": "Test"}}` var data StatusInput err := json.Unmarshal([]byte(jsonData), &data) if err != nil { t.Fatalf("Failed to parse partial JSON: %v", err) } if data.Model.DisplayName != "Test" { t.Errorf("DisplayName = %q, want %q", data.Model.DisplayName, "Test") } if data.Workspace.CurrentDir != "" { t.Errorf("Expected empty CurrentDir, got %q", data.Workspace.CurrentDir) } } func TestStatusInputParsing_InvalidJSON(t *testing.T) { jsonData := `{invalid json}` var data StatusInput err := json.Unmarshal([]byte(jsonData), &data) if err == nil { t.Error("Expected error for invalid JSON, got nil") } } func TestANSIConstants(t *testing.T) { // Verify ANSI constants are properly defined tests := []struct { name string constant string prefix string }{ {"reset", reset, "\033["}, {"red", red, "\033["}, {"green", green, "\033["}, {"yellow", yellow, "\033["}, {"magenta", magenta, "\033["}, {"cyan", cyan, "\033["}, {"boldGreen", boldGreen, "\033["}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if !contains(tt.constant, tt.prefix) { t.Errorf("%s constant doesn't start with ANSI escape", tt.name) } }) } } func TestGetGiteaStatus_ReturnsValidColor(t *testing.T) { result := getGiteaStatus() // Must contain the dot character if !contains(result, "●") { t.Errorf("getGiteaStatus() = %q, expected to contain dot", result) } // Must contain ANSI codes stripped := stripANSI(result) if stripped != "●" { t.Errorf("stripped getGiteaStatus() = %q, expected just dot", stripped) } } func TestReadInputFromStdin(t *testing.T) { input := "line1\nline2\nline3" reader := bufio.NewReader(strings.NewReader(input)) result := readInputFromStdin(reader) expected := "line1\nline2\nline3" if result != expected { t.Errorf("readInputFromStdin = %q, want %q", result, expected) } } func TestReadInputFromStdin_Empty(t *testing.T) { reader := bufio.NewReader(strings.NewReader("")) result := readInputFromStdin(reader) if result != "" { t.Errorf("readInputFromStdin (empty) = %q, want empty", result) } } func TestParseStatusInput_Valid(t *testing.T) { jsonStr := `{"model": {"display_name": "Test"}, "workspace": {"current_dir": "/test"}}` data, err := parseStatusInput(jsonStr) if err != nil { t.Fatalf("parseStatusInput failed: %v", err) } if data.Model.DisplayName != "Test" { t.Errorf("DisplayName = %q, want Test", data.Model.DisplayName) } if data.Workspace.CurrentDir != "/test" { t.Errorf("CurrentDir = %q, want /test", data.Workspace.CurrentDir) } } func TestParseStatusInput_Invalid(t *testing.T) { _, err := parseStatusInput("invalid json") if err == nil { t.Error("parseStatusInput should fail on invalid JSON") } } func TestBuildStatusLine_ContainsComponents(t *testing.T) { data := &StatusInput{} data.Model.DisplayName = "TestModel" data.Workspace.CurrentDir = "/home/user/project" data.ContextWindow.ContextWindowSize = 100000 data.ContextWindow.CurrentUsage = &TokenUsage{ InputTokens: 5000, CacheCreationTokens: 1000, CacheReadInputTokens: 500, } left, right := buildStatusLine(data) // Check left contains model name and directory if !contains(left, "TestModel") { t.Errorf("left statusline missing model: %q", left) } if !contains(left, "project") { t.Errorf("left statusline missing directory: %q", left) } // Check right contains context info if !contains(right, "6k/100k") { t.Errorf("right statusline missing context info: %q", right) } } func TestBuildStatusLine_HasGiteaStatus(t *testing.T) { data := &StatusInput{} data.Model.DisplayName = "Model" data.Workspace.CurrentDir = "/tmp" data.ContextWindow.ContextWindowSize = 100000 left, _ := buildStatusLine(data) // Check for gitea status (dot) if !contains(left, "●") { t.Errorf("left statusline missing gitea status: %q", left) } } func TestCalculatePadding_ZeroWidth(t *testing.T) { result := calculatePadding("left", "right", 0) expected := 1 if result != expected { t.Errorf("calculatePadding(\"left\", \"right\", 0) = %d, want %d", result, expected) } } func TestCalculatePadding_NegativeResult(t *testing.T) { result := calculatePadding("left", "right", 5) expected := 1 if result != expected { t.Errorf("calculatePadding with overflow = %d, want minimum of %d", result, expected) } } func TestCalculatePadding_Normal(t *testing.T) { result := calculatePadding("left", "right", 50) expected := 50 - len("left") - len("right") if result != expected { t.Errorf("calculatePadding(\"left\", \"right\", 50) = %d, want %d", result, expected) } } func TestFormatOutput_Composition(t *testing.T) { result := formatOutput("LEFT", "RIGHT", 5) expected := "LEFT RIGHT" if result != expected { t.Errorf("formatOutput = %q, want %q", result, expected) } } func TestFormatOutput_Empty(t *testing.T) { result := formatOutput("", "", 0) expected := "" if result != expected { t.Errorf("formatOutput (empty) = %q, want %q", result, expected) } } func TestFormatOutput_WithANSI(t *testing.T) { left := red + "text" + reset right := green + "info" + reset result := formatOutput(left, right, 3) stripped := stripANSI(result) if !contains(stripped, "text") || !contains(stripped, "info") { t.Errorf("formatOutput with ANSI = %q, expected both parts visible", stripped) } } // Additional edge case tests func TestFormatContextInfo_MaxTokens(t *testing.T) { // Test when usage equals context size usage := &TokenUsage{ InputTokens: 200000, CacheCreationTokens: 0, CacheReadInputTokens: 0, } result := formatContextInfo(200000, usage) expected := "200k/200k" if result != expected { t.Errorf("formatContextInfo(max) = %q, want %q", result, expected) } } func TestFormatContextInfo_OverflowTokens(t *testing.T) { // Test when usage exceeds context size usage := &TokenUsage{ InputTokens: 250000, CacheCreationTokens: 0, CacheReadInputTokens: 0, } result := formatContextInfo(200000, usage) expected := "250k/200k" if result != expected { t.Errorf("formatContextInfo(overflow) = %q, want %q", result, expected) } } func TestFormatContextInfo_AllCacheTypes(t *testing.T) { // Test all cache token types contributing usage := &TokenUsage{ InputTokens: 10000, CacheCreationTokens: 20000, CacheReadInputTokens: 30000, } result := formatContextInfo(100000, usage) expected := "60k/100k" if result != expected { t.Errorf("formatContextInfo(all cache) = %q, want %q", result, expected) } } func TestFormatContextInfo_OnlyCacheCreation(t *testing.T) { usage := &TokenUsage{ InputTokens: 0, CacheCreationTokens: 5000, CacheReadInputTokens: 0, } result := formatContextInfo(100000, usage) expected := "5k/100k" if result != expected { t.Errorf("formatContextInfo(cache creation) = %q, want %q", result, expected) } } func TestFormatContextInfo_OnlyCacheRead(t *testing.T) { usage := &TokenUsage{ InputTokens: 0, CacheCreationTokens: 0, CacheReadInputTokens: 8000, } result := formatContextInfo(100000, usage) expected := "8k/100k" if result != expected { t.Errorf("formatContextInfo(cache read) = %q, want %q", result, expected) } } func TestBuildStatusLine_EmptyDir(t *testing.T) { data := &StatusInput{} data.Model.DisplayName = "Model" data.Workspace.CurrentDir = "" data.ContextWindow.ContextWindowSize = 100000 left, right := buildStatusLine(data) // Should not panic, should produce valid output if left == "" { t.Error("buildStatusLine with empty dir should produce left output") } if right == "" { t.Error("buildStatusLine with empty dir should produce right output") } } func TestBuildStatusLine_LongModelName(t *testing.T) { data := &StatusInput{} data.Model.DisplayName = "Claude 3.5 Sonnet with Extended Context" data.Workspace.CurrentDir = "/home/user/my-very-long-project-name-here" data.ContextWindow.ContextWindowSize = 200000 data.ContextWindow.CurrentUsage = &TokenUsage{ InputTokens: 50000, } left, right := buildStatusLine(data) if !contains(left, "Claude 3.5 Sonnet with Extended Context") { t.Errorf("long model name not in left: %q", left) } if !contains(left, "my-very-long-project-name-here") { t.Errorf("long dir name not in left: %q", left) } if !contains(right, "50k/200k") { t.Errorf("context info not in right: %q", right) } } func TestBuildStatusLine_NilUsage(t *testing.T) { data := &StatusInput{} data.Model.DisplayName = "Model" data.Workspace.CurrentDir = "/test" data.ContextWindow.ContextWindowSize = 100000 data.ContextWindow.CurrentUsage = nil left, right := buildStatusLine(data) if !contains(right, "0/100k") { t.Errorf("nil usage should show 0: %q", right) } if left == "" { t.Error("left should not be empty") } } func TestBuildStatusLine_RootDir(t *testing.T) { data := &StatusInput{} data.Model.DisplayName = "Model" data.Workspace.CurrentDir = "/" data.ContextWindow.ContextWindowSize = 100000 left, _ := buildStatusLine(data) // filepath.Base("/") returns "/" if !contains(left, "/") || !contains(left, "Model") { t.Errorf("root dir handling failed: %q", left) } } func TestCalculatePadding_ExactFit(t *testing.T) { // When content exactly fills the width result := calculatePadding("12345", "67890", 10) expected := 1 // max(10-5-5, 1) = max(0, 1) = 1 if result != expected { t.Errorf("calculatePadding(exact fit) = %d, want %d", result, expected) } } func TestCalculatePadding_LargeWidth(t *testing.T) { result := calculatePadding("left", "right", 200) expected := 200 - 4 - 5 // 191 if result != expected { t.Errorf("calculatePadding(large) = %d, want %d", result, expected) } } func TestStripANSI_256Color(t *testing.T) { // 256-color escape sequences input := "\033[38;5;196mred\033[0m" result := stripANSI(input) expected := "red" if result != expected { t.Errorf("stripANSI(256color) = %q, want %q", result, expected) } } func TestStripANSI_TrueColor(t *testing.T) { // 24-bit true color escape sequences input := "\033[38;2;255;0;0mred\033[0m" result := stripANSI(input) expected := "red" if result != expected { t.Errorf("stripANSI(truecolor) = %q, want %q", result, expected) } } func TestStripANSI_Multiline(t *testing.T) { input := "\033[31mline1\033[0m\n\033[32mline2\033[0m" result := stripANSI(input) expected := "line1\nline2" if result != expected { t.Errorf("stripANSI(multiline) = %q, want %q", result, expected) } } func TestReadInputFromStdin_SingleLine(t *testing.T) { reader := bufio.NewReader(strings.NewReader("single line")) result := readInputFromStdin(reader) if result != "single line" { t.Errorf("readInputFromStdin(single) = %q, want 'single line'", result) } } func TestReadInputFromStdin_JSONLike(t *testing.T) { jsonStr := `{"key": "value", "nested": {"a": 1}}` reader := bufio.NewReader(strings.NewReader(jsonStr)) result := readInputFromStdin(reader) if result != jsonStr { t.Errorf("readInputFromStdin(json) = %q, want %q", result, jsonStr) } } func TestParseStatusInput_AllFields(t *testing.T) { jsonStr := `{ "model": {"display_name": "FullTest"}, "workspace": {"current_dir": "/full/path"}, "context_window": { "context_window_size": 150000, "current_usage": { "input_tokens": 1000, "cache_creation_input_tokens": 2000, "cache_read_input_tokens": 3000 } } }` data, err := parseStatusInput(jsonStr) if err != nil { t.Fatalf("parseStatusInput failed: %v", err) } if data.Model.DisplayName != "FullTest" { t.Errorf("DisplayName = %q, want FullTest", data.Model.DisplayName) } if data.ContextWindow.CurrentUsage.CacheCreationTokens != 2000 { t.Errorf("CacheCreationTokens = %d, want 2000", data.ContextWindow.CurrentUsage.CacheCreationTokens) } if data.ContextWindow.CurrentUsage.CacheReadInputTokens != 3000 { t.Errorf("CacheReadInputTokens = %d, want 3000", data.ContextWindow.CurrentUsage.CacheReadInputTokens) } } func TestParseStatusInput_ExtraFields(t *testing.T) { // JSON with extra unknown fields should still parse jsonStr := `{"model": {"display_name": "Test", "unknown": "field"}, "extra": "data"}` data, err := parseStatusInput(jsonStr) if err != nil { t.Fatalf("parseStatusInput with extra fields failed: %v", err) } if data.Model.DisplayName != "Test" { t.Errorf("DisplayName = %q, want Test", data.Model.DisplayName) } } func TestTokenUsage_ZeroValues(t *testing.T) { usage := &TokenUsage{ InputTokens: 0, CacheCreationTokens: 0, CacheReadInputTokens: 0, } result := formatContextInfo(100000, usage) expected := "0k/100k" if result != expected { t.Errorf("formatContextInfo(zero usage) = %q, want %q", result, expected) } } func TestStatuslineWidthOffset_Constant(t *testing.T) { // Verify the constant is defined and reasonable if statuslineWidthOffset <= 0 || statuslineWidthOffset > 20 { t.Errorf("statuslineWidthOffset = %d, expected between 1 and 20", statuslineWidthOffset) } } // Benchmark tests func BenchmarkFormatContextInfo(b *testing.B) { usage := &TokenUsage{ InputTokens: 50000, CacheCreationTokens: 10000, CacheReadInputTokens: 5000, } for i := 0; i < b.N; i++ { formatContextInfo(200000, usage) } } func BenchmarkStripANSI(b *testing.B) { input := "\033[32m●\033[0m \033[35mOpus\033[0m \033[1;32m➜\033[0m \033[36mproject\033[0m" for i := 0; i < b.N; i++ { stripANSI(input) } } func BenchmarkParseStatusInput(b *testing.B) { jsonStr := `{"model": {"display_name": "Test"}, "workspace": {"current_dir": "/test"}, "context_window": {"context_window_size": 200000, "current_usage": {"input_tokens": 50000}}}` for i := 0; i < b.N; i++ { parseStatusInput(jsonStr) } } func BenchmarkBuildStatusLine(b *testing.B) { data := &StatusInput{} data.Model.DisplayName = "Claude Opus" data.Workspace.CurrentDir = "/home/user/project" data.ContextWindow.ContextWindowSize = 200000 data.ContextWindow.CurrentUsage = &TokenUsage{ InputTokens: 50000, } for i := 0; i < b.N; i++ { buildStatusLine(data) } } func BenchmarkCalculatePadding(b *testing.B) { for i := 0; i < b.N; i++ { calculatePadding("left side content", "right side", 120) } } func BenchmarkFormatOutput(b *testing.B) { left := red + "left" + reset right := green + "right" + reset for i := 0; i < b.N; i++ { formatOutput(left, right, 50) } } func BenchmarkReadInputFromStdin(b *testing.B) { jsonStr := `{"model": {"display_name": "Test"}, "workspace": {"current_dir": "/test"}}` for i := 0; i < b.N; i++ { reader := bufio.NewReader(strings.NewReader(jsonStr)) readInputFromStdin(reader) } }