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