Monorepo for Tangled
tangled.org
1package sandbox
2
3import (
4 "os"
5 "path/filepath"
6 "runtime"
7 "testing"
8)
9
10func TestChmodRepoTree(t *testing.T) {
11 root := t.TempDir()
12
13 // build a tree:
14 // root/
15 // file.txt (0644)
16 // script.sh (0755)
17 // subdir/
18 // nested.txt (0644)
19 // link -> ../file.txt
20 mustWrite(t, filepath.Join(root, "file.txt"), 0644, "hello")
21 mustWrite(t, filepath.Join(root, "script.sh"), 0755, "#!/bin/sh\n")
22 mustMkdir(t, filepath.Join(root, "subdir"), 0755)
23 mustWrite(t, filepath.Join(root, "subdir", "nested.txt"), 0644, "nested")
24 mustSymlink(t, "../file.txt", filepath.Join(root, "subdir", "link"))
25
26 if err := ChmodRepoTree(root); err != nil {
27 t.Fatalf("ChmodRepoTree: %v", err)
28 }
29
30 cases := []struct {
31 path string
32 wantMode os.FileMode
33 }{
34 {root, 0770},
35 {filepath.Join(root, "file.txt"), 0660},
36 {filepath.Join(root, "script.sh"), 0770},
37 {filepath.Join(root, "subdir"), 0770},
38 {filepath.Join(root, "subdir", "nested.txt"), 0660},
39 }
40 for _, c := range cases {
41 info, err := os.Stat(c.path)
42 if err != nil {
43 t.Errorf("stat %s: %v", c.path, err)
44 continue
45 }
46 if got := info.Mode().Perm(); got != c.wantMode {
47 t.Errorf("%s: mode = %o, want %o", c.path, got, c.wantMode)
48 }
49 }
50}
51
52func TestChmodRepoTree_PreservesExecutableBit(t *testing.T) {
53 root := t.TempDir()
54 mustWrite(t, filepath.Join(root, "exec"), 0744, "")
55 mustWrite(t, filepath.Join(root, "noexec"), 0644, "")
56
57 if err := ChmodRepoTree(root); err != nil {
58 t.Fatalf("ChmodRepoTree: %v", err)
59 }
60
61 if got := mode(t, filepath.Join(root, "exec")); got != 0770 {
62 t.Errorf("exec file: mode = %o, want 0770", got)
63 }
64 if got := mode(t, filepath.Join(root, "noexec")); got != 0660 {
65 t.Errorf("noexec file: mode = %o, want 0660", got)
66 }
67}
68
69func TestChownRepoTree_SelfChown(t *testing.T) {
70 // Chowning to our own UID/GID is always a no-op success. This verifies
71 // the walk visits all entries without erroring.
72 root := t.TempDir()
73 mustWrite(t, filepath.Join(root, "a"), 0644, "")
74 mustMkdir(t, filepath.Join(root, "b"), 0755)
75 mustWrite(t, filepath.Join(root, "b", "c"), 0644, "")
76
77 uid := os.Getuid()
78 gid := os.Getgid()
79 if err := ChownRepoTree(root, uid, gid); err != nil {
80 t.Fatalf("ChownRepoTree: %v", err)
81 }
82
83 // verify everything still belongs to us.
84 for _, p := range []string{root, filepath.Join(root, "a"), filepath.Join(root, "b"), filepath.Join(root, "b", "c")} {
85 info, err := os.Stat(p)
86 if err != nil {
87 t.Fatalf("stat %s: %v", p, err)
88 }
89 _ = info
90 }
91}
92
93func TestLookupUIDForRepoPath(t *testing.T) {
94 if runtime.GOOS == "windows" {
95 t.Skip("uid/gid lookup is unix-only")
96 }
97 scan := t.TempDir()
98 repo := filepath.Join(scan, "did:plc:abc")
99 mustMkdir(t, repo, 0700)
100
101 uid, gid, err := LookupUIDForRepoPath(scan, repo)
102 if err != nil {
103 t.Fatalf("LookupUIDForRepoPath: %v", err)
104 }
105 if uid != uint32(os.Getuid()) {
106 t.Errorf("uid = %d, want %d", uid, os.Getuid())
107 }
108 if gid != uint32(os.Getgid()) {
109 t.Errorf("gid = %d, want %d", gid, os.Getgid())
110 }
111}
112
113func TestLookupUIDForRepoPath_OutsideScanPath(t *testing.T) {
114 _, _, err := LookupUIDForRepoPath("/home/git", "/etc/passwd")
115 if err == nil {
116 t.Fatal("expected error for path outside scan path, got nil")
117 }
118}
119
120func TestLookupUIDForRepoPath_NonexistentPath(t *testing.T) {
121 scan := t.TempDir()
122 _, _, err := LookupUIDForRepoPath(scan, filepath.Join(scan, "does-not-exist"))
123 if err == nil {
124 t.Fatal("expected error for nonexistent path, got nil")
125 }
126}
127
128// helpers
129
130func mustWrite(t *testing.T, path string, mode os.FileMode, content string) {
131 t.Helper()
132 if err := os.WriteFile(path, []byte(content), mode); err != nil {
133 t.Fatalf("write %s: %v", path, err)
134 }
135 // WriteFile respects existing mode on overwrite; chmod to be sure.
136 if err := os.Chmod(path, mode); err != nil {
137 t.Fatalf("chmod %s: %v", path, err)
138 }
139}
140
141func mustMkdir(t *testing.T, path string, mode os.FileMode) {
142 t.Helper()
143 if err := os.MkdirAll(path, mode); err != nil {
144 t.Fatalf("mkdir %s: %v", path, err)
145 }
146}
147
148func mustSymlink(t *testing.T, target, link string) {
149 t.Helper()
150 if err := os.Symlink(target, link); err != nil {
151 t.Fatalf("symlink %s -> %s: %v", link, target, err)
152 }
153}
154
155func mode(t *testing.T, path string) os.FileMode {
156 t.Helper()
157 info, err := os.Stat(path)
158 if err != nil {
159 t.Fatalf("stat %s: %v", path, err)
160 }
161 return info.Mode().Perm()
162}