From 9c12e879c5d6833ce50f5b6d646ccce03a78db31 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 20 Jul 2025 23:10:50 +0300 Subject: test: add comprehensive test coverage for refactored packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test suites for all newly created packages from the main.go refactoring: - batch: 100% coverage - file reading, parsing, edge cases - cli: 96.7% coverage - command setup, flags, configuration - translation: 92% coverage - API integration, caching, errors - phonetic: 87.5% coverage - API fetching, file operations - models: 77.3% coverage - model listing functionality - processor: 18% coverage - basic tests (limited by API dependencies) Total: 1159 lines of test code across 7 new test files πŸ€– Generated with [opencode](https://opencode.ai) Co-Authored-By: opencode --- internal/batch/processor_test.go | 247 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 internal/batch/processor_test.go (limited to 'internal/batch/processor_test.go') diff --git a/internal/batch/processor_test.go b/internal/batch/processor_test.go new file mode 100644 index 0000000..1df8284 --- /dev/null +++ b/internal/batch/processor_test.go @@ -0,0 +1,247 @@ +package batch + +import ( + "os" + "path/filepath" + "reflect" + "testing" +) + +func TestReadBatchFile(t *testing.T) { + tests := []struct { + name string + fileContent string + want []WordEntry + wantErr bool + }{ + { + name: "empty file", + fileContent: "", + want: nil, + }, + { + name: "only whitespace", + fileContent: " \n\t\r\n ", + want: nil, + }, + { + name: "words with translations", + fileContent: `ябълка = apple +ΠΊΠΎΡ‚ΠΊΠ° = cat +ΠΊΡƒΡ‡Π΅ = dog`, + want: []WordEntry{ + {Bulgarian: "ябълка", Translation: "apple"}, + {Bulgarian: "ΠΊΠΎΡ‚ΠΊΠ°", Translation: "cat"}, + {Bulgarian: "ΠΊΡƒΡ‡Π΅", Translation: "dog"}, + }, + }, + { + name: "mixed format", + fileContent: `ябълка +ΠΊΠΎΡ‚ΠΊΠ° = cat +ΠΊΡƒΡ‡Π΅ +хляб = bread`, + want: []WordEntry{ + {Bulgarian: "ябълка", Translation: ""}, + {Bulgarian: "ΠΊΠΎΡ‚ΠΊΠ°", Translation: "cat"}, + {Bulgarian: "ΠΊΡƒΡ‡Π΅", Translation: ""}, + {Bulgarian: "хляб", Translation: "bread"}, + }, + }, + { + name: "empty lines and whitespace", + fileContent: ` +ябълка + +ΠΊΠΎΡ‚ΠΊΠ° = cat + + ΠΊΡƒΡ‡Π΅ + +`, + want: []WordEntry{ + {Bulgarian: "ябълка", Translation: ""}, + {Bulgarian: "ΠΊΠΎΡ‚ΠΊΠ°", Translation: "cat"}, + {Bulgarian: "ΠΊΡƒΡ‡Π΅", Translation: ""}, + }, + }, + { + name: "windows line endings", + fileContent: "ябълка\r\nΠΊΠΎΡ‚ΠΊΠ° = cat\r\nΠΊΡƒΡ‡Π΅", + want: []WordEntry{ + {Bulgarian: "ябълка", Translation: ""}, + {Bulgarian: "ΠΊΠΎΡ‚ΠΊΠ°", Translation: "cat"}, + {Bulgarian: "ΠΊΡƒΡ‡Π΅", Translation: ""}, + }, + }, + { + name: "multiple equals signs", + fileContent: `test = word = with = equals`, + want: []WordEntry{ + {Bulgarian: "test", Translation: "word = with = equals"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create temp file + tmpDir := t.TempDir() + tmpFile := filepath.Join(tmpDir, "test.txt") + err := os.WriteFile(tmpFile, []byte(tt.fileContent), 0644) + if err != nil { + t.Fatalf("Failed to create test file: %v", err) + } + + got, err := ReadBatchFile(tmpFile) + if (err != nil) != tt.wantErr { + t.Errorf("ReadBatchFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ReadBatchFile() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestReadBatchFile_FileNotFound(t *testing.T) { + _, err := ReadBatchFile("/nonexistent/file.txt") + if err == nil { + t.Error("Expected error for non-existent file") + } +} + +func TestSplitLines(t *testing.T) { + tests := []struct { + name string + input string + want []string + }{ + { + name: "unix line endings", + input: "line1\nline2\nline3", + want: []string{"line1", "line2", "line3"}, + }, + { + name: "windows line endings", + input: "line1\r\nline2\r\nline3", + want: []string{"line1", "line2", "line3"}, + }, + { + name: "mixed line endings", + input: "line1\nline2\r\nline3", + want: []string{"line1", "line2", "line3"}, + }, + { + name: "empty string", + input: "", + want: nil, + }, + { + name: "single line no ending", + input: "single line", + want: []string{"single line"}, + }, + { + name: "trailing newline", + input: "line1\nline2\n", + want: []string{"line1", "line2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := splitLines(tt.input) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("splitLines() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTrimSpace(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "no whitespace", + input: "hello", + want: "hello", + }, + { + name: "leading spaces", + input: " hello", + want: "hello", + }, + { + name: "trailing spaces", + input: "hello ", + want: "hello", + }, + { + name: "both sides", + input: " hello ", + want: "hello", + }, + { + name: "tabs and spaces", + input: "\t hello \t", + want: "hello", + }, + { + name: "newlines", + input: "\nhello\n", + want: "hello", + }, + { + name: "all whitespace types", + input: " \t\n\rhello \t\n\r", + want: "hello", + }, + { + name: "empty string", + input: "", + want: "", + }, + { + name: "only whitespace", + input: " \t\n\r ", + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := trimSpace(tt.input) + if got != tt.want { + t.Errorf("trimSpace() = %q, want %q", got, tt.want) + } + }) + } +} + +func TestIsSpace(t *testing.T) { + tests := []struct { + r rune + want bool + }{ + {' ', true}, + {'\t', true}, + {'\n', true}, + {'\r', true}, + {'a', false}, + {'1', false}, + {'!', false}, + {0, false}, + } + + for _, tt := range tests { + t.Run(string(tt.r), func(t *testing.T) { + if got := isSpace(tt.r); got != tt.want { + t.Errorf("isSpace(%q) = %v, want %v", tt.r, got, tt.want) + } + }) + } +} -- cgit v1.2.3