-- The MIT License (MIT) -- -- Copyright (C) 2026 Scott Duensing -- -- Permission is hereby granted, free of charge, to any person obtaining a copy -- of this software and associated documentation files (the "Software"), to -- deal in the Software without restriction, including without limitation the -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -- sell copies of the Software, and to permit persons to whom the Software is -- furnished to do so, subject to the following conditions: -- -- The above copyright notice and this permission notice shall be included in -- all copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -- IN THE SOFTWARE. -- testReview.lua -- the operator's review tool (tools/review.lua) against the running service: what -- waits, a fetch and the package tool's word on it, an approval signed with the repository's key -- that DVX would trust, a rejection, withdrawing and publishing again, and a fetched file changed -- before signing that the service refuses. -- -- calog test/testReview.lua -- local BASE = calogArgs()[1] local DATA = calogArgs()[2] local DVXPKG = calogArgs()[3] local ADMIN = calogArgs()[4] local CALOG = calogArgs()[5] local SINGE = calogArgs()[6] local CONF = DATA .. "/review.conf" local WORK = DATA .. "/review" local pass, fail = 0, 0 local function check(name, got, want) if got == want then pass = pass + 1 else fail = fail + 1 calogPrint("FAIL", name, "got", tostring(got), "want", tostring(want)) end end local function call(method, path, body, token, raw) local headers = { ["Content-Type"] = raw and "application/octet-stream" or "application/json" } if token then headers["Authorization"] = "Bearer " .. token end local r = httpRequest({ method = method, url = BASE .. path, headers = headers, body = raw and body or (body and jsonStringify(body) or nil) }) local parsed = {} if r.body and r.body ~= "" then local ok, value = pcall(jsonParse, r.body) if ok and type(value) == "table" then parsed = value end end return r.status, parsed, r end local function dpk(name) return fsRead(DATA .. "/" .. name .. ".dpk") end -- The tool: its exit status and what it printed local function review(...) local run = procRun({ CALOG, "tools/review.lua", "--", "singe=" .. SINGE, CONF, ... }) return run.exit, (run.stdout or "") .. (run.stderr or "") end local function listed(id) local _, cat = call("GET", "/v1/catalogue") for _, entry in ipairs(cat.packages or {}) do if entry.id == id then return entry end end return nil end fsWrite(CONF, table.concat({ "server = " .. BASE, "token = " .. ADMIN, "dvxpkg = " .. DVXPKG, "secret = " .. DATA .. "/REPO.SEC", "work = " .. WORK, "" }, "\n")) check("dice uploaded", (call("POST", "/v1/upload", dpk("dice"), ADMIN, true)), 202) local exit, said = review("pending") check("pending runs", exit, 0) check("and lists dice", said:find("acme.dice 1.0.0", 1, true) ~= nil, true) exit, said = review("fetch", "acme.dice", "1.0.0") check("fetch runs", exit, 0) check("into the work folder", fsExists(WORK .. "/acme.dice-1.0.0.DPK"), true) check("the bytes uploaded", fsRead(WORK .. "/acme.dice-1.0.0.DPK") == dpk("dice"), true) check("and shows what it is", said:find("acme.dice", 1, true) ~= nil and said:find("fetched", 1, true) ~= nil, true) check("nothing listed before approval", listed("acme.dice"), nil) exit, said = review("approve", "acme.dice", "1.0.0", "looks fine") check("approve runs", exit, 0) check("and says so", said:find("approved acme.dice 1.0.0", 1, true) ~= nil, true) local entry = listed("acme.dice") check("now listed", entry and entry.version, "1.0.0") check("signed by the repository", entry and entry.signed, true) local _, _, sig = call("GET", "/v1/signature/acme.dice/1.0.0") fsWrite(DATA .. "/dice.SIG", sig.body) check("DVX would trust it", procRun({ DVXPKG, "verify", DATA .. "/dice.dpk", "-s", DATA .. "/dice.SIG", DATA .. "/REPO.KEY" }).exit, 0) check("dice 1.1 uploaded", (call("POST", "/v1/upload", dpk("dice2"), ADMIN, true)), 202) exit, said = review("reject", "acme.dice", "1.1.0", "not yet") check("reject runs", exit, 0) local _, versions = call("GET", "/v1/package/acme.dice") check("still one version", #(versions.versions or {}), 1) exit = review("reject", "acme.dice", "1.1.0") check("a rejection needs a note", exit, 2) exit = review("withdraw", "acme.dice") check("withdraw runs", exit, 0) check("not listed", listed("acme.dice"), nil) exit = review("publish", "acme.dice") check("publish runs", exit, 0) check("listed again", listed("acme.dice") ~= nil, true) -- A fetched file that is not what was uploaded: the signature is of other bytes, and refused check("dice 1.2 uploaded", (call("POST", "/v1/upload", dpk("dice3"), ADMIN, true)), 202) check("fetched", (review("fetch", "acme.dice", "1.2.0")), 0) fsWrite(WORK .. "/acme.dice-1.2.0.DPK", dpk("dice2")) exit, said = review("approve", "acme.dice", "1.2.0") check("a changed file's signature is refused", exit, 1) check("as not the repository's", said:find("repository", 1, true) ~= nil, true) check("still at 1.0.0", listed("acme.dice").version, "1.0.0") check("an unknown command", (review("bless", "acme.dice")), 2) local bad = DATA .. "/badtoken.conf" fsWrite(bad, (fsRead(CONF):gsub("token = %w+", "token = wrong"))) check("a wrong token", (procRun({ CALOG, "tools/review.lua", "--", "singe=" .. SINGE, bad, "pending" }).exit), 1) calogPrint(string.format("%d passed, %d failed", pass, fail)) calogExit(fail == 0 and 0 or 1)