config_spec.lua

  1-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2--
  3-- SPDX-License-Identifier: GPL-3.0-or-later
  4
  5-- Load main.lua as a module (it exports functions when required)
  6package.path = package.path .. ";./?.lua"
  7local wt = dofile("src/main.lua")
  8
  9describe("load_global_config", function()
 10	local temp_dir
 11
 12	setup(function()
 13		-- Create temp directory for test configs
 14		local handle = io.popen("mktemp -d")
 15		if handle then
 16			temp_dir = handle:read("*l")
 17			handle:close()
 18		end
 19	end)
 20
 21	teardown(function()
 22		-- Restore HOME and clean up
 23		if temp_dir then
 24			os.execute("rm -rf " .. temp_dir)
 25		end
 26	end)
 27
 28	describe("with no config file", function()
 29		it("returns empty table when config file missing", function()
 30			local config = wt.load_global_config()
 31			assert.is_table(config)
 32		end)
 33	end)
 34
 35	describe("config file format", function()
 36		-- Note: These tests document expected behavior but can't easily
 37		-- override HOME in the running process. They serve as specification.
 38
 39		it("accepts simple table config", function()
 40			-- Expected: { branch_path_style = "flat" }
 41			-- The loader prepends "return " so file should NOT have return
 42			local config = wt.load_global_config()
 43			assert.is_table(config)
 44		end)
 45	end)
 46end)
 47
 48describe("load_project_config", function()
 49	local temp_dir
 50
 51	setup(function()
 52		local handle = io.popen("mktemp -d")
 53		if handle then
 54			temp_dir = handle:read("*l")
 55			handle:close()
 56		end
 57	end)
 58
 59	teardown(function()
 60		if temp_dir then
 61			os.execute("rm -rf " .. temp_dir)
 62		end
 63	end)
 64
 65	describe("with no config file", function()
 66		it("returns empty table for nonexistent path", function()
 67			local config = wt.load_project_config("/nonexistent/path")
 68			assert.are.same({}, config)
 69		end)
 70
 71		it("returns empty table for directory without .wt.lua", function()
 72			local config = wt.load_project_config(temp_dir)
 73			assert.are.same({}, config)
 74		end)
 75	end)
 76
 77	describe("with valid config file", function()
 78		it("loads hooks configuration", function()
 79			if not temp_dir then
 80				pending("temp_dir not available")
 81				return
 82			end
 83			-- Write a test config
 84			local config_path = temp_dir .. "/.wt.lua"
 85			local f = io.open(config_path, "w")
 86			if f then
 87				f:write([[{
 88	hooks = {
 89		copy = { ".env.example" },
 90		symlink = { "node_modules" },
 91		run = { "npm install" },
 92	}
 93}]])
 94				f:close()
 95
 96				local config = wt.load_project_config(temp_dir)
 97				assert.is_table(config.hooks)
 98				assert.are.same({ ".env.example" }, config.hooks.copy)
 99				assert.are.same({ "node_modules" }, config.hooks.symlink)
100				assert.are.same({ "npm install" }, config.hooks.run)
101
102				os.remove(config_path)
103			else
104				pending("could not write test config")
105			end
106		end)
107
108		it("loads branch_path_style from project config", function()
109			if not temp_dir then
110				pending("temp_dir not available")
111				return
112			end
113			local config_path = temp_dir .. "/.wt.lua"
114			local f = io.open(config_path, "w")
115			if f then
116				f:write([[{ branch_path_style = "flat", flat_separator = "-" }]])
117				f:close()
118
119				local config = wt.load_project_config(temp_dir)
120				assert.are.equal("flat", config.branch_path_style)
121				assert.are.equal("-", config.flat_separator)
122
123				os.remove(config_path)
124			else
125				pending("could not write test config")
126			end
127		end)
128	end)
129
130	describe("error handling", function()
131		it("returns empty table for syntax error in config", function()
132			if not temp_dir then
133				pending("temp_dir not available")
134				return
135			end
136			local config_path = temp_dir .. "/.wt.lua"
137			local f = io.open(config_path, "w")
138			if f then
139				f:write([[{ invalid syntax here !!!]])
140				f:close()
141
142				local config = wt.load_project_config(temp_dir)
143				assert.are.same({}, config)
144
145				os.remove(config_path)
146			else
147				pending("could not write test config")
148			end
149		end)
150
151		it("returns empty table for runtime error in config", function()
152			if not temp_dir then
153				pending("temp_dir not available")
154				return
155			end
156			local config_path = temp_dir .. "/.wt.lua"
157			local f = io.open(config_path, "w")
158			if f then
159				f:write([[{ value = undefined_var + 1 }]])
160				f:close()
161
162				local config = wt.load_project_config(temp_dir)
163				assert.are.same({}, config)
164
165				os.remove(config_path)
166			else
167				pending("could not write test config")
168			end
169		end)
170
171		it("returns empty table when config returns non-table", function()
172			if not temp_dir then
173				pending("temp_dir not available")
174				return
175			end
176			local config_path = temp_dir .. "/.wt.lua"
177			local f = io.open(config_path, "w")
178			if f then
179				f:write([["just a string"]])
180				f:close()
181
182				local config = wt.load_project_config(temp_dir)
183				assert.are.same({}, config)
184
185				os.remove(config_path)
186			else
187				pending("could not write test config")
188			end
189		end)
190
191		it("loads config with leading comment", function()
192			if not temp_dir then
193				pending("temp_dir not available")
194				return
195			end
196			local config_path = temp_dir .. "/.wt.lua"
197			local f = io.open(config_path, "w")
198			if f then
199				f:write([[-- my config comment
200{ branch_path_style = "flat" }]])
201				f:close()
202
203				local config = wt.load_project_config(temp_dir)
204				assert.are.equal("flat", config.branch_path_style)
205
206				os.remove(config_path)
207			else
208				pending("could not write test config")
209			end
210		end)
211
212		it("loads config with explicit return", function()
213			if not temp_dir then
214				pending("temp_dir not available")
215				return
216			end
217			local config_path = temp_dir .. "/.wt.lua"
218			local f = io.open(config_path, "w")
219			if f then
220				f:write([[return { branch_path_style = "flat" }]])
221				f:close()
222
223				local config = wt.load_project_config(temp_dir)
224				assert.are.equal("flat", config.branch_path_style)
225
226				os.remove(config_path)
227			else
228				pending("could not write test config")
229			end
230		end)
231	end)
232
233	describe("sandboxed environment", function()
234		it("cannot access os module in config", function()
235			if not temp_dir then
236				pending("temp_dir not available")
237				return
238			end
239			local config_path = temp_dir .. "/.wt.lua"
240			local f = io.open(config_path, "w")
241			if f then
242				f:write([[{ home = os.getenv("HOME") }]])
243				f:close()
244
245				local config = wt.load_project_config(temp_dir)
246				-- Returns empty table due to runtime error (os is nil)
247				assert.are.same({}, config)
248
249				os.remove(config_path)
250			else
251				pending("could not write test config")
252			end
253		end)
254	end)
255end)