package cmd import ( "bytes" "io" "os" "path/filepath" "strings" "testing" "sub-cli/internal/config" ) // setupTestEnv creates a testing environment with redirected stdout // and returns the output buffer and cleanup function func setupTestEnv() (*bytes.Buffer, func()) { // Save original stdout oldStdout := os.Stdout // Create pipe to capture stdout r, w, _ := os.Pipe() os.Stdout = w // Create buffer to store output outBuf := &bytes.Buffer{} // Create cleanup function cleanup := func() { // Restore original stdout os.Stdout = oldStdout // Close writer w.Close() // Read from pipe io.Copy(outBuf, r) r.Close() } return outBuf, cleanup } // TestExecute_Version tests the version command func TestExecute_Version(t *testing.T) { // Save original args oldArgs := os.Args defer func() { os.Args = oldArgs }() // Set up test environment outBuf, cleanup := setupTestEnv() // Set args for version command os.Args = []string{"sub-cli", "version"} // Execute command Execute() // Get output cleanup() output := outBuf.String() // Verify output expectedOutput := "sub-cli version " + config.Version if !strings.Contains(output, expectedOutput) { t.Errorf("Expected version output to contain '%s', got '%s'", expectedOutput, output) } } // TestExecute_Help tests the help command func TestExecute_Help(t *testing.T) { // Save original args oldArgs := os.Args defer func() { os.Args = oldArgs }() // Set up test environment outBuf, cleanup := setupTestEnv() // Set args for help command os.Args = []string{"sub-cli", "help"} // Execute command Execute() // Get output cleanup() output := outBuf.String() // Verify output contains usage information if !strings.Contains(output, "Usage:") { t.Errorf("Expected help output to contain usage information") } if !strings.Contains(output, "Commands:") { t.Errorf("Expected help output to contain commands information") } } // TestExecute_NoArgs tests execution with no arguments func TestExecute_NoArgs(t *testing.T) { // Save original args oldArgs := os.Args defer func() { os.Args = oldArgs }() // Set up test environment outBuf, cleanup := setupTestEnv() // Set args with no command os.Args = []string{"sub-cli"} // Execute command Execute() // Get output cleanup() output := outBuf.String() // Verify output contains usage information if !strings.Contains(output, "Usage:") { t.Errorf("Expected output to contain usage information when no args provided") } } // TestExecute_UnknownCommand tests execution with unknown command func TestExecute_UnknownCommand(t *testing.T) { // Save original args oldArgs := os.Args defer func() { os.Args = oldArgs }() // Set up test environment outBuf, cleanup := setupTestEnv() // Set args with unknown command os.Args = []string{"sub-cli", "unknown-command"} // Execute command Execute() // Get output cleanup() output := outBuf.String() // Verify output if !strings.Contains(output, "Unknown command") { t.Errorf("Expected output to contain 'Unknown command' message") } if !strings.Contains(output, "Usage:") { t.Errorf("Expected output to contain usage information when unknown command provided") } } // TestHandleSync tests the sync command func TestHandleSync(t *testing.T) { // Create temporary test directory tempDir := t.TempDir() // Create source file sourceContent := `[00:01.00]This is line one. [00:05.00]This is line two.` sourceFile := filepath.Join(tempDir, "source.lrc") if err := os.WriteFile(sourceFile, []byte(sourceContent), 0644); err != nil { t.Fatalf("Failed to create source file: %v", err) } // Create target file targetContent := `[00:10.00]This is target line one. [00:20.00]This is target line two.` targetFile := filepath.Join(tempDir, "target.lrc") if err := os.WriteFile(targetFile, []byte(targetContent), 0644); err != nil { t.Fatalf("Failed to create target file: %v", err) } // Set up test environment outBuf, cleanup := setupTestEnv() // Execute sync command handleSync([]string{sourceFile, targetFile}) // Get output cleanup() output := outBuf.String() // Verify no error message if strings.Contains(output, "Error:") { t.Errorf("Expected no error, but got: %s", output) } // Verify target file has been modified modifiedContent, err := os.ReadFile(targetFile) if err != nil { t.Fatalf("Failed to read modified target file: %v", err) } // Check that target file now has source timings if !strings.Contains(string(modifiedContent), "[00:01.000]") { t.Errorf("Expected modified target to contain source timing [00:01.000], got: %s", string(modifiedContent)) } // Check that target content is preserved if !strings.Contains(string(modifiedContent), "This is target line one.") { t.Errorf("Expected modified target to preserve content 'This is target line one.', got: %s", string(modifiedContent)) } } // TestHandleSync_NoArgs tests sync command with insufficient arguments func TestHandleSync_NoArgs(t *testing.T) { // Set up test environment outBuf, cleanup := setupTestEnv() // Execute sync command with no args handleSync([]string{}) // Get output cleanup() output := outBuf.String() // Verify output contains usage information if !strings.Contains(output, "Usage: sub-cli sync") { t.Errorf("Expected sync usage information when no args provided") } } // TestHandleSync_OneArg tests sync command with only one argument func TestHandleSync_OneArg(t *testing.T) { // Set up test environment outBuf, cleanup := setupTestEnv() // Execute sync command with one arg handleSync([]string{"source.lrc"}) // Get output cleanup() output := outBuf.String() // Verify output contains usage information if !strings.Contains(output, "Usage: sub-cli sync") { t.Errorf("Expected sync usage information when only one arg provided") } } // TestHandleConvert tests the convert command func TestHandleConvert(t *testing.T) { // Create temporary test directory tempDir := t.TempDir() // Create source file sourceContent := `1 00:00:01,000 --> 00:00:04,000 This is a test subtitle.` sourceFile := filepath.Join(tempDir, "source.srt") if err := os.WriteFile(sourceFile, []byte(sourceContent), 0644); err != nil { t.Fatalf("Failed to create source file: %v", err) } // Define target file targetFile := filepath.Join(tempDir, "target.vtt") // Set up test environment outBuf, cleanup := setupTestEnv() // Execute convert command handleConvert([]string{sourceFile, targetFile}) // Get output cleanup() output := outBuf.String() // Verify no error message if strings.Contains(output, "Error:") { t.Errorf("Expected no error, but got: %s", output) } // Verify target file has been created if _, err := os.Stat(targetFile); os.IsNotExist(err) { t.Errorf("Target file was not created") } // Verify target file content targetContent, err := os.ReadFile(targetFile) if err != nil { t.Fatalf("Failed to read target file: %v", err) } // Check that target file has VTT format if !strings.Contains(string(targetContent), "WEBVTT") { t.Errorf("Expected target file to have WEBVTT header, got: %s", string(targetContent)) } // Check that content is preserved if !strings.Contains(string(targetContent), "This is a test subtitle.") { t.Errorf("Expected target file to preserve content, got: %s", string(targetContent)) } } // TestHandleConvert_NoArgs tests convert command with insufficient arguments func TestHandleConvert_NoArgs(t *testing.T) { // Set up test environment outBuf, cleanup := setupTestEnv() // Execute convert command with no args handleConvert([]string{}) // Get output cleanup() output := outBuf.String() // Verify output contains usage information if !strings.Contains(output, "Usage: sub-cli convert") { t.Errorf("Expected convert usage information when no args provided") } } // TestHandleFormat tests the fmt command func TestHandleFormat(t *testing.T) { // Create temporary test directory tempDir := t.TempDir() // Create test file with non-sequential numbers content := `2 00:00:05,000 --> 00:00:08,000 This is the second line. 1 00:00:01,000 --> 00:00:04,000 This is the first line.` testFile := filepath.Join(tempDir, "test.srt") if err := os.WriteFile(testFile, []byte(content), 0644); err != nil { t.Fatalf("Failed to create test file: %v", err) } // Set up test environment outBuf, cleanup := setupTestEnv() // Execute fmt command handleFormat([]string{testFile}) // Get output cleanup() output := outBuf.String() // Verify no error message if strings.Contains(output, "Error:") { t.Errorf("Expected no error, but got: %s", output) } // Verify file has been modified modifiedContent, err := os.ReadFile(testFile) if err != nil { t.Fatalf("Failed to read modified file: %v", err) } // Check that entries are correctly numbered - don't assume ordering by timestamp contentStr := string(modifiedContent) // Just check that identifiers 1 and 2 exist and content is preserved if !strings.Contains(contentStr, "1\n00:00:") && !strings.Contains(contentStr, "2\n00:00:") { t.Errorf("Output should contain sequential identifiers (1 and 2)") } // Check content preservation if !strings.Contains(contentStr, "This is the first line.") || !strings.Contains(contentStr, "This is the second line.") { t.Errorf("Output should preserve all content") } } // TestHandleFormat_NoArgs tests fmt command with no arguments func TestHandleFormat_NoArgs(t *testing.T) { // Set up test environment outBuf, cleanup := setupTestEnv() // Execute fmt command with no args handleFormat([]string{}) // Get output cleanup() output := outBuf.String() // Verify output contains usage information if !strings.Contains(output, "Usage: sub-cli fmt") { t.Errorf("Expected fmt usage information when no args provided") } }