diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-20 23:10:50 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-20 23:10:50 +0300 |
| commit | 9c12e879c5d6833ce50f5b6d646ccce03a78db31 (patch) | |
| tree | 206906b551d595b35d00586b6cc5bf9e1f3fe7f8 /internal/processor/processor_test.go | |
| parent | e580fb57a29ec3c3f3e180b20cfa6ec28687689b (diff) | |
test: add comprehensive test coverage for refactored packages
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 <noreply@opencode.ai>
Diffstat (limited to 'internal/processor/processor_test.go')
| -rw-r--r-- | internal/processor/processor_test.go | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/internal/processor/processor_test.go b/internal/processor/processor_test.go new file mode 100644 index 0000000..3a90a05 --- /dev/null +++ b/internal/processor/processor_test.go @@ -0,0 +1,253 @@ +package processor + +import ( + "os" + "path/filepath" + "testing" + + "codeberg.org/snonux/totalrecall/internal/cli" +) + +func TestNewProcessor(t *testing.T) { + // Set up test environment + os.Setenv("OPENAI_API_KEY", "test-key") + defer os.Unsetenv("OPENAI_API_KEY") + + flags := cli.NewFlags() + p := NewProcessor(flags) + + if p == nil { + t.Fatal("NewProcessor returned nil") + } + + if p.flags != flags { + t.Error("Processor flags not set correctly") + } + + if p.translator == nil { + t.Error("Translator not initialized") + } + + if p.translationCache == nil { + t.Error("Translation cache not initialized") + } + + if p.phoneticFetcher == nil { + t.Error("Phonetic fetcher not initialized") + } +} + +func TestProcessSingleWord_InvalidWord(t *testing.T) { + flags := cli.NewFlags() + flags.OutputDir = t.TempDir() + p := NewProcessor(flags) + + // Test with non-Bulgarian text + err := p.ProcessSingleWord("hello") + if err == nil { + t.Error("Expected error for non-Bulgarian word") + } + + // Test with empty string + err = p.ProcessSingleWord("") + if err == nil { + t.Error("Expected error for empty word") + } +} + +func TestProcessSingleWord_ValidWord(t *testing.T) { + // Skip if no API key + if os.Getenv("OPENAI_API_KEY") == "" { + t.Skip("Skipping test: OPENAI_API_KEY not set") + } + + flags := cli.NewFlags() + flags.OutputDir = t.TempDir() + flags.SkipAudio = true + flags.SkipImages = true + p := NewProcessor(flags) + + err := p.ProcessSingleWord("ябълка") + if err != nil { + t.Errorf("ProcessSingleWord failed: %v", err) + } + + // Check that output directory was created + if _, err := os.Stat(flags.OutputDir); os.IsNotExist(err) { + t.Error("Output directory was not created") + } +} + +func TestProcessBatch_InvalidFile(t *testing.T) { + flags := cli.NewFlags() + flags.BatchFile = "/nonexistent/file.txt" + p := NewProcessor(flags) + + err := p.ProcessBatch() + if err == nil { + t.Error("Expected error for non-existent batch file") + } +} + +func TestProcessBatch_ValidFile(t *testing.T) { + // Create test batch file + tmpDir := t.TempDir() + batchFile := filepath.Join(tmpDir, "batch.txt") + content := `ябълка +котка = cat +куче` + err := os.WriteFile(batchFile, []byte(content), 0644) + if err != nil { + t.Fatalf("Failed to create test batch file: %v", err) + } + + flags := cli.NewFlags() + flags.OutputDir = tmpDir + flags.BatchFile = batchFile + flags.SkipAudio = true + flags.SkipImages = true + p := NewProcessor(flags) + + // Skip if no API key + if os.Getenv("OPENAI_API_KEY") == "" { + t.Skip("Skipping test: OPENAI_API_KEY not set") + } + + err = p.ProcessBatch() + if err != nil { + t.Errorf("ProcessBatch failed: %v", err) + } +} + +func TestProcessWordWithTranslation_ProvidedTranslation(t *testing.T) { + flags := cli.NewFlags() + flags.OutputDir = t.TempDir() + flags.SkipAudio = true + flags.SkipImages = true + p := NewProcessor(flags) + + // Skip if no API key (needed for phonetic fetcher) + if os.Getenv("OPENAI_API_KEY") == "" { + t.Skip("Skipping test: OPENAI_API_KEY not set") + } + + err := p.ProcessWordWithTranslation("ябълка", "apple") + if err != nil { + t.Errorf("ProcessWordWithTranslation failed: %v", err) + } + + // Check that translation was cached + cached, found := p.translationCache.Get("ябълка") + if !found || cached != "apple" { + t.Errorf("Expected cached translation 'apple', got '%s' (found: %v)", cached, found) + } +} + +func TestFindOrCreateWordDirectory(t *testing.T) { + flags := cli.NewFlags() + flags.OutputDir = t.TempDir() + p := NewProcessor(flags) + + // First call should create directory + dir1 := p.findOrCreateWordDirectory("тест") + if dir1 == "" { + t.Error("findOrCreateWordDirectory returned empty string") + } + + // Check directory exists + if _, err := os.Stat(dir1); os.IsNotExist(err) { + t.Error("Directory was not created") + } + + // Check word.txt was created + wordFile := filepath.Join(dir1, "word.txt") + content, err := os.ReadFile(wordFile) + if err != nil { + t.Errorf("Failed to read word.txt: %v", err) + } + if string(content) != "тест" { + t.Errorf("Expected word.txt to contain 'тест', got '%s'", string(content)) + } + + // Second call should find existing directory + dir2 := p.findOrCreateWordDirectory("тест") + if dir2 != dir1 { + t.Errorf("Expected to find existing directory %s, got %s", dir1, dir2) + } +} + +func TestFindCardDirectory(t *testing.T) { + flags := cli.NewFlags() + flags.OutputDir = t.TempDir() + p := NewProcessor(flags) + + // Test with non-existent word + dir := p.findCardDirectory("несъществуваща") + if dir != "" { + t.Error("Expected empty string for non-existent word") + } + + // Create a word directory + wordDir := p.findOrCreateWordDirectory("тест") + + // Now should find it + dir = p.findCardDirectory("тест") + if dir != wordDir { + t.Errorf("Expected to find directory %s, got %s", wordDir, dir) + } +} + +func TestGenerateAnkiFile(t *testing.T) { + flags := cli.NewFlags() + flags.OutputDir = t.TempDir() + flags.GenerateAnki = true + flags.AnkiCSV = true // Test CSV format + p := NewProcessor(flags) + + // Add some test translations + p.translationCache.Add("ябълка", "apple") + p.translationCache.Add("котка", "cat") + + err := p.GenerateAnkiFile() + if err != nil { + t.Errorf("GenerateAnkiFile failed: %v", err) + } + + // Check CSV file was created + csvFile := filepath.Join(flags.OutputDir, "anki_import.csv") + if _, err := os.Stat(csvFile); os.IsNotExist(err) { + t.Error("CSV file was not created") + } +} + +func TestGenerateAnkiFile_APKG(t *testing.T) { + flags := cli.NewFlags() + flags.OutputDir = t.TempDir() + flags.GenerateAnki = true + flags.AnkiCSV = false // Test APKG format + flags.DeckName = "Test Deck" + p := NewProcessor(flags) + + // Create test word directories with files + word1Dir := p.findOrCreateWordDirectory("ябълка") + word2Dir := p.findOrCreateWordDirectory("котка") + + // Add test translations + p.translationCache.Add("ябълка", "apple") + p.translationCache.Add("котка", "cat") + + // Create dummy audio files + os.WriteFile(filepath.Join(word1Dir, "ябълка.mp3"), []byte("audio1"), 0644) + os.WriteFile(filepath.Join(word2Dir, "котка.mp3"), []byte("audio2"), 0644) + + err := p.GenerateAnkiFile() + if err != nil { + t.Errorf("GenerateAnkiFile (APKG) failed: %v", err) + } + + // Check APKG file was created + apkgFile := filepath.Join(flags.OutputDir, "Test_Deck.apkg") + if _, err := os.Stat(apkgFile); os.IsNotExist(err) { + t.Error("APKG file was not created") + } +} |
