project_root_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")
  7local helper = require("spec.test_helper")
  8local git = helper.git
  9
 10describe("find_project_root", function()
 11	local temp_dir
 12
 13	setup(function()
 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		if temp_dir then
 23			os.execute("rm -rf " .. temp_dir)
 24		end
 25	end)
 26
 27	describe("wt-managed repository (.bare + .git file)", function()
 28		it("finds root when cwd is project root", function()
 29			if not temp_dir then
 30				pending("temp_dir not available")
 31				return
 32			end
 33			local project = temp_dir .. "/project"
 34			os.execute("mkdir -p " .. project .. "/.bare")
 35			git("init --bare " .. project .. "/.bare")
 36			local f = io.open(project .. "/.git", "w")
 37			if f then
 38				f:write("gitdir: ./.bare\n")
 39				f:close()
 40			end
 41
 42			local root, err = wt.find_project_root(project)
 43			assert.is_nil(err)
 44			assert.are.equal(project, root)
 45		end)
 46
 47		it("finds root when cwd is inside worktree", function()
 48			if not temp_dir then
 49				pending("temp_dir not available")
 50				return
 51			end
 52			local project = temp_dir .. "/project2"
 53			os.execute("mkdir -p " .. project .. "/.bare")
 54			git("init --bare " .. project .. "/.bare")
 55			local f = io.open(project .. "/.git", "w")
 56			if f then
 57				f:write("gitdir: ./.bare\n")
 58				f:close()
 59			end
 60
 61			os.execute("mkdir -p " .. project .. "/main/src/lib")
 62			local wt_git = io.open(project .. "/main/.git", "w")
 63			if wt_git then
 64				wt_git:write("gitdir: ../.bare/worktrees/main\n")
 65				wt_git:close()
 66			end
 67
 68			local root, err = wt.find_project_root(project .. "/main/src/lib")
 69			assert.is_nil(err)
 70			assert.are.equal(project, root)
 71		end)
 72
 73		it("finds root when cwd is in nested branch worktree", function()
 74			if not temp_dir then
 75				pending("temp_dir not available")
 76				return
 77			end
 78			local project = temp_dir .. "/nested-wt"
 79			os.execute("mkdir -p " .. project .. "/.bare")
 80			git("init --bare " .. project .. "/.bare")
 81			local f = io.open(project .. "/.git", "w")
 82			if f then
 83				f:write("gitdir: ./.bare\n")
 84				f:close()
 85			end
 86
 87			os.execute("mkdir -p " .. project .. "/feature/auth/src")
 88			local wt_git = io.open(project .. "/feature/auth/.git", "w")
 89			if wt_git then
 90				wt_git:write("gitdir: ../../.bare/worktrees/feature-auth\n")
 91				wt_git:close()
 92			end
 93
 94			local root, err = wt.find_project_root(project .. "/feature/auth/src")
 95			assert.is_nil(err)
 96			assert.are.equal(project, root)
 97		end)
 98	end)
 99
100	describe("non-wt repository", function()
101		it("returns error for normal git repo", function()
102			if not temp_dir then
103				pending("temp_dir not available")
104				return
105			end
106			local project = temp_dir .. "/normal-git"
107			os.execute("mkdir -p " .. project)
108			git("init " .. project)
109
110			local root, err = wt.find_project_root(project)
111			assert.is_nil(root)
112			assert.is_not_nil(err)
113			assert.is_truthy(err:match("not in a wt%-managed repository"))
114		end)
115
116		it("returns error for non-git directory", function()
117			if not temp_dir then
118				pending("temp_dir not available")
119				return
120			end
121			local project = temp_dir .. "/not-git"
122			os.execute("mkdir -p " .. project)
123
124			local root, err = wt.find_project_root(project)
125			assert.is_nil(root)
126			assert.is_not_nil(err)
127		end)
128	end)
129
130	describe("edge cases", function()
131		it("finds root with .bare but relative .git reference", function()
132			if not temp_dir then
133				pending("temp_dir not available")
134				return
135			end
136			local project = temp_dir .. "/rel-git"
137			os.execute("mkdir -p " .. project .. "/.bare")
138			git("init --bare " .. project .. "/.bare")
139
140			local f = io.open(project .. "/.git", "w")
141			if f then
142				f:write("gitdir: .bare\n")
143				f:close()
144			end
145
146			local root, err = wt.find_project_root(project)
147			assert.is_nil(err)
148			assert.are.equal(project, root)
149		end)
150	end)
151end)
152
153describe("detect_source_worktree", function()
154	local temp_dir
155
156	setup(function()
157		local handle = io.popen("mktemp -d")
158		if handle then
159			temp_dir = handle:read("*l")
160			handle:close()
161		end
162	end)
163
164	teardown(function()
165		if temp_dir then
166			os.execute("rm -rf " .. temp_dir)
167		end
168	end)
169
170	describe("at project root", function()
171		it("returns nil when cwd equals root", function()
172			if not temp_dir then
173				pending("temp_dir not available")
174				return
175			end
176			local project = temp_dir .. "/proj1"
177			os.execute("mkdir -p " .. project .. "/.bare")
178
179			local result = wt.detect_source_worktree(project, project)
180			assert.is_nil(result)
181		end)
182	end)
183
184	describe("inside a worktree", function()
185		it("returns worktree path when cwd has .git file", function()
186			if not temp_dir then
187				pending("temp_dir not available")
188				return
189			end
190			local project = temp_dir .. "/proj2"
191			local worktree = project .. "/main"
192			os.execute("mkdir -p " .. project .. "/.bare")
193			os.execute("mkdir -p " .. worktree .. "/src")
194
195			local f = io.open(worktree .. "/.git", "w")
196			if f then
197				f:write("gitdir: ../.bare/worktrees/main\n")
198				f:close()
199			end
200
201			local result = wt.detect_source_worktree(project, worktree)
202			assert.are.equal(worktree, result)
203		end)
204
205		it("finds worktree root when cwd is deep inside worktree", function()
206			if not temp_dir then
207				pending("temp_dir not available")
208				return
209			end
210			local project = temp_dir .. "/proj3"
211			local worktree = project .. "/main"
212			local deep_path = worktree .. "/src/lib/utils"
213			os.execute("mkdir -p " .. project .. "/.bare")
214			os.execute("mkdir -p " .. deep_path)
215
216			local f = io.open(worktree .. "/.git", "w")
217			if f then
218				f:write("gitdir: ../.bare/worktrees/main\n")
219				f:close()
220			end
221
222			local result = wt.detect_source_worktree(project, deep_path)
223			assert.are.equal(worktree, result)
224		end)
225
226		it("finds nested branch worktree", function()
227			if not temp_dir then
228				pending("temp_dir not available")
229				return
230			end
231			local project = temp_dir .. "/proj4"
232			local worktree = project .. "/feature/auth"
233			os.execute("mkdir -p " .. project .. "/.bare")
234			os.execute("mkdir -p " .. worktree .. "/src")
235
236			local f = io.open(worktree .. "/.git", "w")
237			if f then
238				f:write("gitdir: ../../.bare/worktrees/feature-auth\n")
239				f:close()
240			end
241
242			local result = wt.detect_source_worktree(project, worktree .. "/src")
243			assert.are.equal(worktree, result)
244		end)
245	end)
246
247	describe("not inside a worktree", function()
248		it("returns nil when in bare repo directory", function()
249			if not temp_dir then
250				pending("temp_dir not available")
251				return
252			end
253			local project = temp_dir .. "/proj5"
254			local bare = project .. "/.bare"
255			os.execute("mkdir -p " .. bare)
256
257			local result = wt.detect_source_worktree(project, bare)
258			assert.is_nil(result)
259		end)
260	end)
261end)