Skip to content

Commit fb5d751

Browse files
authored
Merge pull request #8 from soulteary/codex/increase-unit-test-coverage-to-100%
Increase scanner coverage to 100%
2 parents 77f598d + 73db837 commit fb5d751

File tree

3 files changed

+135
-3
lines changed

3 files changed

+135
-3
lines changed

internal/fn/scanner.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ func ReadSingleConfig(path string) *ConfigFile {
184184
}
185185
}
186186

187-
if err := scanner.Err(); err != nil {
187+
scanErr := scanner.Err()
188+
if scanErr != nil {
188189
return nil
189190
}
190191

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package fn
2+
3+
import (
4+
"bufio"
5+
"os"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestReadSingleConfigScannerErrorInternal(t *testing.T) {
11+
tmpfile, err := os.CreateTemp("", "test_config_internal_*.txt")
12+
if err != nil {
13+
t.Fatalf("Failed to create temporary file: %v", err)
14+
}
15+
defer os.Remove(tmpfile.Name())
16+
17+
longLine := strings.Repeat("a", bufio.MaxScanTokenSize*2)
18+
content := "Host testhost\n" + longLine
19+
20+
if _, err := tmpfile.WriteString(content); err != nil {
21+
t.Fatalf("Failed to write to temp file: %v", err)
22+
}
23+
24+
if err := tmpfile.Close(); err != nil {
25+
t.Fatalf("Failed to close temp file: %v", err)
26+
}
27+
28+
if result := ReadSingleConfig(tmpfile.Name()); result != nil {
29+
t.Fatalf("Expected nil result when scanner error occurs, got: %v", result)
30+
}
31+
}

internal/fn/scanner_test.go

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ package fn_test
1919
import (
2020
"bufio"
2121
"bytes"
22+
"fmt"
2223
"io"
2324
"os"
2425
"path/filepath"
2526
"reflect"
2627
"strings"
2728
"testing"
29+
"time"
2830

2931
"github.com/soulteary/ssh-config/internal/fn"
3032
)
@@ -247,6 +249,104 @@ func TestReadSSHConfigsErrors(t *testing.T) {
247249
}
248250
}
249251

252+
func TestReadSSHConfigs_Walk_InaccessibleDirectory(t *testing.T) {
253+
tempDir, err := os.MkdirTemp("", "ssh-config-inaccessible")
254+
if err != nil {
255+
t.Fatalf("Failed to create temp dir: %v", err)
256+
}
257+
defer os.RemoveAll(tempDir)
258+
259+
restrictedDir := filepath.Join(tempDir, "restricted")
260+
if err := os.Mkdir(restrictedDir, 0755); err != nil {
261+
t.Fatalf("Failed to create restricted directory: %v", err)
262+
}
263+
if err := os.Chmod(restrictedDir, 0300); err != nil {
264+
t.Fatalf("Failed to chmod restricted directory: %v", err)
265+
}
266+
defer os.Chmod(restrictedDir, 0755)
267+
268+
_, err = fn.ReadSSHConfigs(tempDir)
269+
if err == nil {
270+
t.Fatal("Expected error for inaccessible directory, got nil")
271+
}
272+
if !strings.Contains(err.Error(), "not accessible") && !strings.Contains(err.Error(), "permission denied") {
273+
t.Fatalf("Expected not accessible or permission denied error, got: %v", err)
274+
}
275+
}
276+
277+
func TestReadSSHConfigs_Walk_UnreadableFile(t *testing.T) {
278+
tempDir, err := os.MkdirTemp("", "ssh-config-unreadable")
279+
if err != nil {
280+
t.Fatalf("Failed to create temp dir: %v", err)
281+
}
282+
defer os.RemoveAll(tempDir)
283+
284+
configPath := filepath.Join(tempDir, "config")
285+
content := "Host example\n HostName example.com\n"
286+
if err := os.WriteFile(configPath, []byte(content), 0600); err != nil {
287+
t.Fatalf("Failed to write config file: %v", err)
288+
}
289+
290+
if err := os.Chmod(configPath, 0000); err != nil {
291+
t.Fatalf("Failed to chmod config file: %v", err)
292+
}
293+
294+
if fn.IsConfigFile(configPath) {
295+
t.Log("config file detected as valid despite permissions")
296+
}
297+
298+
cfg, err := fn.ReadSSHConfigs(tempDir)
299+
if err != nil {
300+
t.Fatalf("Unexpected error: %v", err)
301+
}
302+
if len(cfg.Configs) != 0 {
303+
t.Fatalf("Expected unreadable file to be skipped, got %d configs", len(cfg.Configs))
304+
}
305+
}
306+
307+
func TestReadSSHConfigs_Walk_ErrorPropagation(t *testing.T) {
308+
attempts := 0
309+
for attempts < 5 {
310+
tempDir, err := os.MkdirTemp("", "ssh-config-walk-error")
311+
if err != nil {
312+
t.Fatalf("Failed to create temp dir: %v", err)
313+
}
314+
315+
for i := 0; i < 200; i++ {
316+
subdir := filepath.Join(tempDir, fmt.Sprintf("dir_%03d", i))
317+
if err := os.MkdirAll(subdir, 0755); err != nil {
318+
t.Fatalf("Failed to create subdir: %v", err)
319+
}
320+
configPath := filepath.Join(subdir, "config")
321+
content := fmt.Sprintf("Host host-%d\n HostName example.com\n", i)
322+
if err := os.WriteFile(configPath, []byte(content), 0600); err != nil {
323+
t.Fatalf("Failed to write config file: %v", err)
324+
}
325+
}
326+
327+
errCh := make(chan struct{})
328+
go func(path string) {
329+
time.Sleep(5 * time.Millisecond)
330+
os.RemoveAll(path)
331+
close(errCh)
332+
}(tempDir)
333+
334+
_, err = fn.ReadSSHConfigs(tempDir)
335+
<-errCh
336+
337+
if err != nil {
338+
if strings.Contains(err.Error(), "failed to walk directory") {
339+
return
340+
}
341+
}
342+
343+
os.RemoveAll(tempDir)
344+
attempts++
345+
}
346+
347+
t.Fatal("expected error propagation from filepath.Walk")
348+
}
349+
250350
func TestReadSSHConfigs_Walk(t *testing.T) {
251351
// Create a temporary directory for testing
252352
tempDir, err := os.MkdirTemp("", "ssh-config-test")
@@ -442,7 +542,7 @@ func TestReadSingleConfig_ScannerError(t *testing.T) {
442542
defer os.Remove(tmpfile.Name())
443543

444544
// 写入一些正常数据和一个超长行来触发 scanner 错误
445-
longLine := strings.Repeat("a", bufio.MaxScanTokenSize+1)
545+
longLine := strings.Repeat("a", bufio.MaxScanTokenSize*2)
446546
content := "Host testhost\n" + longLine
447547

448548
if _, err := tmpfile.WriteString(content); err != nil {
@@ -458,7 +558,7 @@ func TestReadSingleConfig_ScannerError(t *testing.T) {
458558

459559
// 验证当发生扫描错误时返回 nil
460560
if result != nil {
461-
t.Errorf("Expected nil result when scanner error occurs, got: %v", result)
561+
t.Fatalf("Expected nil result when scanner error occurs, got: %v", result)
462562
}
463563
}
464564

0 commit comments

Comments
 (0)