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

nightconcept / almandine / 14720604808

29 Apr 2025 12:25AM UTC coverage: 83.233% (-6.9%) from 90.086%
14720604808

push

github

web-flow
feat: Self updating implemented (#5)

98 of 273 new or added lines in 2 files covered. (35.9%)

3 existing lines in 1 file now uncovered.

1246 of 1497 relevant lines covered (83.23%)

1.94 hits per line

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

99.34
/src/spec/modules/self_spec.lua
1
--[[
2
  Busted spec for self module
3

4
  Covers uninstall_self() and self_update() logic using Busted BDD-style tests.
5
  Ensures cross-platform compatibility and atomicity of uninstall/update logic.
6
]]
7
--
8

9
local lfs = require("lfs")
1✔
10

11
local self_module
12
package.loaded["modules.self"] = nil
1✔
13
self_module = require("modules.self")
1✔
14

15
local orig_cwd = lfs.currentdir()
1✔
16

17
local function make_sandbox()
18
  local sandbox = "spec-tmp/self/" .. tostring(math.random(1e8))
1✔
19
  os.execute("mkdir -p " .. sandbox)
1✔
20
  lfs.chdir(sandbox)
1✔
21
  return sandbox
1✔
22
end
23

24
local function cleanup_sandbox(sandbox)
25
  lfs.chdir(orig_cwd)
1✔
26
  os.execute("rm -rf " .. sandbox)
1✔
27
end
28

29
local function cleanup_spec_tmp()
30
  lfs.chdir(orig_cwd)
1✔
31
  os.execute("rm -rf spec-tmp")
1✔
32
end
33

34
-- Utility functions (do not use absolute paths)
35
local function file_exists(path)
36
  local f = io.open(path, "r")
4✔
37
  if f then
4✔
38
    f:close()
1✔
39
    return true
1✔
40
  end
41
  return false
3✔
42
end
43

44
local function dir_exists(path)
45
  return lfs.attributes(path, "mode") == "directory"
1✔
46
end
47

48
local function make_dummy_file(path)
49
  local f = io.open(path, "w")
41✔
50
  assert(f, "Failed to create dummy file: " .. path)
41✔
51
  f:write("dummy")
41✔
52
  f:close()
41✔
53
end
54

55
local function make_dummy_dir(path)
56
  os.execute("mkdir -p " .. path)
10✔
57
  make_dummy_file(path .. "/dummy.lua")
10✔
58
end
59

60
local function cleanup()
61
  os.remove("install/almd.sh")
11✔
62
  os.remove("install/almd.bat")
11✔
63
  os.remove("install/almd.ps1")
11✔
64
  os.execute("rm -rf src")
11✔
65
end
66

67
describe("modules.self", function()
2✔
68
  local sandbox
69

70
  setup(function()
2✔
71
    sandbox = make_sandbox()
1✔
72
    os.execute("mkdir -p install")
1✔
73
  end)
74

75
  teardown(function()
2✔
76
    cleanup()
1✔
77
    cleanup_sandbox(sandbox)
1✔
78
    cleanup_spec_tmp()
1✔
79
  end)
80

81
  describe("uninstall_self", function()
2✔
82
    before_each(function()
2✔
83
      cleanup()
2✔
84
      os.execute("mkdir -p install")
2✔
85
      make_dummy_file("install/almd.sh")
2✔
86
      make_dummy_file("install/almd.bat")
2✔
87
      make_dummy_file("install/almd.ps1")
2✔
88
      make_dummy_dir("src")
2✔
89
    end)
90

91
    it("removes all wrapper scripts and src directory", function()
2✔
92
      local ok = self_module.uninstall_self()
1✔
93
      assert.is_true(ok)
1✔
94
      assert.is_false(file_exists("install/almd.sh"))
1✔
95
      assert.is_false(file_exists("install/almd.bat"))
1✔
96
      assert.is_false(file_exists("install/almd.ps1"))
1✔
97
      assert.is_false(dir_exists("src"))
1✔
98
    end)
99

100
    it("returns error if src removal fails", function()
2✔
101
      -- Remove src directory if it exists to ensure the executor is used
102
      if lfs.attributes("src", "mode") == "directory" then
1✔
103
        os.execute("rm -rf src")
1✔
104
      end
105
      -- Patch rmdir_recursive to simulate failure
106
      local orig_rmdir_recursive = self_module.rmdir_recursive
1✔
107
      self_module.rmdir_recursive = function(_path, _executor)
108
        return false, "simulated failure"
1✔
109
      end
110
      local ok, err = self_module.uninstall_self()
1✔
111
      assert.is_false(ok)
1✔
112
      assert.is_truthy(err)
1✔
113
      assert.is_true(err:find("Failed to remove src/") ~= nil)
1✔
114
      self_module.rmdir_recursive = orig_rmdir_recursive
1✔
115
    end)
116
  end)
117

118
  describe("self_update (mocked)", function()
2✔
119
    before_each(function()
2✔
120
      cleanup()
8✔
121
      os.execute("mkdir -p install")
8✔
122
      make_dummy_file("install/almd.sh")
8✔
123
      make_dummy_file("install/almd.bat")
8✔
124
      make_dummy_file("install/almd.ps1")
8✔
125
      make_dummy_dir("src")
8✔
126
    end)
127

128
    it("updates install tree atomically (simulated)", function()
2✔
129
      -- Patch self_update to simulate update
130
      local real_self_update = self_module.self_update
1✔
131
      self_module.self_update = function()
132
        make_dummy_file("src/main.lua")
1✔
133
        return true
1✔
134
      end
135
      local ok = self_module.self_update()
1✔
136
      assert.is_true(ok)
1✔
137
      assert.is_true(file_exists("src/main.lua"))
1✔
138
      self_module.self_update = real_self_update
1✔
139
    end)
140

141
    it("returns error if tag fetch fails", function()
2✔
142
      local real_self_update = self_module.self_update
1✔
143
      self_module.self_update = function()
144
        return false, "Failed to fetch latest release info: simulated"
1✔
145
      end
146
      local ok, err = self_module.self_update()
1✔
147
      assert.is_false(ok)
1✔
148
      assert.is_truthy(err)
1✔
149
      assert.is_true(err:find("Failed to fetch latest release info") ~= nil)
1✔
150
      self_module.self_update = real_self_update
1✔
151
    end)
152

153
    it("returns error if tag file cannot be read", function()
2✔
154
      local real_self_update = self_module.self_update
1✔
155
      self_module.self_update = function()
156
        return false, "Could not read tag file"
1✔
157
      end
158
      local ok, err = self_module.self_update()
1✔
159
      assert.is_false(ok)
1✔
160
      assert.is_truthy(err)
1✔
161
      assert.is_true(err:find("Could not read tag file") ~= nil)
1✔
162
      self_module.self_update = real_self_update
1✔
163
    end)
164

165
    it("returns error if tag JSON cannot be parsed", function()
2✔
166
      local real_self_update = self_module.self_update
1✔
167
      self_module.self_update = function()
168
        return false, "Could not parse latest tag from GitHub API"
1✔
169
      end
170
      local ok, err = self_module.self_update()
1✔
171
      assert.is_false(ok)
1✔
172
      assert.is_truthy(err)
1✔
173
      assert.is_true(err:find("Could not parse latest tag") ~= nil)
1✔
174
      self_module.self_update = real_self_update
1✔
175
    end)
176

177
    it("returns error if zip download fails", function()
2✔
178
      local real_self_update = self_module.self_update
1✔
179
      self_module.self_update = function()
180
        return false, "Failed to download release zip: simulated"
1✔
181
      end
182
      local ok, err = self_module.self_update()
1✔
183
      assert.is_false(ok)
1✔
184
      assert.is_truthy(err)
1✔
185
      assert.is_true(err:find("Failed to download release zip") ~= nil)
1✔
186
      self_module.self_update = real_self_update
1✔
187
    end)
188

189
    it("returns error if extraction fails", function()
2✔
190
      local real_self_update = self_module.self_update
1✔
191
      self_module.self_update = function()
192
        return false, "Failed to extract release zip"
1✔
193
      end
194
      local ok, err = self_module.self_update()
1✔
195
      assert.is_false(ok)
1✔
196
      assert.is_truthy(err)
1✔
197
      assert.is_true(err:find("Failed to extract release zip") ~= nil)
1✔
198
      self_module.self_update = real_self_update
1✔
199
    end)
200

201
    it("returns error if extracted CLI not found", function()
2✔
202
      local real_self_update = self_module.self_update
1✔
203
      self_module.self_update = function()
204
        return false, "Could not find extracted CLI source in zip"
1✔
205
      end
206
      local ok, err = self_module.self_update()
1✔
207
      assert.is_false(ok)
1✔
208
      assert.is_truthy(err)
1✔
209
      assert.is_true(err:find("Could not find extracted CLI source") ~= nil)
1✔
210
      self_module.self_update = real_self_update
1✔
211
    end)
212

213
    it("returns error and rolls back if validation fails", function()
2✔
214
      local real_self_update = self_module.self_update
1✔
215
      self_module.self_update = function()
216
        return false, "Update failed: new version not found, rolled back to previous version."
1✔
217
      end
218
      local ok, err = self_module.self_update()
1✔
219
      assert.is_false(ok)
1✔
220
      assert.is_truthy(err)
1✔
221
      assert.is_true(err:find("rolled back to previous version") ~= nil)
1✔
222
      self_module.self_update = real_self_update
1✔
223
    end)
224
  end)
225

226
  describe("help_info", function()
2✔
227
    it("prints usage/help text", function()
2✔
228
      local output = {}
1✔
229
      local function capture_print(...)
230
        for i = 1, select("#", ...) do
4✔
231
          table.insert(output, tostring(select(i, ...)))
2✔
232
        end
233
      end
234
      self_module.help_info(capture_print)
1✔
235
      -- Debug: print captured output
236
      for i, v in ipairs(output) do
3✔
237
        print("[help_info output]", i, v)
2✔
238
      end
239
      local found_usage = false
1✔
240
      local found_uninstalls = false
1✔
241
      for _, s in pairs(output) do
3✔
242
        if s:find("Usage: almd self uninstall") then
2✔
243
          found_usage = true
1✔
244
        end
245
        if s:find("Uninstalls the Almandine CLI") then
2✔
246
          found_uninstalls = true
1✔
247
        end
248
      end
249
      if not found_usage or not found_uninstalls then
1✔
NEW
250
        print("[help_info] output table:", table.concat(output, " | "))
×
251
      end
252
      assert.is_true(found_usage)
1✔
253
      assert.is_true(found_uninstalls)
1✔
254
    end)
255
  end)
256
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