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