summaryrefslogtreecommitdiff
path: root/internal/processor/processor_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-07-20 23:10:50 +0300
committerPaul Buetow <paul@buetow.org>2025-07-20 23:10:50 +0300
commit9c12e879c5d6833ce50f5b6d646ccce03a78db31 (patch)
tree206906b551d595b35d00586b6cc5bf9e1f3fe7f8 /internal/processor/processor_test.go
parente580fb57a29ec3c3f3e180b20cfa6ec28687689b (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.go253
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")
+ }
+}