Files
dingtalk-workspace-cli/internal/helpers/drive_sync_platform_test.go
chichuan 504db23823 test(drive): cover platform-only branches missed by the platform coverage gate
The platform coverage gate runs only TestAllShortcuts and
TestCrossPlatformCoverage*, so several changed statements had no platform
test exercising them:

- drive_pull.go: the smart-policy re-check that skips publication when the
  target is refreshed in place (same inode) while the download is running.
- drive_pull.go: the post-publish verifyParent failure, where the result is
  already on disk and must not be rolled back.
- drive_replace_unix.go: rename(2) replacement of an existing target; the
  Windows side already had the symmetric test.
- drive_status_windows.go: the filepath.Clean rewrite guard had no input
  reaching it, because isSafeRemoteSegment filters separators upstream.

macOS changed-code coverage: 99.8053% -> 100.0000% (1541 statements).
2026-08-13 20:55:06 +08:00

237 lines
10 KiB
Go

package helpers
import (
"context"
"errors"
"io/fs"
"os"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/testseam"
)
// forceCaseInsensitiveFS 在当前用例内把大小写探测钉成不敏感,使等价路径冲突分支
// 在大小写敏感的 CI 文件系统(ext4)上同样可达。
func forceCaseInsensitiveFS(t *testing.T) {
t.Helper()
testseam.Swap(t, &isCaseInsensitiveFS, func(string) bool { return true })
}
// ──────────────────────────────────────────────────────────
// 等价路径冲突:pull / sync 都必须全部记 failed 且一个都不落盘
// ──────────────────────────────────────────────────────────
// 远端 A.txt 与 a.txt 在大小写不敏感目标上映射到同一本地文件 → 两项都 failed,
// 绝不顺序覆盖导致静默丢文件。
func TestCrossPlatformCoverageDrivePull_equivalentPathCollisionFailsBoth(t *testing.T) {
forceCaseInsensitiveFS(t)
root := t.TempDir()
caller := pullListingCaller(
`{"name":"A.txt","type":"file","fileId":"UP","modifyTime":1},` +
`{"name":"a.txt","type":"file","fileId":"LOW","modifyTime":2}`)
swapPullDownloadPath(t, func(context.Context, string, map[string]string, string) error {
t.Error("collided entries must not be downloaded")
return nil
})
err := runDriveCmd(t, caller, "pull", "--local-folder", root, "--remote-folder", "ROOT")
var pf *drivePartialFailure
if !errors.As(err, &pf) {
t.Fatalf("expected drivePartialFailure, got %T %v", err, err)
}
for _, name := range []string{"A.txt", "a.txt"} {
if _, statErr := os.Stat(filepath.Join(root, name)); !os.IsNotExist(statErr) {
t.Errorf("%s must not be written on collision", name)
}
}
}
// sync 的 new_remote 阶段同样拒绝等价冲突项。
func TestCrossPlatformCoverageDriveSync_equivalentPathCollisionFailsBoth(t *testing.T) {
forceCaseInsensitiveFS(t)
root := t.TempDir()
withSyncTransport(t, "remote")
caller := syncCaller(map[string]string{
"ROOT": `{"result":{"items":[` +
`{"name":"A.txt","type":"file","fileId":"UP","modifyTime":1},` +
`{"name":"a.txt","type":"file","fileId":"LOW","modifyTime":2}` +
`],"nextToken":""}}`,
})
err := runDriveCmd(t, caller, "sync", "--local-folder", root, "--remote-folder", "ROOT", "--quick")
var sf *driveSyncFailure
if !errors.As(err, &sf) {
t.Fatalf("expected driveSyncFailure, got %T %v", err, err)
}
if sf.failed != 2 {
t.Errorf("failed = %d, want 2 (both collided entries)", sf.failed)
}
}
// ──────────────────────────────────────────────────────────
// detectCaseInsensitiveFS:探针名无大小写差异时回退平台默认
// ──────────────────────────────────────────────────────────
func TestCrossPlatformCoverageDetectCaseInsensitiveFS_caselessProbeFallsBackToPlatformDefault(t *testing.T) {
// 纯数字模板:ToUpper 与原名相同,无法据此判定。
testseam.Swap(t, &caseProbePattern, "1234567890*")
dir := t.TempDir()
got := detectCaseInsensitiveFS(dir)
// 回退值即平台默认;这里只断言它与平台默认一致,不写死具体布尔。
want := runtime.GOOS == "windows" || runtime.GOOS == "darwin"
if got != want {
t.Errorf("detectCaseInsensitiveFS = %v, want platform default %v", got, want)
}
}
// ──────────────────────────────────────────────────────────
// isSafeRemoteSegmentPlatform:非 Windows 平台无额外约束
// ──────────────────────────────────────────────────────────
func TestCrossPlatformCoverageIsSafeRemoteSegmentPlatform_allowsPlainNames(t *testing.T) {
for _, name := range []string{"a.txt", "报告.pdf", "with space.md"} {
if !isSafeRemoteSegmentPlatform(name) {
t.Errorf("isSafeRemoteSegmentPlatform(%q) = false, want true", name)
}
}
}
func TestCrossPlatformCoverageIsSafeRemoteSegmentPlatform_rejectsNonCanonicalWindowsName(t *testing.T) {
if runtime.GOOS != "windows" {
return
}
for _, name := range []string{
`C:relative`, `a?.txt`, `a*.txt`, `a:stream`, `a<.txt`, `a>.txt`, `a|.txt`, `a".txt`,
"name.", "name ", "...", "CON", "con.txt", "PRN", "AUX", "NUL", "COM1", "LPT9", "COM¹", "CONIN$",
// 调用方 isSafeRemoteSegment 已先过滤分隔符,所以只有直接调用才会走到
// filepath.Clean 改写这条防御分支;平台函数本身仍必须 fail closed。
`a/b`, `.\a`,
} {
if isSafeRemoteSegmentPlatform(name) {
t.Errorf("Windows unsafe segment %q must be rejected", name)
}
}
for _, name := range []string{"report.txt", "报告 2026.pdf", "COM10", "LPT0"} {
if !isSafeRemoteSegmentPlatform(name) {
t.Errorf("Windows ordinary segment %q must be accepted", name)
}
}
}
// ──────────────────────────────────────────────────────────
// WalkDir 单条目处理:Info() 与 filepath.Rel 的失败分支
// ──────────────────────────────────────────────────────────
// errDirEntry 是一个 Info() 必然报错的 fs.DirEntry。
type errDirEntry struct{ dir bool }
func (e errDirEntry) Name() string { return "entry" }
func (e errDirEntry) IsDir() bool { return e.dir }
func (e errDirEntry) Type() fs.FileMode { return 0 }
func (e errDirEntry) Info() (fs.FileInfo, error) { return nil, errTestInfo }
var errTestInfo = errors.New("simulated DirEntry.Info failure")
// irregularDirEntry 的 Info() 成功,但报告为非常规文件(如设备文件)。
type irregularDirEntry struct{}
func (irregularDirEntry) Name() string { return "dev" }
func (irregularDirEntry) IsDir() bool { return false }
func (irregularDirEntry) Type() fs.FileMode { return fs.ModeDevice }
func (irregularDirEntry) Info() (fs.FileInfo, error) { return irregularFileInfo{}, nil }
type irregularFileInfo struct{}
func (irregularFileInfo) Name() string { return "dev" }
func (irregularFileInfo) Size() int64 { return 0 }
func (irregularFileInfo) Mode() fs.FileMode { return fs.ModeDevice }
func (irregularFileInfo) ModTime() time.Time { return time.Unix(0, 0) }
func (irregularFileInfo) IsDir() bool { return false }
func (irregularFileInfo) Sys() any { return nil }
func TestCrossPlatformCoverageWalkLocalTreeEntry_errorBranches(t *testing.T) {
files := map[string]*localFile{}
// WalkDir 自身报错原样上抛。
if err := walkLocalTreeEntry("/root", "/root/a", errDirEntry{}, errTestInfo, files); !errors.Is(err, errTestInfo) {
t.Errorf("incoming error must propagate, got %v", err)
}
// 目录条目直接跳过。
if err := walkLocalTreeEntry("/root", "/root/sub", errDirEntry{dir: true}, nil, files); err != nil {
t.Errorf("directory entry must be skipped, got %v", err)
}
// Info() 失败上抛。
if err := walkLocalTreeEntry("/root", "/root/a", errDirEntry{}, nil, files); !errors.Is(err, errTestInfo) {
t.Errorf("Info() error must propagate, got %v", err)
}
// 非常规文件忽略。
if err := walkLocalTreeEntry("/root", "/root/dev", irregularDirEntry{}, nil, files); err != nil {
t.Errorf("irregular entry must be skipped, got %v", err)
}
// filepath.Rel 失败(root 绝对、path 相对)上抛。
if err := walkLocalTreeEntry("/root", "relative/path", regularDirEntry{}, nil, files); err == nil {
t.Error("filepath.Rel error must propagate")
}
if len(files) != 0 {
t.Errorf("no entry should have been indexed, got %v", files)
}
}
// regularDirEntry 的 Info() 返回一个常规文件,用于走到 filepath.Rel。
type regularDirEntry struct{}
func (regularDirEntry) Name() string { return "a" }
func (regularDirEntry) IsDir() bool { return false }
func (regularDirEntry) Type() fs.FileMode { return 0 }
func (regularDirEntry) Info() (fs.FileInfo, error) { return regularFileInfo{}, nil }
type regularFileInfo struct{}
func (regularFileInfo) Name() string { return "a" }
func (regularFileInfo) Size() int64 { return 1 }
func (regularFileInfo) Mode() fs.FileMode { return 0o644 }
func (regularFileInfo) ModTime() time.Time { return time.Unix(1, 0) }
func (regularFileInfo) IsDir() bool { return false }
func (regularFileInfo) Sys() any { return nil }
func TestCrossPlatformCoverageWalkLocalForPushEntry_errorBranches(t *testing.T) {
var dirs []string
var files []localPushFile
// WalkDir 自身报错原样上抛。
if err := walkLocalForPushEntry("/root", "/root/a", regularDirEntry{}, errTestInfo, &dirs, &files); !errors.Is(err, errTestInfo) {
t.Errorf("incoming error must propagate, got %v", err)
}
// filepath.Rel 失败上抛。
if err := walkLocalForPushEntry("/root", "relative/path", regularDirEntry{}, nil, &dirs, &files); err == nil {
t.Error("filepath.Rel error must propagate")
}
// 根目录本身跳过。
if err := walkLocalForPushEntry("/root", "/root", regularDirEntry{}, nil, &dirs, &files); err != nil {
t.Errorf("root itself must be skipped, got %v", err)
}
// Info() 失败上抛。
if err := walkLocalForPushEntry("/root", "/root/a", errDirEntry{}, nil, &dirs, &files); !errors.Is(err, errTestInfo) {
t.Errorf("Info() error must propagate, got %v", err)
}
// 非常规文件忽略。
if err := walkLocalForPushEntry("/root", "/root/dev", irregularDirEntry{}, nil, &dirs, &files); err != nil {
t.Errorf("irregular entry must be skipped, got %v", err)
}
// 目录条目进 dirs。
if err := walkLocalForPushEntry("/root", "/root/sub", errDirEntry{dir: true}, nil, &dirs, &files); err != nil {
t.Errorf("directory entry must be recorded, got %v", err)
}
if len(dirs) != 1 || dirs[0] != "sub" {
t.Errorf("dirs = %v, want [sub]", dirs)
}
if len(files) != 0 {
t.Errorf("files = %v, want empty", files)
}
}