package tests import ( "os" "os/exec" "path/filepath" "strings" "testing" ) // TestIntegration_EndToEnd runs a series of commands to test the entire workflow func TestIntegration_EndToEnd(t *testing.T) { // Skip if not running integration tests if os.Getenv("RUN_INTEGRATION_TESTS") == "" { t.Skip("Skipping integration tests. Set RUN_INTEGRATION_TESTS=1 to run.") } // Get the path to the built binary binaryPath := os.Getenv("BINARY_PATH") if binaryPath == "" { // Default to looking in the current directory binaryPath = "sub-cli" } // Create temporary directory for test files tempDir := t.TempDir() // Test files srtFile := filepath.Join(tempDir, "test.srt") lrcFile := filepath.Join(tempDir, "test.lrc") vttFile := filepath.Join(tempDir, "test.vtt") txtFile := filepath.Join(tempDir, "test.txt") // Create SRT test file srtContent := `1 00:00:01,000 --> 00:00:04,000 This is the first subtitle line. 2 00:00:05,000 --> 00:00:08,000 This is the second subtitle line. 3 00:00:09,500 --> 00:00:12,800 This is the third subtitle line with a line break. ` if err := os.WriteFile(srtFile, []byte(srtContent), 0644); err != nil { t.Fatalf("Failed to create SRT test file: %v", err) } // Step 1: Test conversion from SRT to LRC t.Log("Testing SRT to LRC conversion...") cmd := exec.Command(binaryPath, "convert", srtFile, lrcFile) output, err := cmd.CombinedOutput() if err != nil { t.Fatalf("Convert command failed: %v\nOutput: %s", err, output) } // Verify LRC file was created if _, err := os.Stat(lrcFile); os.IsNotExist(err) { t.Fatalf("LRC file was not created") } // Read LRC content lrcContent, err := os.ReadFile(lrcFile) if err != nil { t.Fatalf("Failed to read LRC file: %v", err) } // Verify LRC content if !strings.Contains(string(lrcContent), "[00:01.000]") { t.Errorf("Expected LRC to contain timeline [00:01.000], got: %s", string(lrcContent)) } if !strings.Contains(string(lrcContent), "This is the first subtitle line.") { t.Errorf("Expected LRC to contain text content, got: %s", string(lrcContent)) } // Step 2: Create a new SRT file with different timing srtModifiedContent := `1 00:00:10,000 --> 00:00:14,000 This is the first subtitle line. 2 00:00:15,000 --> 00:00:18,000 This is the second subtitle line. 3 00:00:19,500 --> 00:00:22,800 This is the third subtitle line with a line break. ` srtModifiedFile := filepath.Join(tempDir, "modified.srt") if err := os.WriteFile(srtModifiedFile, []byte(srtModifiedContent), 0644); err != nil { t.Fatalf("Failed to create modified SRT test file: %v", err) } // Step 3: Test sync between SRT files t.Log("Testing SRT to SRT sync...") cmd = exec.Command(binaryPath, "sync", srtModifiedFile, srtFile) output, err = cmd.CombinedOutput() if err != nil { t.Fatalf("Sync command failed: %v\nOutput: %s", err, output) } // Read synced SRT content syncedSrtContent, err := os.ReadFile(srtFile) if err != nil { t.Fatalf("Failed to read synced SRT file: %v", err) } // Verify synced content has new timings but original text if !strings.Contains(string(syncedSrtContent), "00:00:10,000 -->") { t.Errorf("Expected synced SRT to have new timing 00:00:10,000, got: %s", string(syncedSrtContent)) } if !strings.Contains(string(syncedSrtContent), "This is the first subtitle line.") { t.Errorf("Expected synced SRT to preserve original text, got: %s", string(syncedSrtContent)) } // Step 4: Test conversion from SRT to VTT t.Log("Testing SRT to VTT conversion...") cmd = exec.Command(binaryPath, "convert", srtFile, vttFile) output, err = cmd.CombinedOutput() if err != nil { t.Fatalf("Convert command failed: %v\nOutput: %s", err, output) } // Verify VTT file was created if _, err := os.Stat(vttFile); os.IsNotExist(err) { t.Fatalf("VTT file was not created") } // Read VTT content vttContent, err := os.ReadFile(vttFile) if err != nil { t.Fatalf("Failed to read VTT file: %v", err) } // Verify VTT content if !strings.Contains(string(vttContent), "WEBVTT") { t.Errorf("Expected VTT to contain WEBVTT header, got: %s", string(vttContent)) } if !strings.Contains(string(vttContent), "00:00:10.000 -->") { t.Errorf("Expected VTT to contain timeline 00:00:10.000, got: %s", string(vttContent)) } // Step 5: Create VTT file with styling vttStyledContent := `WEBVTT - Styled Test STYLE ::cue { color: white; background-color: black; } 1 00:00:20.000 --> 00:00:24.000 align:start position:10% This is a styled subtitle. 2 00:00:25.000 --> 00:00:28.000 align:middle This is another styled subtitle. ` vttStyledFile := filepath.Join(tempDir, "styled.vtt") if err := os.WriteFile(vttStyledFile, []byte(vttStyledContent), 0644); err != nil { t.Fatalf("Failed to create styled VTT test file: %v", err) } // Step 6: Test sync between VTT files (should preserve styling) t.Log("Testing VTT to VTT sync...") cmd = exec.Command(binaryPath, "sync", vttFile, vttStyledFile) output, err = cmd.CombinedOutput() if err != nil { t.Fatalf("Sync command failed: %v\nOutput: %s", err, output) } // Read synced VTT content syncedVttContent, err := os.ReadFile(vttStyledFile) if err != nil { t.Fatalf("Failed to read synced VTT file: %v", err) } // Verify synced content has new timings but preserves styling and text if !strings.Contains(string(syncedVttContent), "00:00:10.000 -->") { t.Errorf("Expected synced VTT to have new timing 00:00:10.000, got: %s", string(syncedVttContent)) } if !strings.Contains(string(syncedVttContent), "align:") { t.Errorf("Expected synced VTT to preserve styling, got: %s", string(syncedVttContent)) } if !strings.Contains(string(syncedVttContent), "styled") { t.Errorf("Expected synced VTT to preserve HTML formatting, got: %s", string(syncedVttContent)) } // Step 7: Test format command with VTT file t.Log("Testing VTT formatting...") cmd = exec.Command(binaryPath, "fmt", vttStyledFile) output, err = cmd.CombinedOutput() if err != nil { t.Fatalf("Format command failed: %v\nOutput: %s", err, output) } // Read formatted VTT content formattedVttContent, err := os.ReadFile(vttStyledFile) if err != nil { t.Fatalf("Failed to read formatted VTT file: %v", err) } // Verify formatted content preserves styling and has sequential cue identifiers if !strings.Contains(string(formattedVttContent), "1\n00:00:10.000") { t.Errorf("Expected formatted VTT to have sequential identifiers, got: %s", string(formattedVttContent)) } if !strings.Contains(string(formattedVttContent), "align:") { t.Errorf("Expected formatted VTT to preserve styling, got: %s", string(formattedVttContent)) } // Step 8: Test conversion to plain text t.Log("Testing VTT to TXT conversion...") cmd = exec.Command(binaryPath, "convert", vttStyledFile, txtFile) output, err = cmd.CombinedOutput() if err != nil { t.Fatalf("Convert command failed: %v\nOutput: %s", err, output) } // Verify TXT file was created if _, err := os.Stat(txtFile); os.IsNotExist(err) { t.Fatalf("TXT file was not created") } // Read TXT content txtContent, err := os.ReadFile(txtFile) if err != nil { t.Fatalf("Failed to read TXT file: %v", err) } // Verify TXT content has text but no timing or styling if strings.Contains(string(txtContent), "00:00:") { t.Errorf("Expected TXT to not contain timing information, got: %s", string(txtContent)) } if strings.Contains(string(txtContent), "align:") { t.Errorf("Expected TXT to not contain styling information, got: %s", string(txtContent)) } if !strings.Contains(string(txtContent), "styled") { t.Errorf("Expected TXT to contain text content, got: %s", string(txtContent)) } t.Log("All integration tests passed!") } // TestIntegration_ErrorHandling tests how the CLI handles error conditions func TestIntegration_ErrorHandling(t *testing.T) { // Skip if not running integration tests if os.Getenv("RUN_INTEGRATION_TESTS") == "" { t.Skip("Skipping integration tests. Set RUN_INTEGRATION_TESTS=1 to run.") } // Get the path to the built binary binaryPath := os.Getenv("BINARY_PATH") if binaryPath == "" { // Default to looking in the current directory binaryPath = "sub-cli" } // Create temporary directory for test files tempDir := t.TempDir() // Test cases testCases := []struct { name string args []string errorMsg string }{ { name: "Nonexistent source file", args: []string{"convert", "nonexistent.srt", filepath.Join(tempDir, "output.vtt")}, errorMsg: "no such file", }, { name: "Invalid source format", args: []string{"convert", filepath.Join(tempDir, "test.xyz"), filepath.Join(tempDir, "output.vtt")}, errorMsg: "unsupported format", }, { name: "Invalid target format", args: []string{"convert", filepath.Join(tempDir, "test.srt"), filepath.Join(tempDir, "output.xyz")}, errorMsg: "unsupported format", }, { name: "Sync different formats", args: []string{"sync", filepath.Join(tempDir, "test.srt"), filepath.Join(tempDir, "test.lrc")}, errorMsg: "same format", }, { name: "Format unsupported file", args: []string{"fmt", filepath.Join(tempDir, "test.txt")}, errorMsg: "unsupported format", }, } // Create a sample SRT file for testing srtFile := filepath.Join(tempDir, "test.srt") srtContent := `1 00:00:01,000 --> 00:00:04,000 This is a test subtitle. ` if err := os.WriteFile(srtFile, []byte(srtContent), 0644); err != nil { t.Fatalf("Failed to create SRT test file: %v", err) } // Create a sample LRC file for testing lrcFile := filepath.Join(tempDir, "test.lrc") lrcContent := `[00:01.00]This is a test lyric. ` if err := os.WriteFile(lrcFile, []byte(lrcContent), 0644); err != nil { t.Fatalf("Failed to create LRC test file: %v", err) } // Create a sample TXT file for testing txtFile := filepath.Join(tempDir, "test.txt") txtContent := `This is a plain text file. ` if err := os.WriteFile(txtFile, []byte(txtContent), 0644); err != nil { t.Fatalf("Failed to create TXT test file: %v", err) } // Run test cases for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { cmd := exec.Command(binaryPath, tc.args...) output, err := cmd.CombinedOutput() // We expect an error if err == nil { t.Fatalf("Expected command to fail, but it succeeded. Output: %s", output) } // Check error message if !strings.Contains(string(output), tc.errorMsg) { t.Errorf("Expected error message to contain '%s', got: %s", tc.errorMsg, output) } }) } }