Files
lvmh/e2e/scenarios/repo-images.mjs
T

126 lines
3.4 KiB
JavaScript

// scenarios/repo-images.mjs — repo→custom worker image registry (REST CRUD).
// Spawn itself is NOT exercised here: it needs Docker, which the harness must
// not require (see spawn-validation.mjs).
import { rest } from "../lib.mjs";
export async function run(ctx) {
const { r, base, token } = ctx;
const unauth = await rest(base, null, "/api/repos");
r.check(
"GET /api/repos without token → 401",
unauth.status === 401,
`got ${unauth.status}`,
);
const empty = await rest(base, token, "/api/repos");
r.check(
"GET /api/repos on fresh daemon → 200 []",
empty.status === 200 &&
Array.isArray(empty.json) &&
empty.json.length === 0,
`got ${empty.status} ${empty.text}`,
);
const put = await rest(base, token, "/api/repos/lvmh/e2e/image", {
method: "PUT",
body: { image: "lvmh-worker-lvmh--e2e-ab12cd" },
});
r.check(
"PUT valid repo/image → 200 {ok:true}",
put.status === 200 && put.json?.ok === true,
`got ${put.status} ${put.text}`,
);
const putTagged = await rest(base, token, "/api/repos/lvmh/e2e2/image", {
method: "PUT",
body: { image: "lvmh-worker-lvmh--e2e2-ab12cd:1.0" },
});
r.check(
"PUT tagged image → 200",
putTagged.status === 200,
`got ${putTagged.status}`,
);
const list = await rest(base, token, "/api/repos");
const registrations = Array.isArray(list.json)
? list.json.filter((x) => x.repo === "lvmh/e2e" || x.repo === "lvmh/e2e2")
: [];
r.check(
"GET /api/repos lists both registrations",
list.status === 200 &&
registrations.length === 2 &&
registrations[0].repo === "lvmh/e2e" &&
registrations[0].image === "lvmh-worker-lvmh--e2e-ab12cd" &&
registrations[1].repo === "lvmh/e2e2" &&
registrations[1].image === "lvmh-worker-lvmh--e2e2-ab12cd:1.0",
`got ${list.status} ${list.text}`,
);
for (const image of ["evil-image", "", "lvmh-worker-", "LVMH-worker-x"]) {
const bad = await rest(base, token, "/api/repos/lvmh/e2e/image", {
method: "PUT",
body: { image },
});
r.check(
`PUT invalid image ${JSON.stringify(image)} → 400`,
bad.status === 400 && typeof bad.json?.error === "string",
`got ${bad.status} ${bad.text}`,
);
}
const badRepo = await rest(base, token, "/api/repos/noslash/image", {
method: "PUT",
body: { image: "lvmh-worker-x" },
});
r.check(
"PUT invalid repo → 400",
badRepo.status === 400,
`got ${badRepo.status}`,
);
const malformed = await fetch(`${base}/api/repos/lvmh/e2e/image`, {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: "{not json",
});
r.check(
"PUT malformed JSON → 400",
malformed.status === 400,
`got ${malformed.status}`,
);
await malformed.text();
const del = await rest(base, token, "/api/repos/lvmh/e2e/image", {
method: "DELETE",
});
r.check(
"DELETE image → 200 {ok:true}",
del.status === 200 && del.json?.ok === true,
`got ${del.status}`,
);
const delAgain = await rest(base, token, "/api/repos/lvmh/e2e/image", {
method: "DELETE",
});
r.check(
"DELETE absent image → 200 (idempotent)",
delAgain.status === 200,
`got ${delAgain.status}`,
);
const after = await rest(base, token, "/api/repos");
const remaining = Array.isArray(after.json)
? after.json.filter((x) => x.repo === "lvmh/e2e")
: null;
r.check(
"GET after delete no longer lists lvmh/e2e",
after.status === 200 && remaining?.length === 0,
`got ${after.status} ${after.text}`,
);
}