Clear all golangci-lint findings and make the CI build green (closes #1)
All checks were successful
check / check (push) Successful in 35s
All checks were successful
check / check (push) Successful in 35s
Fix every golangci-lint finding under the repo's standard .golangci.yml (from ~192 down to 0 under the pinned v2.10.1) without changing program behavior: - gochecknoglobals: replace the verbose/quiet/exclude* package globals with an options struct threaded through the command implementations. - err113: introduce package-level sentinel errors and wrap them with %w. - errcheck: check or explicitly discard every previously unchecked error (bar.Add/Finish, deferred Close, verbose writes). - forbidigo: route verbose output through os.Stdout instead of fmt.Print*. - noinlineerr / wsl_v5 / nlreturn / gofmt: split inline error checks and normalize whitespace. - complexity (cyclop/gocognit/nestif): extract small behavior-preserving helpers (runOverPaths, countAndBar, walkSkip, clearOne, checkOne, missingChecksum, reportCheck). - mnd/lll/nonamedreturns/revive/modernize/nilnil: named constants, wrapped lines, unnamed returns, doc comments, SplitSeq, non-(nil,nil) returns. - paralleltest/thelper: mark tests parallel (now race-safe with no shared globals) and add t.Helper(); probe the real xattr key so the guard is accurate. Also make the tests actually runnable in CI rather than skipping: - move the xattr keys into the user.* namespace (user.berlin.sneak.app.*), which Linux requires for regular-file xattrs; macOS treats the whole string as an opaque name, so behavior is unchanged there. - run the Docker builder's checks as an unprivileged user so the permission tests are meaningful (root bypasses file mode bits).
This commit was merged in pull request #3.
This commit is contained in:
186
attrsum_test.go
186
attrsum_test.go
@@ -10,23 +10,58 @@ import (
|
||||
"github.com/pkg/xattr"
|
||||
)
|
||||
|
||||
func skipIfNoXattr(t *testing.T, path string) {
|
||||
if err := xattr.Set(path, "user.test", []byte("1")); err != nil {
|
||||
t.Skipf("skipping: xattr not supported: %v", err)
|
||||
} else {
|
||||
_ = xattr.Remove(path, "user.test")
|
||||
const (
|
||||
dirPerm = 0o755
|
||||
filePerm = 0o644
|
||||
noPerm = 0o000
|
||||
|
||||
// futureSkew advances a file's mtime far enough to be unambiguously
|
||||
// newer than a previously recorded sumtime.
|
||||
futureSkew = 2 * time.Second
|
||||
)
|
||||
|
||||
// skipIfNoXattr skips the test unless the filesystem under dir supports the
|
||||
// extended-attribute namespace the program actually uses. Probing with the
|
||||
// real checksumKey (rather than a user.* key) matters on Linux, where regular
|
||||
// files only accept xattrs in the user.* namespace and the program's
|
||||
// berlin.sneak.* keys yield "operation not supported".
|
||||
func skipIfNoXattr(t *testing.T, dir string) {
|
||||
t.Helper()
|
||||
|
||||
probe := filepath.Join(dir, "xattr-probe")
|
||||
|
||||
err := os.WriteFile(probe, []byte("probe"), filePerm)
|
||||
if err != nil {
|
||||
t.Fatalf("write probe file: %v", err)
|
||||
}
|
||||
|
||||
defer func() { _ = os.Remove(probe) }()
|
||||
|
||||
err = xattr.Set(probe, checksumKey, []byte("1"))
|
||||
if err != nil {
|
||||
t.Skipf("skipping: xattr namespace %q not supported: %v", checksumKey, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_ = xattr.Remove(probe, checksumKey)
|
||||
}
|
||||
|
||||
func writeFile(t *testing.T, root, name, content string) string {
|
||||
t.Helper()
|
||||
|
||||
p := filepath.Join(root, name)
|
||||
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
|
||||
|
||||
err := os.MkdirAll(filepath.Dir(p), dirPerm)
|
||||
if err != nil {
|
||||
t.Fatalf("mkdir: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(p, []byte(content), 0o644); err != nil {
|
||||
|
||||
err = os.WriteFile(p, []byte(content), filePerm)
|
||||
if err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -35,28 +70,46 @@ func newTestStats() *Stats {
|
||||
}
|
||||
|
||||
func TestSumAddAndUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := &options{}
|
||||
dir := t.TempDir()
|
||||
skipIfNoXattr(t, dir)
|
||||
|
||||
f := writeFile(t, dir, "a.txt", "hello")
|
||||
|
||||
if err := ProcessSumAdd(dir, newTestStats(), nil); err != nil {
|
||||
err := processSumAdd(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("add: %v", err)
|
||||
}
|
||||
if _, err := xattr.Get(f, checksumKey); err != nil {
|
||||
|
||||
_, err = xattr.Get(f, checksumKey)
|
||||
if err != nil {
|
||||
t.Fatalf("checksum missing: %v", err)
|
||||
}
|
||||
|
||||
tsb, _ := xattr.Get(f, sumTimeKey)
|
||||
origTime, _ := time.Parse(time.RFC3339Nano, string(tsb))
|
||||
|
||||
os.WriteFile(f, []byte(strings.ToUpper("hello")), 0o644)
|
||||
now := time.Now().Add(2 * time.Second)
|
||||
os.Chtimes(f, now, now)
|
||||
err = os.WriteFile(f, []byte(strings.ToUpper("hello")), filePerm)
|
||||
if err != nil {
|
||||
t.Fatalf("rewrite: %v", err)
|
||||
}
|
||||
|
||||
if err := ProcessSumUpdate(dir, newTestStats(), nil); err != nil {
|
||||
now := time.Now().Add(futureSkew)
|
||||
|
||||
err = os.Chtimes(f, now, now)
|
||||
if err != nil {
|
||||
t.Fatalf("chtimes: %v", err)
|
||||
}
|
||||
|
||||
err = processSumUpdate(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("update: %v", err)
|
||||
}
|
||||
|
||||
tsb2, _ := xattr.Get(f, sumTimeKey)
|
||||
|
||||
newTime, _ := time.Parse(time.RFC3339Nano, string(tsb2))
|
||||
if !newTime.After(origTime) {
|
||||
t.Fatalf("sumtime not updated")
|
||||
@@ -64,46 +117,74 @@ func TestSumAddAndUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessCheckIntegration(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := &options{}
|
||||
dir := t.TempDir()
|
||||
skipIfNoXattr(t, dir)
|
||||
|
||||
writeFile(t, dir, "b.txt", "world")
|
||||
if err := ProcessSumAdd(dir, newTestStats(), nil); err != nil {
|
||||
|
||||
err := processSumAdd(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("add: %v", err)
|
||||
}
|
||||
if err := ProcessCheck(dir, false, newTestStats(), nil); err != nil {
|
||||
|
||||
err = processCheck(opts, dir, false, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("check ok: %v", err)
|
||||
}
|
||||
|
||||
f := filepath.Join(dir, "b.txt")
|
||||
os.WriteFile(f, []byte("corrupt"), 0o644)
|
||||
|
||||
if err := ProcessCheck(dir, false, newTestStats(), nil); err == nil {
|
||||
err = os.WriteFile(f, []byte("corrupt"), filePerm)
|
||||
if err != nil {
|
||||
t.Fatalf("corrupt: %v", err)
|
||||
}
|
||||
|
||||
err = processCheck(opts, dir, false, newTestStats(), nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expected mismatch error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearRemovesAttrs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := &options{}
|
||||
dir := t.TempDir()
|
||||
skipIfNoXattr(t, dir)
|
||||
|
||||
f := writeFile(t, dir, "c.txt", "data")
|
||||
if err := ProcessSumAdd(dir, newTestStats(), nil); err != nil {
|
||||
|
||||
err := processSumAdd(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("add: %v", err)
|
||||
}
|
||||
|
||||
if err := ProcessClear(dir, newTestStats(), nil); err != nil {
|
||||
err = processClear(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("clear: %v", err)
|
||||
}
|
||||
if _, err := xattr.Get(f, checksumKey); err == nil {
|
||||
|
||||
_, err = xattr.Get(f, checksumKey)
|
||||
if err == nil {
|
||||
t.Fatalf("checksum still present after clear")
|
||||
}
|
||||
if _, err := xattr.Get(f, sumTimeKey); err == nil {
|
||||
|
||||
_, err = xattr.Get(f, sumTimeKey)
|
||||
if err == nil {
|
||||
t.Fatalf("sumtime still present after clear")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExcludeDotfilesAndPatterns(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := &options{
|
||||
excludeDotfiles: true,
|
||||
excludePatterns: []string{"*.me"},
|
||||
}
|
||||
dir := t.TempDir()
|
||||
skipIfNoXattr(t, dir)
|
||||
|
||||
@@ -111,61 +192,82 @@ func TestExcludeDotfilesAndPatterns(t *testing.T) {
|
||||
keep := writeFile(t, dir, "keep.txt", "keep")
|
||||
skip := writeFile(t, dir, "skip.me", "skip")
|
||||
|
||||
oldDot := excludeDotfiles
|
||||
oldPat := excludePatterns
|
||||
excludeDotfiles = true
|
||||
excludePatterns = []string{"*.me"}
|
||||
defer func() { excludeDotfiles, excludePatterns = oldDot, oldPat }()
|
||||
|
||||
if err := ProcessSumAdd(dir, newTestStats(), nil); err != nil {
|
||||
err := processSumAdd(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("add with excludes: %v", err)
|
||||
}
|
||||
|
||||
if _, err := xattr.Get(keep, checksumKey); err != nil {
|
||||
_, err = xattr.Get(keep, checksumKey)
|
||||
if err != nil {
|
||||
t.Fatalf("expected xattr on keep.txt: %v", err)
|
||||
}
|
||||
if _, err := xattr.Get(hidden, checksumKey); err == nil {
|
||||
|
||||
_, err = xattr.Get(hidden, checksumKey)
|
||||
if err == nil {
|
||||
t.Fatalf(".hidden should have been excluded")
|
||||
}
|
||||
if _, err := xattr.Get(skip, checksumKey); err == nil {
|
||||
|
||||
_, err = xattr.Get(skip, checksumKey)
|
||||
if err == nil {
|
||||
t.Fatalf("skip.me should have been excluded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipBrokenSymlink(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := &options{}
|
||||
dir := t.TempDir()
|
||||
skipIfNoXattr(t, dir)
|
||||
|
||||
// Create a dangling symlink
|
||||
// Create a dangling symlink.
|
||||
link := filepath.Join(dir, "dangling.lnk")
|
||||
if err := os.Symlink(filepath.Join(dir, "nonexistent.txt"), link); err != nil {
|
||||
|
||||
err := os.Symlink(filepath.Join(dir, "nonexistent.txt"), link)
|
||||
if err != nil {
|
||||
t.Fatalf("symlink: %v", err)
|
||||
}
|
||||
|
||||
// Should not error and should not create xattrs on link
|
||||
if err := ProcessSumAdd(dir, newTestStats(), nil); err != nil {
|
||||
t.Fatalf("ProcessSumAdd with symlink: %v", err)
|
||||
// Should not error and should not create xattrs on link.
|
||||
err = processSumAdd(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("processSumAdd with symlink: %v", err)
|
||||
}
|
||||
if _, err := xattr.Get(link, checksumKey); err == nil {
|
||||
|
||||
_, err = xattr.Get(link, checksumKey)
|
||||
if err == nil {
|
||||
t.Fatalf("symlink should not have xattr")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPermissionErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := &options{}
|
||||
dir := t.TempDir()
|
||||
skipIfNoXattr(t, dir)
|
||||
|
||||
secret := writeFile(t, dir, "secret.txt", "data")
|
||||
os.Chmod(secret, 0o000)
|
||||
defer os.Chmod(secret, 0o644)
|
||||
|
||||
if err := ProcessSumAdd(dir, newTestStats(), nil); err == nil {
|
||||
err := os.Chmod(secret, noPerm)
|
||||
if err != nil {
|
||||
t.Fatalf("chmod: %v", err)
|
||||
}
|
||||
|
||||
defer func() { _ = os.Chmod(secret, filePerm) }()
|
||||
|
||||
err = processSumAdd(opts, dir, newTestStats(), nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expected permission error, got nil")
|
||||
}
|
||||
if err := ProcessSumUpdate(dir, newTestStats(), nil); err == nil {
|
||||
|
||||
err = processSumUpdate(opts, dir, newTestStats(), nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expected permission error on update, got nil")
|
||||
}
|
||||
if err := ProcessCheck(dir, false, newTestStats(), nil); err == nil {
|
||||
|
||||
err = processCheck(opts, dir, false, newTestStats(), nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expected permission error on check, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user