summaryrefslogtreecommitdiff
path: root/internal/video/veo_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/video/veo_test.go')
-rw-r--r--internal/video/veo_test.go273
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)
- }
-}