mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-03 16:16:38 -04:00
132 lines
5.7 KiB
Lua
132 lines
5.7 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
local Json = require("src.link.Json")
|
|
local Check = require("src.update.Check")
|
|
local Semver = require("src.update.Semver")
|
|
local payloadName = "gen1recomp-0.3.47.love"
|
|
local payloadRel = "updates/" .. payloadName
|
|
local hash = string.rep("a", 64)
|
|
local notes = "Windows update regression fixture"
|
|
local body = Json.encode({
|
|
tag_name = "v0.3.47", body = notes,
|
|
assets = {
|
|
{ name = payloadName, browser_download_url = "fixture:payload", size = 7 },
|
|
{ name = "sha256sums.txt", browser_download_url = "fixture:sums" },
|
|
{ name = "gen1recomp-0.3.47-windows.zip", browser_download_url = "fixture:full" },
|
|
},
|
|
})
|
|
|
|
-- Execute the real worker command loop with deterministic transport, hashing
|
|
-- and archive probes. Both cached and fresh payloads must pass the same gate.
|
|
local function runWorker(boot, cached)
|
|
local files, states = {}, {}
|
|
if cached then files[payloadRel] = "payload" end
|
|
local commands = { { cmd = "check", target = { os = "Windows" } } }
|
|
if not cached then commands[#commands + 1] = { cmd = "download" } end
|
|
commands[#commands + 1] = { cmd = "quit" }
|
|
local index = 0
|
|
local modules = {
|
|
["src/link/Json.lua"] = Json,
|
|
["src/update/Check.lua"] = Check,
|
|
["src/update/Semver.lua"] = Semver,
|
|
["src/core/Version.lua"] = { engine = "0.3.46", shell = 2, payloadHost = "love" },
|
|
["src/update/Boot.lua"] = boot,
|
|
["src/core/HostShell.lua"] = {
|
|
canFetch = function() return true end,
|
|
haveCurl = function() return false end,
|
|
httpGet = function(url)
|
|
if url == "fixture:sums" then return hash .. " " .. payloadName .. "\n" end
|
|
return body
|
|
end,
|
|
httpDownload = function(_, path)
|
|
files[path:gsub("^fixture%-save/", "")] = "payload"
|
|
return true
|
|
end,
|
|
},
|
|
}
|
|
local savedLove, savedRename = _G.love, os.rename
|
|
local savedModules = {}
|
|
local moduleNames = { "love.thread", "love.filesystem", "love.data", "love.timer", "love.system" }
|
|
for _, name in ipairs(moduleNames) do
|
|
savedModules[name] = package.loaded[name]
|
|
package.loaded[name] = true
|
|
end
|
|
_G.love = {
|
|
filesystem = {
|
|
load = function(path) return function() return modules[path] end end,
|
|
getSaveDirectory = function() return "fixture-save" end,
|
|
getInfo = function(path)
|
|
if files[path] then return { type = "file", size = #files[path] } end
|
|
end,
|
|
read = function(path) return files[path] end,
|
|
write = function(path, data) files[path] = data; return true end,
|
|
remove = function(path) files[path] = nil; return true end,
|
|
createDirectory = function() return true end,
|
|
},
|
|
system = { getOS = function() return "Windows" end },
|
|
data = { hash = function() return "digest" end, encode = function() return hash end },
|
|
timer = {},
|
|
thread = { getChannel = function(name)
|
|
if name == "update_check_state" then
|
|
return { push = function(_, state) states[#states + 1] = state end }
|
|
end
|
|
return { demand = function()
|
|
index = index + 1
|
|
assert(commands[index], "worker consumed all fixture commands")
|
|
return commands[index]
|
|
end }
|
|
end },
|
|
}
|
|
os.rename = function() return false end
|
|
local ok, err = pcall(dofile, "src/update/check_worker.lua")
|
|
_G.love, os.rename = savedLove, savedRename
|
|
for _, name in ipairs(moduleNames) do package.loaded[name] = savedModules[name] end
|
|
assert(ok, err)
|
|
return states, files
|
|
end
|
|
|
|
local cases = {
|
|
{ name = "missing Boot", reason = "payload_probe_unavailable" },
|
|
{ name = "missing probe", boot = {}, reason = "payload_probe_unavailable" },
|
|
{ name = "failed mount", boot = { probePayload = function() return nil, "mount failed" end },
|
|
reason = "payload_probe_failed" },
|
|
{ name = "throwing probe", boot = { probePayload = function() error("probe failed") end },
|
|
reason = "payload_probe_failed" },
|
|
{ name = "invalid probe result", boot = { probePayload = function() return false end },
|
|
reason = "payload_probe_failed" },
|
|
{ name = "newer shell", boot = { probePayload = function() return { minShell = 3 } end },
|
|
reason = "min_shell" },
|
|
{ name = "different host", boot = { probePayload = function() return { payloadHost = "other" } end },
|
|
reason = "payload_host" },
|
|
}
|
|
for _, cached in ipairs({ true, false }) do
|
|
local route = cached and "cached" or "downloaded"
|
|
for _, case in ipairs(cases) do
|
|
local label = route .. " " .. case.name
|
|
local states, files = runWorker(case.boot, cached)
|
|
local final = states[#states]
|
|
eq(final.status, "needs_full", label .. " requires a native package")
|
|
eq(final.reason, case.reason, label .. " reports its reason")
|
|
eq(final.notes, notes, label .. " retains release notes")
|
|
for _, state in ipairs(states) do
|
|
check(state.status ~= "ready", label .. " never offers a false restart")
|
|
end
|
|
local record = Json.decode(files["updates/full-update.json"] or "{}")
|
|
eq(record.reason, case.reason, label .. " persists its reason")
|
|
eq(record.full and record.full.url, "fixture:full", label .. " persists the Windows package")
|
|
eq(files[payloadRel], nil, label .. " removes the rejected payload")
|
|
eq(files[payloadRel .. ".part"], nil, label .. " removes the rejected partial payload")
|
|
eq(Json.decode(files["updates/notes_cache.json"])["0.3.47"], notes,
|
|
label .. " caches notes before rejecting the payload")
|
|
end
|
|
local states, files = runWorker({ probePayload = function()
|
|
return { engine = "0.3.47", minShell = 2, payloadHost = "love" }
|
|
end }, cached)
|
|
eq(states[#states].status, "ready", route .. " compatible payload offers a restart")
|
|
eq(files[payloadRel], "payload", route .. " compatible payload is retained")
|
|
end
|
|
|
|
T.finish("update worker probe gate")
|