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)