diff --git a/internal/cbz/cbz_loader_test.go b/internal/cbz/cbz_loader_test.go
index 4d82959..9f986f4 100644
--- a/internal/cbz/cbz_loader_test.go
+++ b/internal/cbz/cbz_loader_test.go
@@ -17,9 +17,9 @@ func TestLoadChapter(t *testing.T) {
testCases := []testCase{
{
name: "Original Chapter CBZ",
- filePath: "../../testdata/Chapter 1.cbz",
- expectedPages: 16,
- expectedSeries: "Boundless Necromancer",
+ filePath: "../../testdata/Chapter 128.cbz",
+ expectedPages: 14,
+ expectedSeries: "The Knight King Who Returned with a God",
expectedConversion: false,
},
{
diff --git a/internal/utils/optimize_integration_test.go b/internal/utils/optimize_integration_test.go
new file mode 100644
index 0000000..dd97a7c
--- /dev/null
+++ b/internal/utils/optimize_integration_test.go
@@ -0,0 +1,402 @@
+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)
+ }
+}
diff --git a/testdata/Chapter 1.cbz b/testdata/Chapter 1.cbz
deleted file mode 100644
index f01b7b6..0000000
Binary files a/testdata/Chapter 1.cbz and /dev/null differ
diff --git a/testdata/Chapter 128.cbz b/testdata/Chapter 128.cbz
new file mode 100644
index 0000000..b210835
Binary files /dev/null and b/testdata/Chapter 128.cbz differ
diff --git a/testdata/Chapter 278 - New Fable (Part 3).cbz b/testdata/Chapter 278 - New Fable (Part 3).cbz
new file mode 100644
index 0000000..4c3e256
Binary files /dev/null and b/testdata/Chapter 278 - New Fable (Part 3).cbz differ