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("cmd_init", function()
9 local temp_dir
10 local original_cwd
11
12 setup(function()
13 local handle = io.popen("pwd")
14 if handle then
15 original_cwd = handle:read("*l")
16 handle:close()
17 end
18 handle = io.popen("mktemp -d")
19 if handle then
20 temp_dir = handle:read("*l")
21 handle:close()
22 end
23 end)
24
25 teardown(function()
26 if original_cwd then
27 os.execute("cd " .. original_cwd)
28 end
29 if temp_dir then
30 os.execute("rm -rf " .. temp_dir)
31 end
32 end)
33
34 describe("already wt-managed repository", function()
35 it("reports already using wt structure when .git file points to .bare", function()
36 if not temp_dir then
37 pending("temp_dir not available")
38 return
39 end
40 local project = temp_dir .. "/already-wt"
41 os.execute("mkdir -p " .. project .. "/.bare")
42 os.execute("git init --bare " .. project .. "/.bare")
43 local f = io.open(project .. "/.git", "w")
44 if f then
45 f:write("gitdir: ./.bare\n")
46 f:close()
47 end
48
49 local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init")
50 assert.are.equal(0, code)
51 assert.is_truthy(output:match("[Aa]lready"))
52 end)
53 end)
54
55 describe("inside a worktree", function()
56 it("errors when run from inside a worktree", function()
57 if not temp_dir then
58 pending("temp_dir not available")
59 return
60 end
61 local project = temp_dir .. "/wt-project"
62 local worktree = project .. "/main"
63 os.execute("mkdir -p " .. project .. "/.bare")
64 os.execute("git init --bare " .. project .. "/.bare")
65 os.execute("mkdir -p " .. worktree)
66 local f = io.open(worktree .. "/.git", "w")
67 if f then
68 f:write("gitdir: ../.bare/worktrees/main\n")
69 f:close()
70 end
71
72 local output, code = wt.run_cmd("cd " .. worktree .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
73 assert.are_not.equal(0, code)
74 assert.is_truthy(output:match("worktree") or output:match("not a git"))
75 end)
76 end)
77
78 describe("not a git repository", function()
79 it("errors when run in non-git directory", function()
80 if not temp_dir then
81 pending("temp_dir not available")
82 return
83 end
84 local project = temp_dir .. "/not-git"
85 os.execute("mkdir -p " .. project)
86
87 local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
88 assert.are_not.equal(0, code)
89 assert.is_truthy(output:match("not a git"))
90 end)
91 end)
92
93 describe(".bare exists but no .git", function()
94 it("errors and suggests creating .git file", function()
95 if not temp_dir then
96 pending("temp_dir not available")
97 return
98 end
99 local project = temp_dir .. "/bare-only"
100 os.execute("mkdir -p " .. project .. "/.bare")
101 os.execute("git init --bare " .. project .. "/.bare")
102
103 local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
104 assert.are_not.equal(0, code)
105 assert.is_truthy(output:match("%.git") or output:match("gitdir"))
106 end)
107 end)
108
109 describe("existing git worktrees", function()
110 it("errors when .git/worktrees/ exists", function()
111 if not temp_dir then
112 pending("temp_dir not available")
113 return
114 end
115 local project = temp_dir .. "/has-worktrees"
116 os.execute("mkdir -p " .. project)
117 os.execute("git init " .. project)
118 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
119 os.execute("mkdir -p " .. project .. "/.git/worktrees/feature-branch")
120
121 local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
122 assert.are_not.equal(0, code)
123 assert.is_truthy(output:match("worktree"))
124 end)
125 end)
126
127 describe("dirty repository", function()
128 it("errors when uncommitted changes exist", function()
129 if not temp_dir then
130 pending("temp_dir not available")
131 return
132 end
133 local project = temp_dir .. "/dirty-repo"
134 os.execute("mkdir -p " .. project)
135 os.execute("git init " .. project)
136 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
137 local f = io.open(project .. "/dirty.txt", "w")
138 if f then
139 f:write("uncommitted\n")
140 f:close()
141 end
142 os.execute("cd " .. project .. " && git add dirty.txt")
143
144 local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
145 assert.are_not.equal(0, code)
146 assert.is_truthy(output:match("uncommitted") or output:match("stash") or output:match("commit"))
147 end)
148 end)
149
150 describe("--dry-run", function()
151 it("prints plan without modifying filesystem", function()
152 if not temp_dir then
153 pending("temp_dir not available")
154 return
155 end
156 local project = temp_dir .. "/dry-run-test"
157 os.execute("mkdir -p " .. project)
158 os.execute("git init " .. project)
159 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
160
161 local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1"
162 local output, code = wt.run_cmd(cmd)
163 assert.are.equal(0, code)
164 assert.is_truthy(output:match("[Dd]ry run") or output:match("planned"))
165
166 local git_still_dir = io.open(project .. "/.git/HEAD", "r")
167 assert.is_not_nil(git_still_dir)
168 if git_still_dir then
169 git_still_dir:close()
170 end
171
172 local bare_exists = io.open(project .. "/.bare/HEAD", "r")
173 assert.is_nil(bare_exists)
174 end)
175
176 it("lists orphaned files that would be removed", function()
177 if not temp_dir then
178 pending("temp_dir not available")
179 return
180 end
181 local project = temp_dir .. "/dry-orphans"
182 os.execute("mkdir -p " .. project)
183 os.execute("git init " .. project)
184 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
185
186 local f = io.open(project .. "/README.md", "w")
187 if f then
188 f:write("# Test\n")
189 f:close()
190 end
191 os.execute("cd " .. project .. " && git add README.md && git commit -m 'add readme'")
192
193 local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1"
194 local output, code = wt.run_cmd(cmd)
195 assert.are.equal(0, code)
196 assert.is_truthy(output:match("README") or output:match("orphan"))
197 end)
198
199 it("shows target worktree path", function()
200 if not temp_dir then
201 pending("temp_dir not available")
202 return
203 end
204 local project = temp_dir .. "/dry-worktree"
205 os.execute("mkdir -p " .. project)
206 os.execute("git init " .. project)
207 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
208
209 local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1"
210 local output, code = wt.run_cmd(cmd)
211 assert.are.equal(0, code)
212 assert.is_truthy(output:match("worktree") or output:match("main") or output:match("master"))
213 end)
214 end)
215
216 describe("-y/--yes flag", function()
217 it("bypasses confirmation prompt", function()
218 if not temp_dir then
219 pending("temp_dir not available")
220 return
221 end
222 local project = temp_dir .. "/yes-test"
223 os.execute("mkdir -p " .. project)
224 os.execute("git init " .. project)
225 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
226
227 local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
228 assert.are.equal(0, code)
229 assert.is_truthy(output:match("[Cc]onverted") or output:match("[Bb]are"))
230
231 local bare_exists = io.open(project .. "/.bare/HEAD", "r")
232 assert.is_not_nil(bare_exists)
233 if bare_exists then
234 bare_exists:close()
235 end
236
237 local git_is_file = io.open(project .. "/.git", "r")
238 if git_is_file then
239 local content = git_is_file:read("*a")
240 git_is_file:close()
241 assert.is_truthy(content:match("gitdir"))
242 end
243 end)
244 end)
245
246 describe("successful conversion", function()
247 it("moves .git to .bare", function()
248 if not temp_dir then
249 pending("temp_dir not available")
250 return
251 end
252 local project = temp_dir .. "/convert-test"
253 os.execute("mkdir -p " .. project)
254 os.execute("git init " .. project)
255 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
256
257 local _, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
258 assert.are.equal(0, code)
259
260 local bare_head = io.open(project .. "/.bare/HEAD", "r")
261 assert.is_not_nil(bare_head)
262 if bare_head then
263 bare_head:close()
264 end
265 end)
266
267 it("creates .git file pointing to .bare", function()
268 if not temp_dir then
269 pending("temp_dir not available")
270 return
271 end
272 local project = temp_dir .. "/git-file-test"
273 os.execute("mkdir -p " .. project)
274 os.execute("git init " .. project)
275 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
276
277 local _, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
278 assert.are.equal(0, code)
279
280 local git_file = io.open(project .. "/.git", "r")
281 assert.is_not_nil(git_file)
282 if git_file then
283 local content = git_file:read("*a")
284 git_file:close()
285 assert.is_truthy(content:match("gitdir:.*%.bare"))
286 end
287 end)
288
289 it("creates worktree for default branch", function()
290 if not temp_dir then
291 pending("temp_dir not available")
292 return
293 end
294 local project = temp_dir .. "/worktree-created"
295 os.execute("mkdir -p " .. project)
296 os.execute("git init " .. project)
297 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
298
299 local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1"
300 local _, code = wt.run_cmd(cmd)
301 assert.are.equal(0, code)
302
303 local worktrees_output, _ = wt.run_cmd("GIT_DIR=" .. project .. "/.bare git worktree list")
304 assert.is_truthy(worktrees_output:match("main") or worktrees_output:match("master"))
305 end)
306
307 it("removes orphaned files from root", function()
308 if not temp_dir then
309 pending("temp_dir not available")
310 return
311 end
312 local project = temp_dir .. "/orphan-cleanup"
313 os.execute("mkdir -p " .. project)
314 os.execute("git init " .. project)
315 local f = io.open(project .. "/tracked.txt", "w")
316 if f then
317 f:write("tracked\n")
318 f:close()
319 end
320 os.execute("cd " .. project .. " && git add tracked.txt && git commit -m 'add file'")
321
322 local _, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
323 assert.are.equal(0, code)
324
325 local orphan_check = io.open(project .. "/tracked.txt", "r")
326 assert.is_nil(orphan_check)
327 end)
328 end)
329
330 describe("warnings", function()
331 it("warns about submodules", function()
332 if not temp_dir then
333 pending("temp_dir not available")
334 return
335 end
336 local project = temp_dir .. "/submodule-warn"
337 os.execute("mkdir -p " .. project)
338 os.execute("git init " .. project)
339 os.execute("cd " .. project .. " && git commit --allow-empty -m 'initial'")
340 local f = io.open(project .. "/.gitmodules", "w")
341 if f then
342 f:write("[submodule \"lib\"]\n\tpath = lib\n\turl = https://example.com/lib.git\n")
343 f:close()
344 end
345 os.execute("cd " .. project .. " && git add .gitmodules && git commit -m 'add submodules'")
346
347 local output, _ = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1")
348 assert.is_truthy(output:match("submodule") or output:match("[Ww]arning"))
349 end)
350 end)
351end)