diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-20 21:20:40 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-20 21:20:40 +0300 |
| commit | 9e3328a6aaefe4bd1aa0ec3e8bf6e93d6033180b (patch) | |
| tree | f70a6b53facc81a8bddbe5eeee76708e474e3298 /internal/testutil/mocks.go | |
| parent | 1afd19206720af695625dd46ff0ded0dedeef329 (diff) | |
test: add comprehensive test suite for audio and anki packages
- Add tests for audio package (62.8% coverage)
- OpenAI provider tests with mocking
- Provider interface and fallback mechanism tests
- Bulgarian text validation tests
- Audio caching functionality tests
- Add tests for anki package (84.8% coverage)
- CSV generation tests
- APKG package generation tests
- Card management and formatting tests
- Directory scanning and media handling tests
- Add test utilities and mocks
- Mock implementations for external dependencies
- Test helpers for common operations
- Utilities for creating test directories and files
- Update Taskfile.yaml with comprehensive test targets
- test: Run all tests
- test-verbose: Run with verbose output
- test-coverage: Run with coverage report
- test-coverage-html: Generate HTML coverage report
- test-race: Run with race detector
- test-short: Run only short tests
- test-all: Comprehensive suite with coverage and race detection
- clean: Remove build artifacts and test files
- Fix existing image package tests
- Remove tests for non-existent methods
- Update tests to match actual implementation
- Skip tests requiring live OpenAI API
This provides a solid foundation for ensuring code quality and catching regressions.
π€ Generated with [opencode](https://opencode.ai)
Co-Authored-By: opencode <noreply@opencode.ai>
Diffstat (limited to 'internal/testutil/mocks.go')
| -rw-r--r-- | internal/testutil/mocks.go | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/internal/testutil/mocks.go b/internal/testutil/mocks.go new file mode 100644 index 0000000..811b840 --- /dev/null +++ b/internal/testutil/mocks.go @@ -0,0 +1,187 @@ +package testutil + +import ( + "context" + "fmt" + "io" + "strings" +) + +// MockHTTPClient mocks HTTP client for testing +type MockHTTPClient struct { + Responses map[string]*MockResponse + Errors map[string]error + Calls []string +} + +// MockResponse represents a mocked HTTP response +type MockResponse struct { + StatusCode int + Body string + Headers map[string]string +} + +// Get mocks an HTTP GET request +func (m *MockHTTPClient) Get(url string) (*MockResponse, error) { + m.Calls = append(m.Calls, fmt.Sprintf("GET %s", url)) + + if err, ok := m.Errors[url]; ok { + return nil, err + } + + if resp, ok := m.Responses[url]; ok { + return resp, nil + } + + return &MockResponse{ + StatusCode: 404, + Body: "Not Found", + }, nil +} + +// Post mocks an HTTP POST request +func (m *MockHTTPClient) Post(url string, body interface{}) (*MockResponse, error) { + m.Calls = append(m.Calls, fmt.Sprintf("POST %s", url)) + + if err, ok := m.Errors[url]; ok { + return nil, err + } + + if resp, ok := m.Responses[url]; ok { + return resp, nil + } + + return &MockResponse{ + StatusCode: 404, + Body: "Not Found", + }, nil +} + +// MockOpenAIClient mocks OpenAI API client +type MockOpenAIClient struct { + TTSResponses map[string][]byte + ImageResponses map[string]string + Errors map[string]error + Calls []string +} + +// CreateSpeech mocks OpenAI TTS API +func (m *MockOpenAIClient) CreateSpeech(ctx context.Context, text, voice, model string) (io.ReadCloser, error) { + call := fmt.Sprintf("TTS: %s (voice=%s, model=%s)", text, voice, model) + m.Calls = append(m.Calls, call) + + key := fmt.Sprintf("%s-%s-%s", text, voice, model) + if err, ok := m.Errors[key]; ok { + return nil, err + } + + if data, ok := m.TTSResponses[key]; ok { + return io.NopCloser(strings.NewReader(string(data))), nil + } + + // Default response + return io.NopCloser(strings.NewReader("mock audio data")), nil +} + +// CreateImage mocks OpenAI DALL-E API +func (m *MockOpenAIClient) CreateImage(ctx context.Context, prompt string) (string, error) { + call := fmt.Sprintf("Image: %s", prompt) + m.Calls = append(m.Calls, call) + + if err, ok := m.Errors[prompt]; ok { + return "", err + } + + if url, ok := m.ImageResponses[prompt]; ok { + return url, nil + } + + // Default response + return "https://example.com/mock-image.jpg", nil +} + +// MockFileSystem mocks file system operations +type MockFileSystem struct { + Files map[string][]byte + Errors map[string]error + Calls []string +} + +// ReadFile mocks reading a file +func (m *MockFileSystem) ReadFile(path string) ([]byte, error) { + m.Calls = append(m.Calls, fmt.Sprintf("READ %s", path)) + + if err, ok := m.Errors[path]; ok { + return nil, err + } + + if data, ok := m.Files[path]; ok { + return data, nil + } + + return nil, fmt.Errorf("file not found: %s", path) +} + +// WriteFile mocks writing a file +func (m *MockFileSystem) WriteFile(path string, data []byte) error { + m.Calls = append(m.Calls, fmt.Sprintf("WRITE %s (%d bytes)", path, len(data))) + + if err, ok := m.Errors[path]; ok { + return err + } + + m.Files[path] = data + return nil +} + +// Exists mocks checking if a file exists +func (m *MockFileSystem) Exists(path string) bool { + m.Calls = append(m.Calls, fmt.Sprintf("EXISTS %s", path)) + _, exists := m.Files[path] + return exists +} + +// MockTranslator mocks translation service +type MockTranslator struct { + Translations map[string]string + Errors map[string]error + Calls []string +} + +// Translate mocks translating text +func (m *MockTranslator) Translate(ctx context.Context, text, fromLang, toLang string) (string, error) { + call := fmt.Sprintf("Translate: %s (%s->%s)", text, fromLang, toLang) + m.Calls = append(m.Calls, call) + + if err, ok := m.Errors[text]; ok { + return "", err + } + + if translation, ok := m.Translations[text]; ok { + return translation, nil + } + + // Default mock translation + return fmt.Sprintf("mock translation of %s", text), nil +} + +// TestDataGenerator generates test data +type TestDataGenerator struct{} + +// GenerateBulgarianWord generates a test Bulgarian word +func (g *TestDataGenerator) GenerateBulgarianWord() string { + words := []string{"ΡΠ±ΡΠ»ΠΊΠ°", "ΠΊΠΎΡΠΊΠ°", "ΠΊΡΡΠ΅", "Ρ
Π»ΡΠ±", "Π²ΠΎΠ΄Π°", "ΠΊΠ½ΠΈΠ³Π°", "ΡΡΠΎΠ»", "ΠΏΡΠΎΠ·ΠΎΡΠ΅Ρ"} + return words[0] // Simple implementation, could be randomized +} + +// GenerateAudioData generates mock audio data +func (g *TestDataGenerator) GenerateAudioData() []byte { + // Simple mock MP3 header + return []byte{0xFF, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00} +} + +// GenerateImageData generates mock image data +func (g *TestDataGenerator) GenerateImageData() []byte { + // Simple mock JPEG header + return []byte{0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46} +} |
