hooks_spec.lua

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