1package capturefile
2
3import (
4 "errors"
5 "io/fs"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "github.com/bnema/nefercap/internal/ports"
11)
12
13func TestCreateIsExclusiveAndPrivate(t *testing.T) {
14 path := filepath.Join(t.TempDir(), "a")
15 f, res, err := Create(path)
16 if err != nil {
17 t.Fatal(err)
18 }
19 defer f.Close()
20 if info, err := os.Stat(path); err != nil || info.Mode().Perm() != 0o600 {
21 t.Fatalf("mode = %v, err = %v; want 0600", info.Mode(), err)
22 }
23 _, _, err = Create(path)
24 if !errors.Is(err, ports.ErrPathExists) || !errors.Is(err, fs.ErrExist) {
25 t.Fatalf("got %v, want both ErrPathExists and fs.ErrExist", err)
26 }
27 var pe *fs.PathError
28 if !errors.As(err, &pe) {
29 t.Fatalf("path error detail lost: %v", err)
30 }
31 if err := res.Remove(); err != nil {
32 t.Fatal(err)
33 }
34 if _, err := os.Lstat(path); !errors.Is(err, fs.ErrNotExist) {
35 t.Fatalf("own file not removed: %v", err)
36 }
37 if err := res.Remove(); err != nil {
38 t.Fatalf("second Remove: %v", err)
39 }
40}
41
42func TestCreateRefusesSymlink(t *testing.T) {
43 dir := t.TempDir()
44 link := filepath.Join(dir, "link")
45 if err := os.Symlink(filepath.Join(dir, "target"), link); err != nil {
46 t.Skip(err)
47 }
48 if _, _, err := Create(link); !errors.Is(err, ports.ErrPathExists) {
49 t.Fatalf("got %v", err)
50 }
51 if _, err := os.Lstat(filepath.Join(dir, "target")); !errors.Is(err, fs.ErrNotExist) {
52 t.Fatalf("symlink target created: %v", err)
53 }
54}
55
56func TestCreateOtherErrorsAreNotPathExists(t *testing.T) {
57 _, _, err := Create(filepath.Join(t.TempDir(), "missing", "a"))
58 if err == nil || errors.Is(err, ports.ErrPathExists) {
59 t.Fatalf("got %v", err)
60 }
61}
62
63func TestRemoveLeavesReplacement(t *testing.T) {
64 dir := t.TempDir()
65 path := filepath.Join(dir, "a")
66 f, res, err := Create(path)
67 if err != nil {
68 t.Fatal(err)
69 }
70 defer f.Close()
71 moved := filepath.Join(dir, "moved")
72 if err := os.Rename(path, moved); err != nil {
73 t.Fatal(err)
74 }
75 for name, replace := range map[string]func() error{
76 "file": func() error { return os.WriteFile(path, []byte("user"), 0o600) },
77 "symlink": func() error { return os.Symlink(moved, path) },
78 } {
79 if err := replace(); err != nil {
80 t.Fatal(err)
81 }
82 if err := res.Remove(); err != nil {
83 t.Fatalf("%s: %v", name, err)
84 }
85 if _, err := os.Lstat(path); err != nil {
86 t.Fatalf("%s replacement was removed: %v", name, err)
87 }
88 if err := os.Remove(path); err != nil {
89 t.Fatal(err)
90 }
91 }
92 if _, err := os.Lstat(moved); err != nil {
93 t.Fatalf("moved original touched: %v", err)
94 }
95}
96
97func TestOwnsOnlyTheReservedFile(t *testing.T) {
98 dir := t.TempDir()
99 path := filepath.Join(dir, "a")
100 f, res, err := Create(path)
101 if err != nil {
102 t.Fatal(err)
103 }
104 defer f.Close()
105 if !res.Owns() {
106 t.Fatal("reserved file not owned")
107 }
108 if err := os.Rename(path, filepath.Join(dir, "moved")); err != nil {
109 t.Fatal(err)
110 }
111 if res.Owns() {
112 t.Fatal("missing path reported as owned")
113 }
114 if err := os.WriteFile(path, []byte("user"), 0o600); err != nil {
115 t.Fatal(err)
116 }
117 if res.Owns() {
118 t.Fatal("replacement reported as owned")
119 }
120}