Give the forge bridge a merge commit's trees in one call, and a pull request's head branch (issue 1234)
gitea_commit_trees reads the commit's recursive tree listing and answers the root tree and the tree of every directory holding a module.json; a cut listing fails loudly. gitea_open_pulls entries gain head. The query keys recursive and per_page join the allow-list, read flags only. mesh-delivery decodes open pulls with json.Unmarshal into a struct (cmd/mesh-delivery/ports.go:405-410), which ignores the added key.
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { createServer } from "node:http";
|
||||
|
||||
// Fixtures follow Gitea's GitTreeResponse as its API documents it (swagger: sha, url, tree[{path, mode, type, size,
|
||||
// sha, url}], truncated, page, total_count) and as the forge answers GET /repos/{o}/{r}/git/trees/{sha}; the entry
|
||||
// fields are those, with sha values written out (no live capture: the answer's shape is what matters here).
|
||||
|
||||
const COMMIT = "c".repeat(40);
|
||||
const h = (c: string) => c.repeat(40);
|
||||
const entry = (path: string, type: "tree" | "blob", sha: string) => ({
|
||||
path, mode: type === "tree" ? "040000" : "100644", type, size: type === "tree" ? 0 : 12, sha, url: `u/${sha}`,
|
||||
});
|
||||
const LISTING = [
|
||||
entry("README.md", "blob", h("1")),
|
||||
entry("module.json", "blob", h("2")),
|
||||
entry("modules", "tree", h("3")),
|
||||
entry("modules/a", "tree", h("4")),
|
||||
entry("modules/a/module.json", "blob", h("5")),
|
||||
entry("modules/a/main.go", "blob", h("6")),
|
||||
entry("modules/b", "tree", h("7")),
|
||||
entry("modules/b/c", "tree", h("8")),
|
||||
entry("modules/b/c/module.json", "blob", h("9")),
|
||||
entry("modules/d", "tree", h("a")),
|
||||
entry("modules/d/notes.md", "blob", h("b")),
|
||||
];
|
||||
|
||||
async function serve(pages: any[]) {
|
||||
const { GiteaClient } = await import("../client.ts");
|
||||
const asked: string[] = [];
|
||||
const server = createServer((req, res) => {
|
||||
asked.push(req.url ?? "");
|
||||
const url = new URL(req.url ?? "", "http://x");
|
||||
if (url.pathname !== `/api/v1/repos/novox/hq/git/trees/${COMMIT}` || url.searchParams.get("recursive") !== "true") {
|
||||
res.statusCode = 404;
|
||||
return res.end("{}");
|
||||
}
|
||||
const page = Number(url.searchParams.get("page") ?? 1);
|
||||
res.end(JSON.stringify(pages[page - 1] ?? { sha: h("0"), tree: [], truncated: false, page, total_count: 0 }));
|
||||
});
|
||||
await new Promise<void>((r) => server.listen(0, r));
|
||||
const client = new GiteaClient(`http://127.0.0.1:${(server.address() as any).port}`, "t");
|
||||
return { client, server, asked };
|
||||
}
|
||||
|
||||
test("a commit's trees: the root and the tree of each directory holding a module.json", async () => {
|
||||
// 1234/AC1 and 1234/AC4
|
||||
const { client, server } = await serve([{ sha: h("0"), url: "u", tree: LISTING, truncated: false, page: 1, total_count: LISTING.length }]);
|
||||
try {
|
||||
const got = await client.commitTrees("novox", "hq", COMMIT);
|
||||
assert.deepEqual(got, { root: h("0"), trees: { "": h("0"), "modules/a": h("4"), "modules/b/c": h("8") } });
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("a listing the forge pages is read page by page", async () => {
|
||||
// 1234/AC1
|
||||
const { client, server, asked } = await serve([
|
||||
{ sha: h("0"), tree: LISTING.slice(0, 5), truncated: true, page: 1, total_count: LISTING.length },
|
||||
{ sha: h("0"), tree: LISTING.slice(5, 9), truncated: true, page: 2, total_count: LISTING.length },
|
||||
{ sha: h("0"), tree: LISTING.slice(9), truncated: false, page: 3, total_count: LISTING.length },
|
||||
]);
|
||||
try {
|
||||
const got = await client.commitTrees("novox", "hq", COMMIT);
|
||||
assert.deepEqual(got.trees, { "": h("0"), "modules/a": h("4"), "modules/b/c": h("8") });
|
||||
assert.equal(asked.length, 3, `asked ${asked.join(" ")}`);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("a listing the forge says is cut fails, naming the repository and the commit, and answers no trees", async () => {
|
||||
// 1234/AC2
|
||||
const { client, server } = await serve([{ sha: h("0"), tree: LISTING, truncated: true, page: 1, total_count: LISTING.length }]);
|
||||
try {
|
||||
await assert.rejects(client.commitTrees("novox", "hq", COMMIT), (err: Error) => {
|
||||
assert.match(err.message, /novox\/hq/);
|
||||
assert.ok(err.message.includes(COMMIT), err.message);
|
||||
assert.match(err.message, /cut/);
|
||||
return true;
|
||||
});
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("a sha that is not a full commit, or no owner or repo, is refused before the forge is asked", async () => {
|
||||
// 1234/AC3
|
||||
const { getGiteaTools } = await import("../tools/index.ts");
|
||||
const { client, server, asked } = await serve([]);
|
||||
try {
|
||||
const tool = getGiteaTools(client).find((t) => t.name === "gitea_commit_trees")!;
|
||||
assert.ok(tool, "the tool is listed");
|
||||
for (const args of [
|
||||
{ owner: "novox", repo: "hq", sha: "abc1234" },
|
||||
{ owner: "novox", repo: "hq", sha: "main" },
|
||||
{ owner: "novox", repo: "hq", sha: "" },
|
||||
{ owner: "novox", repo: "hq", sha: "c".repeat(41) },
|
||||
{ owner: "", repo: "hq", sha: COMMIT },
|
||||
{ owner: "novox", repo: " ", sha: COMMIT },
|
||||
]) await assert.rejects(tool.run(args as any), /./, JSON.stringify(args));
|
||||
assert.equal(asked.length, 0, `the forge was asked ${asked.join(" ")}`);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("gitea_open_pulls says each pull request's head branch beside its head sha", async () => {
|
||||
// 1234/AC5
|
||||
const { GiteaClient } = await import("../client.ts");
|
||||
const server = createServer((req, res) => {
|
||||
const url = req.url ?? "";
|
||||
const send = (v: unknown) => res.end(JSON.stringify(v));
|
||||
if (url.startsWith("/api/v1/repos/issues/search")) {
|
||||
return send(url.includes("page=1") ? [{ number: 7, repository: { owner: "novox", name: "hq", full_name: "novox/hq" } }] : []);
|
||||
}
|
||||
if (url.startsWith("/api/v1/repos/novox/hq/pulls/7")) {
|
||||
return send({ number: 7, state: "open", base: { ref: "main" }, head: { ref: "feat/x", sha: "aaa1111" }, html_url: "h" });
|
||||
}
|
||||
if (url.includes("/branch_protections")) { res.statusCode = 403; return res.end("{}"); }
|
||||
if (url.startsWith("/api/v1/repos/novox/hq/branches/main")) return send({ name: "main" });
|
||||
if (url.includes("/commits/aaa1111/status")) return send({ statuses: [] });
|
||||
res.statusCode = 404;
|
||||
res.end("{}");
|
||||
});
|
||||
await new Promise<void>((r) => server.listen(0, r));
|
||||
const client = new GiteaClient(`http://127.0.0.1:${(server.address() as any).port}`, "t");
|
||||
try {
|
||||
const [p] = await client.openPulls();
|
||||
assert.deepEqual(p, { owner: "novox", repo: "hq", number: 7, base: "main", head: "feat/x", head_sha: "aaa1111", html_url: "h",
|
||||
required: [], statuses: [], checks: {}, labels: [] });
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
@@ -539,7 +539,7 @@ test("the tools register once there is a way to a token, and the first call mint
|
||||
"gitea_list_branches",
|
||||
"gitea_delete_branch",
|
||||
"gitea_branch_protection_get", "gitea_branch_protection_set",
|
||||
"gitea_note_append", "gitea_delivery_view", "gitea_commit_statuses", "gitea_open_pulls", "gitea_contains",
|
||||
"gitea_note_append", "gitea_delivery_view", "gitea_commit_statuses", "gitea_open_pulls", "gitea_commit_trees", "gitea_contains",
|
||||
"gitea_list_labels", "gitea_create_label",
|
||||
"gitea_api",
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user