diff --git a/internal/helpers/drive_status.go b/internal/helpers/drive_status.go index 586c004c..adacdae5 100644 --- a/internal/helpers/drive_status.go +++ b/internal/helpers/drive_status.go @@ -470,6 +470,11 @@ var md5File = func(path string) (string, error) { // FileInfo 来复现 Windows 上无法创建的符号链接场景。 var statusRootLstat = os.Lstat +// statusWalkDir 是本地目录遍历的注入 seam。改造根类型校验后,filepath.WalkDir +// 的错误分支(walkLocalTreeEntry 之外的外层错误)在 macOS/Windows 上都难以自然 +// 触发,需要 seam 让该分支可以确定性回归。 +var statusWalkDir = filepath.WalkDir + // walkLocalTree 递归遍历本地目录,只收集常规文件(跳过符号链接、设备文件等)。 // rel_path 统一使用 / 作为分隔符,相对于 root。 // @@ -491,7 +496,7 @@ func walkLocalTree(root string) (map[string]*localFile, error) { return nil, fmt.Errorf("本地根目录 %q 不是目录", root) } files := make(map[string]*localFile) - err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + err = statusWalkDir(root, func(path string, d fs.DirEntry, err error) error { return walkLocalTreeEntry(root, path, d, err, files) }) if err != nil { diff --git a/internal/helpers/drive_status_coverage_test.go b/internal/helpers/drive_status_coverage_test.go index afe13dcf..c750695d 100644 --- a/internal/helpers/drive_status_coverage_test.go +++ b/internal/helpers/drive_status_coverage_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "io" + "io/fs" "os" "path/filepath" "runtime" @@ -325,6 +326,20 @@ func TestCrossPlatformCoverageWalkLocalTree_rejectsSymlinkRootReal(t *testing.T) } } +// 根类型校验通过后 WalkDir 内部返回的错误必须原样上抛,不能被吞掉;根消失或 +// 权限变更之类的情形在 Linux/Windows runner 上都难以稳定复现,用 seam 注入。 +func TestCrossPlatformCoverageWalkLocalTree_propagatesWalkDirError(t *testing.T) { + testseam.Swap(t, &statusWalkDir, func(string, fs.WalkDirFunc) error { + return errWalkDirSentinel + }) + dir := t.TempDir() + if _, err := walkLocalTree(dir); !errors.Is(err, errWalkDirSentinel) { + t.Fatalf("walk dir error must be surfaced, got %v", err) + } +} + +var errWalkDirSentinel = errors.New("simulated walk dir failure") + // 根路径既非目录也非符号链接(例如普通文件被误传)时,也必须报错而非空索引。 func TestCrossPlatformCoverageWalkLocalTree_rejectsNonDirectoryRoot(t *testing.T) { file := filepath.Join(t.TempDir(), "not-a-dir")