package utils import ( "fmt" "os" "path/filepath" "strings" "testing" "time" "github.com/belphemur/CBZOptimizer/v2/internal/cbz" "github.com/belphemur/CBZOptimizer/v2/internal/utils/errs" "github.com/belphemur/CBZOptimizer/v2/pkg/converter" "github.com/belphemur/CBZOptimizer/v2/pkg/converter/constant" ) func TestOptimizeIntegration(t *testing.T) { // Skip integration tests if no libwebp is available or testdata doesn't exist if testing.Short() { t.Skip("Skipping integration test in short mode") } // Check if testdata directory exists testdataDir := "../../testdata" if _, err := os.Stat(testdataDir); os.IsNotExist(err) { t.Skip("testdata directory not found, skipping integration tests") } // Create temporary directory for tests tempDir, err := os.MkdirTemp("", "test_optimize_integration") if err != nil { t.Fatal(err) } defer errs.CaptureGeneric(&err, os.RemoveAll, tempDir, "failed to remove temporary directory") // Get the real webp converter converterInstance, err := converter.Get(constant.WebP) if err != nil { t.Skip("WebP converter not available, skipping integration tests") } // Prepare the converter err = converterInstance.PrepareConverter() if err != nil { t.Skip("Failed to prepare WebP converter, skipping integration tests") } // Collect all test files (CBZ/CBR, excluding converted ones) var testFiles []string err = filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { fileName := strings.ToLower(info.Name()) if (strings.HasSuffix(fileName, ".cbz") || strings.HasSuffix(fileName, ".cbr")) && !strings.Contains(fileName, "converted") { testFiles = append(testFiles, path) } } return nil }) if err != nil { t.Fatal(err) } if len(testFiles) == 0 { t.Skip("No test files found") } tests := []struct { name string inputFile string override bool expectedOutput string shouldDelete bool expectError bool }{} // Generate test cases for each available test file for _, testFile := range testFiles { baseName := strings.TrimSuffix(filepath.Base(testFile), filepath.Ext(testFile)) isCBR := strings.HasSuffix(strings.ToLower(testFile), ".cbr") // Test without override tests = append(tests, struct { name string inputFile string override bool expectedOutput string shouldDelete bool expectError bool }{ name: fmt.Sprintf("%s file without override", strings.ToUpper(filepath.Ext(testFile)[1:])), inputFile: testFile, override: false, expectedOutput: filepath.Join(filepath.Dir(testFile), baseName+"_converted.cbz"), shouldDelete: false, expectError: false, }) // Test with override if isCBR { tests = append(tests, struct { name string inputFile string override bool expectedOutput string shouldDelete bool expectError bool }{ name: fmt.Sprintf("%s file with override", strings.ToUpper(filepath.Ext(testFile)[1:])), inputFile: testFile, override: true, expectedOutput: filepath.Join(filepath.Dir(testFile), baseName+".cbz"), shouldDelete: true, expectError: false, }) } } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create a copy of the input file for this test testFile := filepath.Join(tempDir, tt.name+"_"+filepath.Base(tt.inputFile)) data, err := os.ReadFile(tt.inputFile) if err != nil { t.Fatal(err) } err = os.WriteFile(testFile, data, 0644) if err != nil { t.Fatal(err) } // Setup options with real converter options := &OptimizeOptions{ ChapterConverter: converterInstance, Path: testFile, Quality: 85, Override: tt.override, Split: false, Timeout: 30 * time.Second, // Longer timeout for real conversion } // Run optimization err = Optimize(options) if tt.expectError { if err == nil { t.Error("Expected error but got none") } return } if err != nil { t.Fatalf("Unexpected error: %v", err) } // Determine expected output path for this test expectedOutput := tt.expectedOutput if tt.override && strings.HasSuffix(strings.ToLower(testFile), ".cbr") { expectedOutput = strings.TrimSuffix(testFile, filepath.Ext(testFile)) + ".cbz" } else if !tt.override { if strings.HasSuffix(strings.ToLower(testFile), ".cbz") { expectedOutput = strings.TrimSuffix(testFile, ".cbz") + "_converted.cbz" } else if strings.HasSuffix(strings.ToLower(testFile), ".cbr") { expectedOutput = strings.TrimSuffix(testFile, ".cbr") + "_converted.cbz" } } else { expectedOutput = testFile } // Verify output file exists if _, err := os.Stat(expectedOutput); os.IsNotExist(err) { t.Errorf("Expected output file not found: %s", expectedOutput) } // Verify output is a valid CBZ with converted content chapter, err := cbz.LoadChapter(expectedOutput) if err != nil { t.Errorf("Failed to load converted chapter: %v", err) } if !chapter.IsConverted { t.Error("Chapter is not marked as converted") } // Verify all pages are in WebP format (real conversion indicator) for i, page := range chapter.Pages { if page.Extension != ".webp" { t.Errorf("Page %d is not converted to WebP format (got: %s)", i, page.Extension) } } // Verify original file deletion for CBR override if tt.shouldDelete { if _, err := os.Stat(testFile); !os.IsNotExist(err) { t.Error("Original CBR file should have been deleted but still exists") } } else { // Verify original file still exists (unless it's the same as output) if testFile != expectedOutput { if _, err := os.Stat(testFile); os.IsNotExist(err) { t.Error("Original file should not have been deleted") } } } // Clean up output file os.Remove(expectedOutput) }) } } func TestOptimizeIntegration_AlreadyConverted(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") } // Create temporary directory tempDir, err := os.MkdirTemp("", "test_optimize_integration_converted") if err != nil { t.Fatal(err) } defer errs.CaptureGeneric(&err, os.RemoveAll, tempDir, "failed to remove temporary directory") // Use a converted test file testdataDir := "../../testdata" if _, err := os.Stat(testdataDir); os.IsNotExist(err) { t.Skip("testdata directory not found, skipping integration tests") } // Get the real webp converter converterInstance, err := converter.Get(constant.WebP) if err != nil { t.Skip("WebP converter not available, skipping integration tests") } // Prepare the converter err = converterInstance.PrepareConverter() if err != nil { t.Skip("Failed to prepare WebP converter, skipping integration tests") } var convertedFile string err = filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.Contains(strings.ToLower(info.Name()), "converted") { destPath := filepath.Join(tempDir, info.Name()) data, err := os.ReadFile(path) if err != nil { return err } err = os.WriteFile(destPath, data, info.Mode()) if err != nil { return err } convertedFile = destPath return filepath.SkipDir } return nil }) if err != nil { t.Fatal(err) } if convertedFile == "" { t.Skip("No converted test file found") } options := &OptimizeOptions{ ChapterConverter: converterInstance, Path: convertedFile, Quality: 85, Override: false, Split: false, Timeout: 30 * time.Second, } err = Optimize(options) if err != nil { t.Fatalf("Unexpected error: %v", err) } // Should not create a new file since it's already converted expectedOutput := strings.TrimSuffix(convertedFile, ".cbz") + "_converted.cbz" if _, err := os.Stat(expectedOutput); !os.IsNotExist(err) { t.Error("Should not have created a new converted file for already converted chapter") } } func TestOptimizeIntegration_InvalidFile(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") } // Get the real webp converter converterInstance, err := converter.Get(constant.WebP) if err != nil { t.Skip("WebP converter not available, skipping integration tests") } // Prepare the converter err = converterInstance.PrepareConverter() if err != nil { t.Skip("Failed to prepare WebP converter, skipping integration tests") } options := &OptimizeOptions{ ChapterConverter: converterInstance, Path: "/nonexistent/file.cbz", Quality: 85, Override: false, Split: false, Timeout: 30 * time.Second, } err = Optimize(options) if err == nil { t.Error("Expected error for nonexistent file") } } func TestOptimizeIntegration_Timeout(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") } // Create temporary directory tempDir, err := os.MkdirTemp("", "test_optimize_integration_timeout") if err != nil { t.Fatal(err) } defer errs.CaptureGeneric(&err, os.RemoveAll, tempDir, "failed to remove temporary directory") // Copy test files testdataDir := "../../testdata" if _, err := os.Stat(testdataDir); os.IsNotExist(err) { t.Skip("testdata directory not found, skipping integration tests") } // Get the real webp converter converterInstance, err := converter.Get(constant.WebP) if err != nil { t.Skip("WebP converter not available, skipping integration tests") } // Prepare the converter err = converterInstance.PrepareConverter() if err != nil { t.Skip("Failed to prepare WebP converter, skipping integration tests") } var cbzFile string err = filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".cbz") && !strings.Contains(info.Name(), "converted") { destPath := filepath.Join(tempDir, "test.cbz") data, err := os.ReadFile(path) if err != nil { return err } err = os.WriteFile(destPath, data, info.Mode()) if err != nil { return err } cbzFile = destPath return filepath.SkipDir } return nil }) if err != nil { t.Fatal(err) } if cbzFile == "" { t.Skip("No CBZ test file found") } // Test with short timeout to force timeout during conversion options := &OptimizeOptions{ ChapterConverter: converterInstance, Path: cbzFile, Quality: 85, Override: false, Split: false, Timeout: 10 * time.Millisecond, // Very short timeout to force timeout } err = Optimize(options) if err == nil { t.Error("Expected timeout error but got none") } // Check that the error contains timeout information if err != nil && !strings.Contains(err.Error(), "context deadline exceeded") && !strings.Contains(err.Error(), "timeout") { t.Errorf("Expected timeout error message, got: %v", err) } }