Archived
134 lines
4.8 KiB
TypeScript
134 lines
4.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { createHmac } from "node:crypto";
|
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
import { createServer } from "node:http";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import test from "node:test";
|
|
import { AgentStore } from "../../adapters/database/store.js";
|
|
import {
|
|
type HttpRequestLog,
|
|
handleHttp,
|
|
observeHttpRequest,
|
|
} from "../../application/controller/server.js";
|
|
|
|
test("signed webhooks are durably admitted before acknowledgment", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "agent-http-"));
|
|
const store = new AgentStore(join(root, "agent.db"));
|
|
const server = createServer((request, response) => {
|
|
handleHttp(request, response, {
|
|
store,
|
|
webhookSecret: "test-secret",
|
|
repositoryId: 9,
|
|
repositoryFullName: "owner/repo",
|
|
}).catch((error) => {
|
|
response.writeHead(500);
|
|
response.end(String(error));
|
|
});
|
|
});
|
|
try {
|
|
await new Promise<void>((resolve) =>
|
|
server.listen(0, "127.0.0.1", resolve),
|
|
);
|
|
const address = server.address();
|
|
assert.ok(address && typeof address === "object");
|
|
const body = JSON.stringify({
|
|
action: "created",
|
|
comment: { body: "/agent plan" },
|
|
repository: { id: 9, full_name: "owner/repo" },
|
|
});
|
|
const signature = createHmac("sha256", "test-secret")
|
|
.update(body)
|
|
.digest("hex");
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${address.port}/webhooks/gitea`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
"x-gitea-signature": signature,
|
|
"x-gitea-delivery": "delivery-http-1",
|
|
"x-gitea-event": "issue_comment",
|
|
"x-gitea-event-type": "issue_comment",
|
|
},
|
|
body,
|
|
},
|
|
);
|
|
assert.equal(response.status, 204);
|
|
assert.equal(store.leaseDelivery()?.id, "delivery-http-1");
|
|
} finally {
|
|
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
store.close();
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("invalid webhook signatures are rejected without persistence", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "agent-http-auth-"));
|
|
const store = new AgentStore(join(root, "agent.db"));
|
|
const requests: HttpRequestLog[] = [];
|
|
const server = createServer((request, response) => {
|
|
observeHttpRequest(request, response, (entry) => requests.push(entry));
|
|
handleHttp(request, response, {
|
|
store,
|
|
webhookSecret: "test-secret",
|
|
repositoryId: 9,
|
|
repositoryFullName: "owner/repo",
|
|
}).catch(() => {
|
|
response.writeHead(500);
|
|
response.end();
|
|
});
|
|
});
|
|
try {
|
|
await new Promise<void>((resolve) =>
|
|
server.listen(0, "127.0.0.1", resolve),
|
|
);
|
|
const address = server.address();
|
|
assert.ok(address && typeof address === "object");
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${address.port}/webhooks/gitea`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
"x-gitea-signature": "0".repeat(64),
|
|
"x-gitea-delivery": "delivery-http-2",
|
|
"x-gitea-event": "issues",
|
|
"x-gitea-event-type": "issue_label",
|
|
},
|
|
body: JSON.stringify({
|
|
repository: { id: 9, full_name: "owner/repo" },
|
|
}),
|
|
},
|
|
);
|
|
assert.equal(response.status, 401);
|
|
assert.equal(store.leaseDelivery(), undefined);
|
|
assert.equal(requests.length, 1);
|
|
assert.deepEqual(
|
|
{
|
|
method: requests[0]?.method,
|
|
path: requests[0]?.path,
|
|
statusCode: requests[0]?.statusCode,
|
|
completed: requests[0]?.completed,
|
|
},
|
|
{
|
|
method: "POST",
|
|
path: "/webhooks/gitea",
|
|
statusCode: 401,
|
|
completed: true,
|
|
},
|
|
);
|
|
assert.ok((requests[0]?.durationMs ?? -1) >= 0);
|
|
|
|
const readiness = await fetch(
|
|
`http://127.0.0.1:${address.port}/readyz`,
|
|
);
|
|
assert.equal(readiness.status, 200);
|
|
assert.equal(requests.length, 1);
|
|
} finally {
|
|
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
store.close();
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|