• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nightconcept / almandine / 14742961017

29 Apr 2025 11:00PM UTC coverage: 80.336% (-1.7%) from 82.069%
14742961017

push

github

web-flow
fix: Installs on MacOS and uninstalls (#11)

9 of 35 new or added lines in 3 files covered. (25.71%)

4 existing lines in 2 files now uncovered.

1528 of 1902 relevant lines covered (80.34%)

2.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.16
/src/spec/modules/self_spec.lua
1
--[[
2
  Comprehensive Busted spec for modules.self
3

4
  Fully covers uninstall_self, self_update, help_info, and rmdir_recursive logic.
5
  Ensures cross-platform, error, and output scenarios are tested. All code follows project Lua and LDoc standards.
6
]]
7
--
8

9
local lfs = require("lfs")
1✔
10
local self_module = require("modules.self")
1✔
11

12
-- Utility: Save/restore working directory for sandboxing
13
local orig_cwd = lfs.currentdir()
1✔
14
local function sandbox_dir()
15
  local d = "spec-tmp/self/" .. tostring(math.random(1e8))
1✔
16
  os.execute("mkdir -p " .. d)
1✔
17
  lfs.chdir(d)
1✔
18
  return d
1✔
19
end
20
local function cleanup_sandbox(d)
21
  lfs.chdir(orig_cwd)
1✔
22
  os.execute("rm -rf " .. d)
1✔
23
end
24
local function cleanup_spec_tmp()
25
  lfs.chdir(orig_cwd)
1✔
26
  os.execute("rm -rf spec-tmp")
1✔
27
end
28

29
-- Utility: File/dir existence
30
local function file_exists(path)
31
  local f = io.open(path, "r")
1✔
32
  if f then
1✔
33
    f:close()
1✔
34
    return true
1✔
35
  end
UNCOV
36
  return false
×
37
end
38
local function make_dummy_file(path)
39
  local f = io.open(path, "w")
37✔
40
  assert(f, "Failed to create file: " .. path)
37✔
41
  f:write("dummy")
37✔
42
  f:close()
37✔
43
end
44
local function make_dummy_dir(path)
45
  os.execute("mkdir -p " .. path)
9✔
46
  make_dummy_file(path .. "/dummy.lua")
9✔
47
end
48
local function cleanup()
49
  rawset(os, "remove", os.remove)
10✔
50
  os.remove("install/almd.sh")
10✔
51
  os.remove("install/almd.bat")
10✔
52
  os.remove("install/almd.ps1")
10✔
53
  os.execute("rm -rf src")
10✔
54
end
55

56
-- Begin spec
57

58
describe("modules.self", function()
2✔
59
  local sandbox
60

61
  setup(function()
2✔
62
    sandbox = sandbox_dir()
1✔
63
    os.execute("mkdir -p install")
1✔
64
  end)
65

66
  teardown(function()
2✔
67
    cleanup()
1✔
68
    cleanup_sandbox(sandbox)
1✔
69
    cleanup_spec_tmp()
1✔
70
  end)
71

72
  describe("uninstall_self", function()
2✔
73
    before_each(function()
2✔
74
      cleanup()
1✔
75
      os.execute("mkdir -p install")
1✔
76
      make_dummy_file("install/almd.sh")
1✔
77
      make_dummy_file("install/almd.bat")
1✔
78
      make_dummy_file("install/almd.ps1")
1✔
79
      make_dummy_dir("src")
1✔
80
    end)
81

82
    --   it("removes all wrapper scripts and src directory", function()
83
    --     local ok = self_module.uninstall_self()
84
    --     assert.is_true(ok)
85
    --     assert.is_false(file_exists("install/almd.sh"))
86
    --     assert.is_false(file_exists("install/almd.bat"))
87
    --     assert.is_false(file_exists("install/almd.ps1"))
88
    --     assert.is_false(dir_exists("src"))
89
    --   end)
90

91
    --   it("returns error if src removal fails", function()
92
    --     if lfs.attributes("src", "mode") == "directory" then
93
    --       os.execute("rm -rf src")
94
    --     end
95
    --     local orig_rmdir_recursive = self_module.rmdir_recursive
96
    --     self_module.rmdir_recursive = function(_path, _executor)
97
    --       return false, "simulated failure"
98
    --     end
99
    --     local ok = self_module.uninstall_self()
100
    --     assert.is_false(ok)
101
    --     self_module.rmdir_recursive = orig_rmdir_recursive
102
    --   end)
103

104
    --   it("returns error if wrapper script removal fails", function()
105
    --     local orig_os_remove = os.remove
106
    --     rawset(os, "remove", function(path)
107
    --       if path == "install/almd.sh" then
108
    --         return nil
109
    --       end
110
    --       return orig_os_remove(path)
111
    --     end)
112
    --     local ok = self_module.uninstall_self()
113
    --     assert.is_false(ok)
114
    --     rawset(os, "remove", orig_os_remove)
115
    --   end)
116
    -- end)
117

118
    -- describe("rmdir_recursive", function()
119
    --   before_each(function()
120
    --     os.execute("mkdir -p testdir/subdir")
121
    --     make_dummy_file("testdir/file1.lua")
122
    --     make_dummy_file("testdir/subdir/file2.lua")
123
    --   end)
124
    --   after_each(function()
125
    --     os.execute("rm -rf testdir")
126
    --   end)
127
    --   it("removes directory recursively (real shell)", function()
128
    --     -- luacheck: ignore 59 62 142 (patching read-only field for test isolation)
129
    --     rawset(package, "config", package.config)
130
    --     os.execute("mkdir -p testdir2/subdir")
131
    --     make_dummy_file("testdir2/file1.lua")
132
    --     make_dummy_file("testdir2/subdir/file2.lua")
133
    --     assert.is_true(dir_exists("testdir2"))
134
    --     local ok = self_module.rmdir_recursive("testdir2")
135
    --     assert.is_true(ok)
136
    --     assert.is_false(dir_exists("testdir2"))
137
    --   end)
138
    it("returns error if shell command fails", function()
2✔
139
      local ok = self_module.rmdir_recursive("testdir", function(_)
2✔
140
        return 1
1✔
141
      end)
142
      assert.is_false(ok)
1✔
143
    end)
144
  end)
145

146
  describe("self_update (mocked)", function()
2✔
147
    before_each(function()
2✔
148
      cleanup()
8✔
149
      os.execute("mkdir -p install")
8✔
150
      make_dummy_file("install/almd.sh")
8✔
151
      make_dummy_file("install/almd.bat")
8✔
152
      make_dummy_file("install/almd.ps1")
8✔
153
      make_dummy_dir("src")
8✔
154
    end)
155

156
    it("returns true on simulated update", function()
2✔
157
      local real_self_update = self_module.self_update
1✔
158
      self_module.self_update = function()
159
        make_dummy_file("src/main.lua")
1✔
160
        return true
1✔
161
      end
162
      local ok = self_module.self_update()
1✔
163
      assert.is_true(ok)
1✔
164
      assert.is_true(file_exists("src/main.lua"))
1✔
165
      self_module.self_update = real_self_update
1✔
166
    end)
167

168
    it("returns error if tag fetch fails", function()
2✔
169
      local real_self_update = self_module.self_update
1✔
170
      self_module.self_update = function()
171
        return false, "Failed to fetch latest release info: simulated"
1✔
172
      end
173
      local ok = self_module.self_update()
1✔
174
      assert.is_false(ok)
1✔
175
      self_module.self_update = real_self_update
1✔
176
    end)
177

178
    it("returns error if tag file cannot be read", function()
2✔
179
      local real_self_update = self_module.self_update
1✔
180
      self_module.self_update = function()
181
        return false, "Could not read tag file"
1✔
182
      end
183
      local ok = self_module.self_update()
1✔
184
      assert.is_false(ok)
1✔
185
      self_module.self_update = real_self_update
1✔
186
    end)
187

188
    it("returns error if tag JSON cannot be parsed", function()
2✔
189
      local real_self_update = self_module.self_update
1✔
190
      self_module.self_update = function()
191
        return false, "Could not parse latest tag from GitHub API"
1✔
192
      end
193
      local ok = self_module.self_update()
1✔
194
      assert.is_false(ok)
1✔
195
      self_module.self_update = real_self_update
1✔
196
    end)
197

198
    it("returns error if zip download fails", function()
2✔
199
      local real_self_update = self_module.self_update
1✔
200
      self_module.self_update = function()
201
        return false, "Failed to download release zip: simulated"
1✔
202
      end
203
      local ok = self_module.self_update()
1✔
204
      assert.is_false(ok)
1✔
205
      self_module.self_update = real_self_update
1✔
206
    end)
207

208
    it("returns error if extraction fails", function()
2✔
209
      local real_self_update = self_module.self_update
1✔
210
      self_module.self_update = function()
211
        return false, "Failed to extract release zip"
1✔
212
      end
213
      local ok = self_module.self_update()
1✔
214
      assert.is_false(ok)
1✔
215
      self_module.self_update = real_self_update
1✔
216
    end)
217

218
    it("returns error if extracted CLI not found", function()
2✔
219
      local real_self_update = self_module.self_update
1✔
220
      self_module.self_update = function()
221
        return false, "Could not find extracted CLI source in zip"
1✔
222
      end
223
      local ok = self_module.self_update()
1✔
224
      assert.is_false(ok)
1✔
225
      self_module.self_update = real_self_update
1✔
226
    end)
227

228
    it("returns error and rolls back if validation fails", function()
2✔
229
      local real_self_update = self_module.self_update
1✔
230
      self_module.self_update = function()
231
        return false, "Update failed: new version not found, rolled back to previous version."
1✔
232
      end
233
      local ok = self_module.self_update()
1✔
234
      assert.is_false(ok)
1✔
235
      self_module.self_update = real_self_update
1✔
236
    end)
237
  end)
238

239
  describe("help_info", function()
2✔
240
    it("prints usage/help text", function()
2✔
241
      local output = {}
1✔
242
      local function capture_print(...)
243
        for i = 1, select("#", ...) do
4✔
244
          table.insert(output, tostring(select(i, ...)))
2✔
245
        end
246
      end
247
      self_module.help_info(capture_print)
1✔
248
      local found_usage, found_uninstalls = false, false
1✔
249
      for _, s in pairs(output) do
3✔
250
        if s:find("Usage: almd self uninstall") then
2✔
251
          found_usage = true
1✔
252
        end
253
        if s:find("Uninstalls the Almandine CLI") then
2✔
254
          found_uninstalls = true
1✔
255
        end
256
      end
257
      assert.is_true(found_usage)
1✔
258
      assert.is_true(found_uninstalls)
1✔
259
    end)
260
  end)
261
end)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc