sub-cli/cmd/root_test.go
2025-04-23 19:22:41 +08:00

487 lines
12 KiB
Go

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")
}
}
// TestExecute_SyncCommand tests the sync command through Execute
func TestExecute_SyncCommand(t *testing.T) {
// Save original args
oldArgs := os.Args
defer func() { os.Args = oldArgs }()
// Create temporary test directory
tempDir := t.TempDir()
// Create source and target files
sourceFile := filepath.Join(tempDir, "source.lrc")
targetFile := filepath.Join(tempDir, "target.lrc")
if err := os.WriteFile(sourceFile, []byte("[00:01.00]Test line"), 0644); err != nil {
t.Fatalf("Failed to create source file: %v", err)
}
if err := os.WriteFile(targetFile, []byte("[00:10.00]Target line"), 0644); err != nil {
t.Fatalf("Failed to create target file: %v", err)
}
// Set up test environment
outBuf, cleanup := setupTestEnv()
// Set args for sync command
os.Args = []string{"sub-cli", "sync", sourceFile, targetFile}
// Execute command
Execute()
// Get output
cleanup()
output := outBuf.String()
// Verify no error message or expected error format
if strings.Contains(output, "Error:") && !strings.Contains(output, "Error: ") {
t.Errorf("Expected formatted error or no error, got: %s", output)
}
}
// TestExecute_ConvertCommand tests the convert command through Execute
func TestExecute_ConvertCommand(t *testing.T) {
// Save original args
oldArgs := os.Args
defer func() { os.Args = oldArgs }()
// 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.lrc")
// Set up test environment
outBuf, cleanup := setupTestEnv()
// Set args for convert command
os.Args = []string{"sub-cli", "convert", sourceFile, targetFile}
// Execute command
Execute()
// 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 exists
if _, err := os.Stat(targetFile); os.IsNotExist(err) {
t.Errorf("Target file was not created")
}
}
// 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")
}
}
// TestHandleFormat_Error tests the error path in handleFormat
func TestHandleFormat_Error(t *testing.T) {
// Set up test environment
outBuf, cleanup := setupTestEnv()
// Execute format command with non-existent file
nonExistentFile := "/non/existent/path.srt"
handleFormat([]string{nonExistentFile})
// Get output
cleanup()
output := outBuf.String()
// Verify error message is printed
if !strings.Contains(output, "Error:") {
t.Errorf("Expected error message for non-existent file, got: %s", output)
}
}