1-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2--
3-- SPDX-License-Identifier: GPL-3.0-or-later
4
5package.path = package.path .. ";./?.lua"
6local wt = dofile("src/main.lua")
7
8describe("hook permission system", function()
9 local temp_dir
10
11 setup(function()
12 local handle = io.popen("mktemp -d")
13 if handle then
14 temp_dir = handle:read("*l")
15 handle:close()
16 end
17 end)
18
19 teardown(function()
20 if temp_dir then
21 os.execute("rm -rf " .. temp_dir)
22 end
23 end)
24
25 describe("load_hook_permissions", function()
26 it("returns empty table when file does not exist", function()
27 local perms = wt.load_hook_permissions(temp_dir .. "/nonexistent")
28 assert.are.same({}, perms)
29 end)
30
31 it("loads valid permissions file", function()
32 if not temp_dir then
33 pending("temp_dir not available")
34 return
35 end
36 local perm_dir = temp_dir .. "/perms1/.local/share/wt"
37 os.execute("mkdir -p " .. perm_dir)
38
39 local f = io.open(perm_dir .. "/hook-dirs.lua", "w")
40 if f then
41 f:write('{\n\t["/home/user/project1"] = true,\n\t["/home/user/project2"] = false,\n}\n')
42 f:close()
43
44 local perms = wt.load_hook_permissions(temp_dir .. "/perms1")
45 assert.are.equal(true, perms["/home/user/project1"])
46 assert.are.equal(false, perms["/home/user/project2"])
47 else
48 pending("could not write test file")
49 end
50 end)
51
52 it("returns empty table for malformed file", function()
53 if not temp_dir then
54 pending("temp_dir not available")
55 return
56 end
57 local perm_dir = temp_dir .. "/perms2/.local/share/wt"
58 os.execute("mkdir -p " .. perm_dir)
59
60 local f = io.open(perm_dir .. "/hook-dirs.lua", "w")
61 if f then
62 f:write('this is not valid lua {{{}')
63 f:close()
64
65 local perms = wt.load_hook_permissions(temp_dir .. "/perms2")
66 assert.are.same({}, perms)
67 else
68 pending("could not write test file")
69 end
70 end)
71 end)
72
73 describe("save_hook_permissions", function()
74 it("creates directory if it does not exist", function()
75 if not temp_dir then
76 pending("temp_dir not available")
77 return
78 end
79 local home = temp_dir .. "/save1"
80 wt.save_hook_permissions({ ["/test/project"] = true }, home)
81
82 local f = io.open(home .. "/.local/share/wt/hook-dirs.lua", "r")
83 assert.is_not_nil(f)
84 if f then
85 f:close()
86 end
87 end)
88
89 it("saves permissions correctly", function()
90 if not temp_dir then
91 pending("temp_dir not available")
92 return
93 end
94 local home = temp_dir .. "/save2"
95 wt.save_hook_permissions({
96 ["/project/a"] = true,
97 ["/project/b"] = false,
98 }, home)
99
100 local perms = wt.load_hook_permissions(home)
101 assert.are.equal(true, perms["/project/a"])
102 assert.are.equal(false, perms["/project/b"])
103 end)
104
105 it("overwrites existing permissions", function()
106 if not temp_dir then
107 pending("temp_dir not available")
108 return
109 end
110 local home = temp_dir .. "/save3"
111 wt.save_hook_permissions({ ["/old"] = true }, home)
112 wt.save_hook_permissions({ ["/new"] = false }, home)
113
114 local perms = wt.load_hook_permissions(home)
115 assert.is_nil(perms["/old"])
116 assert.are.equal(false, perms["/new"])
117 end)
118 end)
119end)
120
121describe("run_hooks", function()
122 local temp_dir
123
124 setup(function()
125 local handle = io.popen("mktemp -d")
126 if handle then
127 temp_dir = handle:read("*l")
128 handle:close()
129 end
130 end)
131
132 teardown(function()
133 if temp_dir then
134 os.execute("rm -rf " .. temp_dir)
135 end
136 end)
137
138 describe("copy hooks", function()
139 it("copies files from source to target", function()
140 if not temp_dir then
141 pending("temp_dir not available")
142 return
143 end
144 local source = temp_dir .. "/source1"
145 local target = temp_dir .. "/target1"
146 local home = temp_dir .. "/home1"
147 os.execute("mkdir -p " .. source)
148 os.execute("mkdir -p " .. target)
149
150 local f = io.open(source .. "/config.json", "w")
151 if f then
152 f:write('{"key": "value"}\n')
153 f:close()
154 end
155
156 wt.save_hook_permissions({ ["/project"] = true }, home)
157
158 local hooks = { copy = { "config.json" } }
159 wt.run_hooks(source, target, hooks, "/project", home)
160
161 local copied = io.open(target .. "/config.json", "r")
162 assert.is_not_nil(copied)
163 if copied then
164 local content = copied:read("*a")
165 copied:close()
166 assert.is_truthy(content:match("key"))
167 end
168 end)
169
170 it("creates parent directories for nested paths", function()
171 if not temp_dir then
172 pending("temp_dir not available")
173 return
174 end
175 local source = temp_dir .. "/source2"
176 local target = temp_dir .. "/target2"
177 local home = temp_dir .. "/home2"
178 os.execute("mkdir -p " .. source .. "/nested/path")
179 os.execute("mkdir -p " .. target)
180
181 local f = io.open(source .. "/nested/path/file.txt", "w")
182 if f then
183 f:write("nested content\n")
184 f:close()
185 end
186
187 wt.save_hook_permissions({ ["/project2"] = true }, home)
188
189 local hooks = { copy = { "nested/path/file.txt" } }
190 wt.run_hooks(source, target, hooks, "/project2", home)
191
192 local copied = io.open(target .. "/nested/path/file.txt", "r")
193 assert.is_not_nil(copied)
194 if copied then
195 copied:close()
196 end
197 end)
198 end)
199
200 describe("symlink hooks", function()
201 it("creates symlinks from target to source", function()
202 if not temp_dir then
203 pending("temp_dir not available")
204 return
205 end
206 local source = temp_dir .. "/source3"
207 local target = temp_dir .. "/target3"
208 local home = temp_dir .. "/home3"
209 os.execute("mkdir -p " .. source .. "/node_modules")
210 os.execute("mkdir -p " .. target)
211
212 local f = io.open(source .. "/node_modules/package.json", "w")
213 if f then
214 f:write('{"name": "test"}\n')
215 f:close()
216 end
217
218 wt.save_hook_permissions({ ["/project3"] = true }, home)
219
220 local hooks = { symlink = { "node_modules" } }
221 wt.run_hooks(source, target, hooks, "/project3", home)
222
223 local link_check, _ = wt.run_cmd("test -L " .. target .. "/node_modules && echo yes")
224 assert.is_truthy(link_check:match("yes"))
225 end)
226 end)
227
228 describe("run hooks", function()
229 it("executes commands in target directory", function()
230 if not temp_dir then
231 pending("temp_dir not available")
232 return
233 end
234 local source = temp_dir .. "/source4"
235 local target = temp_dir .. "/target4"
236 local home = temp_dir .. "/home4"
237 os.execute("mkdir -p " .. source)
238 os.execute("mkdir -p " .. target)
239
240 wt.save_hook_permissions({ ["/project4"] = true }, home)
241
242 local hooks = { run = { "touch ran-hook.txt" } }
243 wt.run_hooks(source, target, hooks, "/project4", home)
244
245 local ran = io.open(target .. "/ran-hook.txt", "r")
246 assert.is_not_nil(ran)
247 if ran then
248 ran:close()
249 end
250 end)
251 end)
252
253 describe("permission denied", function()
254 it("skips hooks when permission is false", function()
255 if not temp_dir then
256 pending("temp_dir not available")
257 return
258 end
259 local source = temp_dir .. "/source5"
260 local target = temp_dir .. "/target5"
261 local home = temp_dir .. "/home5"
262 os.execute("mkdir -p " .. source)
263 os.execute("mkdir -p " .. target)
264
265 local f = io.open(source .. "/shouldnt-copy.txt", "w")
266 if f then
267 f:write("content\n")
268 f:close()
269 end
270
271 wt.save_hook_permissions({ ["/project5"] = false }, home)
272
273 local hooks = { copy = { "shouldnt-copy.txt" } }
274 wt.run_hooks(source, target, hooks, "/project5", home)
275
276 local not_copied = io.open(target .. "/shouldnt-copy.txt", "r")
277 assert.is_nil(not_copied)
278 end)
279 end)
280end)