hooks_spec.lua

  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)