diff options
Diffstat (limited to 'internal/video/veo_test.go')
| -rw-r--r-- | internal/video/veo_test.go | 273 |
1 files changed, 0 insertions, 273 deletions
diff --git a/internal/video/veo_test.go b/internal/video/veo_test.go deleted file mode 100644 index ce653e3..0000000 --- a/internal/video/veo_test.go +++ /dev/null @@ -1,273 +0,0 @@ -// Package video_test provides unit tests for the Veo video generator. -// All tests are mock-based — no real API calls are made. -package video - -import ( - "context" - "errors" - "os" - "path/filepath" - "strings" - "testing" - - "google.golang.org/genai" -) - -// TestNewVeoGenerator_EmptyKey verifies that an empty API key is rejected. -func TestNewVeoGenerator_EmptyKey(t *testing.T) { - t.Parallel() - - _, err := NewVeoGenerator("") - if err == nil { - t.Fatal("expected error for empty API key, got nil") - } -} - -// TestNewVeoGenerator_WhitespaceKey verifies that a whitespace-only API key is -// treated the same as an empty key. -func TestNewVeoGenerator_WhitespaceKey(t *testing.T) { - t.Parallel() - - _, err := NewVeoGenerator(" ") - if err == nil { - t.Fatal("expected error for whitespace API key, got nil") - } -} - -// TestNewVeoGenerator_ClientInitFailure verifies that a genai client -// initialisation error propagates as a wrapped error. -func TestNewVeoGenerator_ClientInitFailure(t *testing.T) { - // Do not use t.Parallel: this test replaces the package-global newGenaiClient hook. - - // Temporarily replace the genai client constructor with one that always fails. - orig := newGenaiClient - newGenaiClient = func(_ context.Context, _ *genai.ClientConfig) (*genai.Client, error) { - return nil, errors.New("injected init error") - } - t.Cleanup(func() { newGenaiClient = orig }) - - _, err := NewVeoGenerator("test-api-key") - if err == nil { - t.Fatal("expected error from client init failure, got nil") - } - if !strings.Contains(err.Error(), "injected init error") { - t.Fatalf("unexpected error text: %v", err) - } -} - -// TestLoadGalleryImage_Missing verifies that loadGalleryImage returns an error -// when no matching file exists in the given directory. -func TestLoadGalleryImage_Missing(t *testing.T) { - t.Parallel() - - dir := t.TempDir() - _, _, err := loadGalleryImage(dir, 1) - if err == nil { - t.Fatal("expected error for missing gallery image, got nil") - } -} - -// TestLoadGalleryImage_Found verifies that loadGalleryImage returns the correct -// path and bytes when the expected file exists. -func TestLoadGalleryImage_Found(t *testing.T) { - t.Parallel() - - dir := t.TempDir() - imgFile := filepath.Join(dir, "ябълка_gallery_2.png") - wantBytes := []byte("fake-png-data") - if err := os.WriteFile(imgFile, wantBytes, 0o644); err != nil { - t.Fatalf("setup: write test image: %v", err) - } - - gotPath, gotBytes, err := loadGalleryImage(dir, 2) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gotPath != imgFile { - t.Errorf("path: got %q, want %q", gotPath, imgFile) - } - if string(gotBytes) != string(wantBytes) { - t.Errorf("bytes: got %q, want %q", gotBytes, wantBytes) - } -} - -// TestBuildVeoPrompt verifies that the prompt is non-empty and contains the -// key terms that shape Veo's output style. -func TestBuildVeoPrompt(t *testing.T) { - t.Parallel() - - prompt := buildVeoPrompt() - if prompt == "" { - t.Fatal("buildVeoPrompt returned empty string") - } - - keywords := []string{"comic", "Bulgarian", "educational", "8-second"} - for _, kw := range keywords { - if !strings.Contains(prompt, kw) { - t.Errorf("expected prompt to contain %q", kw) - } - } -} - -// TestSaveMP4_WritesFile verifies that saveMP4 creates the expected MP4 file and -// returns its absolute path. -func TestSaveMP4_WritesFile(t *testing.T) { - t.Parallel() - - outDir := t.TempDir() - fakeVideo := []byte{0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70} // minimal ftyp box bytes - - // Simulate source path like the real gallery image would produce. - srcPath := "/stories/ябълка/ябълка_gallery_3.png" - - got, err := saveMP4(fakeVideo, outDir, srcPath, 3) - if err != nil { - t.Fatalf("saveMP4 failed: %v", err) - } - - if !strings.HasSuffix(got, ".mp4") { - t.Errorf("expected .mp4 suffix, got %q", got) - } - - data, err := os.ReadFile(got) - if err != nil { - t.Fatalf("reading saved MP4: %v", err) - } - if string(data) != string(fakeVideo) { - t.Errorf("file contents mismatch") - } -} - -// TestSaveMP4_CreatesOutputDir verifies that saveMP4 creates the output directory -// when it does not already exist. -func TestSaveMP4_CreatesOutputDir(t *testing.T) { - t.Parallel() - - base := t.TempDir() - outDir := filepath.Join(base, "nested", "output") - fakeVideo := []byte("video-data") - - _, err := saveMP4(fakeVideo, outDir, "word_gallery_1.png", 1) - if err != nil { - t.Fatalf("saveMP4 failed: %v", err) - } - - if _, statErr := os.Stat(outDir); os.IsNotExist(statErr) { - t.Error("expected output directory to be created") - } -} - -// TestSaveMP4_FallbackName verifies that saveMP4 uses a fallback name when the -// source path lacks a recognisable gallery file name (no .png suffix). -func TestSaveMP4_FallbackName(t *testing.T) { - t.Parallel() - - outDir := t.TempDir() - fakeVideo := []byte("video-data") - - // srcPath with no .png extension triggers the fallback naming path. - got, err := saveMP4(fakeVideo, outDir, "unusual_source", 5) - if err != nil { - t.Fatalf("saveMP4 failed: %v", err) - } - - if !strings.HasSuffix(got, ".mp4") { - t.Errorf("expected .mp4 suffix even for fallback name, got %q", got) - } -} - -// --------------------------------------------------------------------------- -// pageNumFromPath -// --------------------------------------------------------------------------- - -// TestPageNumFromPath verifies that pageNumFromPath correctly extracts the -// gallery page number from various file name patterns. -func TestPageNumFromPath(t *testing.T) { - t.Parallel() - - cases := []struct { - path string - want int - }{ - {"/stories/ябълка/ябълка_gallery_1.png", 1}, - {"/stories/word/word_gallery_10.png", 10}, - // Non-gallery path — should return 0. - {"/stories/word/word_cover.png", 0}, - // Missing trailing number — should return 0. - {"/stories/word/word_gallery_.png", 0}, - // Page number zero — should return 0 (non-positive). - {"/stories/word/word_gallery_0.png", 0}, - // Nested gallery name with multiple "_gallery_" tokens — last one wins. - {"/comics/slug/slug_gallery_3.png", 3}, - } - - for _, tc := range cases { - got := pageNumFromPath(tc.path) - if got != tc.want { - t.Errorf("pageNumFromPath(%q) = %d, want %d", tc.path, got, tc.want) - } - } -} - -// --------------------------------------------------------------------------- -// loadGalleryImage — additional edge cases -// --------------------------------------------------------------------------- - -// TestLoadGalleryImage_MultipleMatchesUsesFirst verifies that when several -// gallery files share the same page number, loadGalleryImage returns the -// lexicographically first match without error. -func TestLoadGalleryImage_MultipleMatchesUsesFirst(t *testing.T) { - t.Parallel() - - dir := t.TempDir() - - // Two files for page 1 — alphabetical order determines which is returned. - files := []string{"aaa_gallery_1.png", "zzz_gallery_1.png"} - for _, name := range files { - if err := os.WriteFile(filepath.Join(dir, name), []byte(name), 0o644); err != nil { - t.Fatalf("setup: %v", err) - } - } - - gotPath, gotBytes, err := loadGalleryImage(dir, 1) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - // filepath.Glob returns results in sorted order, so aaa_gallery_1.png comes first. - expectedName := "aaa_gallery_1.png" - if filepath.Base(gotPath) != expectedName { - t.Errorf("expected first match %q, got %q", expectedName, filepath.Base(gotPath)) - } - if string(gotBytes) != expectedName { - t.Errorf("bytes mismatch: got %q, want %q", gotBytes, expectedName) - } -} - -// --------------------------------------------------------------------------- -// VeoGenerator — constructor with valid mock client -// --------------------------------------------------------------------------- - -// TestNewVeoGenerator_WithMockClient verifies that NewVeoGenerator succeeds -// when the genai client factory does not return an error. -func TestNewVeoGenerator_WithMockClient(t *testing.T) { - // Do not use t.Parallel: this test replaces the package-global newGenaiClient hook. - - orig := newGenaiClient - newGenaiClient = func(_ context.Context, _ *genai.ClientConfig) (*genai.Client, error) { - // Return a zero-value client pointer — sufficient for construction. - return &genai.Client{}, nil - } - t.Cleanup(func() { newGenaiClient = orig }) - - gen, err := NewVeoGenerator("valid-api-key") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gen == nil { - t.Fatal("expected non-nil VeoGenerator") - } - if gen.model != DefaultVeoModel { - t.Errorf("model: got %q, want %q", gen.model, DefaultVeoModel) - } -} |
