Skip to content

Commit 3dd04d5

Browse files
committed
feat: Optimize code and cover more test cases
✨ 主要改进: - 总体覆盖率从78.3%提升至94.3% (+16%) - 新增20+个测试函数,覆盖50+个边界情况 - 全面测试错误处理和异常场景 🔧 具体改进: - count.go: 添加无效输入类型测试 (87.5%) - file.go: 添加空文件、权限错误测试 (93.3%) - server.go: 添加空请求体、无效JSON测试 - export.go: 添加无文件名、空数据导出测试 (80-85.7%) - helper.go: 添加路径处理边界情况测试 (75-87.5%) - dir.go: 添加并发处理、相对路径测试 (90.9%) - ignore.go: 添加混合内容解析测试 (92.3%) - errors.go: 添加复杂错误上下文测试 (100%) 🎯 新增测试场景: - ✅ 空文件处理 - ✅ 文件权限错误 - ✅ 无效输入类型 - ✅ 网络请求错误 - ✅ 并发处理边界情况 - ✅ 路径解析错误 - ✅ 文件导出错误 - ✅ CLI集成测试 📊 覆盖率达到100%的函数: - getTotal (helper.go) - Run (server.go) - IsIgnored & IsIgnoredWithError (dir.go) - WithContext (errors.go) - 所有cmd_utils.go函数 - 所有错误构造函数
1 parent bb9652e commit 3dd04d5

16 files changed

Lines changed: 1351 additions & 68 deletions

.gitignore

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,6 @@ test_output*
114114
counter.xlsx
115115
*.csv
116116
*.xlsx
117-
test.csv
118-
test.xlsx
119117

120118
# Coverage files
121-
coverage.out
122-
coverage.html
119+
coverage*

cmd_integration_test.go

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package wordcounter_test
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"strings"
7+
"testing"
8+
)
9+
10+
// TestCLIIntegration tests the CLI commands by running the actual binary
11+
func TestCLIIntegration(t *testing.T) {
12+
// Build the CLI binary first
13+
binaryPath := "./wcg_test"
14+
buildCmd := exec.Command("go", "build", "-o", binaryPath, "./cmd/wordcounter")
15+
if err := buildCmd.Run(); err != nil {
16+
t.Fatalf("Failed to build CLI binary: %v", err)
17+
}
18+
defer os.Remove(binaryPath)
19+
20+
// Create test files for integration testing
21+
testFiles := []struct {
22+
name string
23+
content string
24+
}{
25+
{"testdata/cli_test1.txt", "Hello 世界 test"},
26+
{"testdata/cli_test2.md", "测试 content here"},
27+
}
28+
29+
for _, tf := range testFiles {
30+
err := os.WriteFile(tf.name, []byte(tf.content), 0644)
31+
if err != nil {
32+
t.Fatalf("Failed to create test file %s: %v", tf.name, err)
33+
}
34+
defer os.Remove(tf.name)
35+
}
36+
37+
tests := []struct {
38+
name string
39+
args []string
40+
wantErr bool
41+
wantOutput string
42+
description string
43+
}{
44+
{
45+
name: "Count directory with table output",
46+
args: []string{"count", "-m", "dir", "testdata"},
47+
wantErr: false,
48+
wantOutput: "FILE",
49+
description: "Should output table with FILE header",
50+
},
51+
{
52+
name: "Count directory with total",
53+
args: []string{"count", "-m", "dir", "--total", "testdata"},
54+
wantErr: false,
55+
wantOutput: "Total",
56+
description: "Should output table with Total row",
57+
},
58+
{
59+
name: "Count single file",
60+
args: []string{"count", "-m", "file", "testdata/cli_test1.txt"},
61+
wantErr: false,
62+
wantOutput: "cli_test1.txt",
63+
description: "Should output table with filename",
64+
},
65+
{
66+
name: "Count directory with CSV export",
67+
args: []string{"count", "-m", "dir", "-e", "csv", "--exportPath", "test_cli.csv", "testdata"},
68+
wantErr: false,
69+
wantOutput: "File,Lines,ChineseChars",
70+
description: "Should export CSV and show CSV content",
71+
},
72+
{
73+
name: "Count directory with Excel export",
74+
args: []string{"count", "-m", "dir", "-e", "excel", "--exportPath", "test_cli.xlsx", "testdata"},
75+
wantErr: false,
76+
wantOutput: "Excel file exported to",
77+
description: "Should export Excel and show success message",
78+
},
79+
{
80+
name: "Count with relative paths",
81+
args: []string{"count", "-m", "dir", "-r", "testdata"},
82+
wantErr: false,
83+
wantOutput: "cli_test1.txt",
84+
description: "Should show relative paths",
85+
},
86+
{
87+
name: "Invalid mode",
88+
args: []string{"count", "-m", "invalid", "testdata"},
89+
wantErr: true,
90+
wantOutput: "",
91+
description: "Should fail with invalid mode",
92+
},
93+
{
94+
name: "No arguments",
95+
args: []string{"count"},
96+
wantErr: true,
97+
wantOutput: "",
98+
description: "Should fail with no path argument",
99+
},
100+
{
101+
name: "Non-existent path",
102+
args: []string{"count", "-m", "dir", "/path/that/does/not/exist"},
103+
wantErr: true,
104+
wantOutput: "",
105+
description: "Should fail with non-existent path",
106+
},
107+
}
108+
109+
for _, tt := range tests {
110+
t.Run(tt.name, func(t *testing.T) {
111+
cmd := exec.Command(binaryPath, tt.args...)
112+
output, err := cmd.CombinedOutput()
113+
outputStr := string(output)
114+
115+
if tt.wantErr {
116+
if err == nil {
117+
t.Errorf("Expected command to fail, but it succeeded. Output: %s", outputStr)
118+
}
119+
} else {
120+
if err != nil {
121+
t.Errorf("Expected command to succeed, but it failed with error: %v. Output: %s", err, outputStr)
122+
}
123+
if tt.wantOutput != "" && !strings.Contains(outputStr, tt.wantOutput) {
124+
t.Errorf("Expected output to contain '%s', but got: %s", tt.wantOutput, outputStr)
125+
}
126+
}
127+
128+
// Clean up any generated files
129+
os.Remove("test_cli.csv")
130+
os.Remove("test_cli.xlsx")
131+
})
132+
}
133+
}
134+
135+
// TestCLIServerIntegration tests the server command
136+
func TestCLIServerIntegration(t *testing.T) {
137+
// Build the CLI binary first
138+
binaryPath := "./wcg_server_test"
139+
buildCmd := exec.Command("go", "build", "-o", binaryPath, "./cmd/wordcounter")
140+
if err := buildCmd.Run(); err != nil {
141+
t.Fatalf("Failed to build CLI binary: %v", err)
142+
}
143+
defer os.Remove(binaryPath)
144+
145+
// Test server command (this will start and we need to kill it quickly)
146+
t.Run("Server command starts", func(t *testing.T) {
147+
cmd := exec.Command(binaryPath, "server", "--port", "8081")
148+
149+
// Start the command
150+
if err := cmd.Start(); err != nil {
151+
t.Errorf("Failed to start server command: %v", err)
152+
return
153+
}
154+
155+
// Kill it immediately to avoid hanging
156+
if err := cmd.Process.Kill(); err != nil {
157+
t.Errorf("Failed to kill server process: %v", err)
158+
}
159+
160+
// Wait for it to finish
161+
cmd.Wait()
162+
})
163+
}
164+
165+
// TestCLIHelpAndVersion tests help and version commands
166+
func TestCLIHelpAndVersion(t *testing.T) {
167+
// Build the CLI binary first
168+
binaryPath := "./wcg_help_test"
169+
buildCmd := exec.Command("go", "build", "-o", binaryPath, "./cmd/wordcounter")
170+
if err := buildCmd.Run(); err != nil {
171+
t.Fatalf("Failed to build CLI binary: %v", err)
172+
}
173+
defer os.Remove(binaryPath)
174+
175+
tests := []struct {
176+
name string
177+
args []string
178+
wantOutput string
179+
}{
180+
{
181+
name: "Help command",
182+
args: []string{"--help"},
183+
wantOutput: "wordcounter is a simple tool",
184+
},
185+
{
186+
name: "Count help",
187+
args: []string{"count", "--help"},
188+
wantOutput: "Count for a file or directory",
189+
},
190+
{
191+
name: "Server help",
192+
args: []string{"server", "--help"},
193+
wantOutput: "Run wordcounter as a server",
194+
},
195+
}
196+
197+
for _, tt := range tests {
198+
t.Run(tt.name, func(t *testing.T) {
199+
cmd := exec.Command(binaryPath, tt.args...)
200+
output, err := cmd.CombinedOutput()
201+
outputStr := string(output)
202+
203+
// Help commands should succeed
204+
if err != nil {
205+
t.Errorf("Help command failed: %v. Output: %s", err, outputStr)
206+
}
207+
208+
if !strings.Contains(outputStr, tt.wantOutput) {
209+
t.Errorf("Expected output to contain '%s', but got: %s", tt.wantOutput, outputStr)
210+
}
211+
})
212+
}
213+
}

0 commit comments

Comments
 (0)