mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-06 14:33:11 +08:00
fix(ci): edit the mock-LLM E2E PR comment in place (#16591)
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
// @vitest-environment node
|
// @vitest-environment node
|
||||||
import { describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { renderReport } from "../../tests/e2e/mock-llm/scripts/render-mock-llm-report.mjs";
|
import { renderReport } from "../../tests/e2e/mock-llm/scripts/render-mock-llm-report.mjs";
|
||||||
import {
|
import {
|
||||||
buildCommentBody,
|
buildCommentBody,
|
||||||
findMatchingJobComments,
|
findMatchingJobComments,
|
||||||
|
upsertJobComment,
|
||||||
} from "../../tests/e2e/mock-llm/scripts/upsert-pr-comment.mjs";
|
} from "../../tests/e2e/mock-llm/scripts/upsert-pr-comment.mjs";
|
||||||
|
|
||||||
const MOCK_MARKER = "<!-- agent-canvas-mock-llm-e2e-report -->";
|
const MOCK_MARKER = "<!-- agent-canvas-mock-llm-e2e-report -->";
|
||||||
@@ -86,4 +87,59 @@ describe("mock-LLM E2E reporting", () => {
|
|||||||
1, 2,
|
1, 2,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("edits the existing report in place instead of reposting it", async () => {
|
||||||
|
const requests: { method: string; path: string }[] = [];
|
||||||
|
const fetchMock = vi
|
||||||
|
.spyOn(globalThis, "fetch")
|
||||||
|
.mockImplementation(async (url, init) => {
|
||||||
|
const method = init?.method ?? "GET";
|
||||||
|
const path =
|
||||||
|
new URL(String(url)).pathname + new URL(String(url)).search;
|
||||||
|
requests.push({ method, path });
|
||||||
|
|
||||||
|
if (method === "GET") {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify(
|
||||||
|
path.includes("page=1")
|
||||||
|
? [
|
||||||
|
{ id: 11, body: "unrelated", user: { type: "User" } },
|
||||||
|
{
|
||||||
|
id: 22,
|
||||||
|
body: `${MOCK_MARKER}\n## ✅ Mock-LLM E2E Tests`,
|
||||||
|
user: { login: "github-actions[bot]", type: "Bot" },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
{ status: 200 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new Response(JSON.stringify({ id: 22 }), { status: 200 });
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await upsertJobComment({
|
||||||
|
repo: "OpenHands/OpenHands",
|
||||||
|
issueNumber: "16521",
|
||||||
|
token: "t",
|
||||||
|
body: "## ✅ Mock-LLM E2E Tests\n\n**62/62 passed**",
|
||||||
|
marker: MOCK_MARKER,
|
||||||
|
legacyTitle: "Mock-LLM E2E Tests",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ updated: true, deleted: 0 });
|
||||||
|
expect(requests.filter((r) => r.method === "PATCH")).toEqual([
|
||||||
|
{
|
||||||
|
method: "PATCH",
|
||||||
|
path: "/repos/OpenHands/OpenHands/issues/comments/22",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(requests.some((r) => r.method === "POST")).toBe(false);
|
||||||
|
expect(requests.some((r) => r.method === "DELETE")).toBe(false);
|
||||||
|
fetchMock.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ export function findMatchingJobComments(comments, options) {
|
|||||||
return comments.filter((comment) => isMatchingJobComment(comment, options));
|
return comments.filter((comment) => isMatchingJobComment(comment, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function replaceJobComment({
|
export async function upsertJobComment({
|
||||||
repo,
|
repo,
|
||||||
issueNumber,
|
issueNumber,
|
||||||
token,
|
token,
|
||||||
@@ -210,24 +210,41 @@ export async function replaceJobComment({
|
|||||||
legacyTitle = "",
|
legacyTitle = "",
|
||||||
}) {
|
}) {
|
||||||
const comments = await listIssueComments(repo, issueNumber, token);
|
const comments = await listIssueComments(repo, issueNumber, token);
|
||||||
const existing = findMatchingJobComments(comments, { marker, legacyTitle });
|
const [existing, ...duplicates] = findMatchingJobComments(comments, {
|
||||||
|
marker,
|
||||||
|
legacyTitle,
|
||||||
|
});
|
||||||
|
const commentBody = buildCommentBody(body, marker);
|
||||||
|
|
||||||
for (const comment of existing) {
|
// Edit the oldest report in place so the comment keeps its position in the
|
||||||
|
// thread and does not notify subscribers on every run. Anything beyond it is
|
||||||
|
// a leftover duplicate from an earlier run.
|
||||||
|
for (const duplicate of duplicates) {
|
||||||
await githubRequest(
|
await githubRequest(
|
||||||
"DELETE",
|
"DELETE",
|
||||||
`/repos/${repo}/issues/comments/${comment.id}`,
|
`/repos/${repo}/issues/comments/${duplicate.id}`,
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
const updated = await githubRequest(
|
||||||
|
"PATCH",
|
||||||
|
`/repos/${repo}/issues/comments/${existing.id}`,
|
||||||
|
token,
|
||||||
|
{ body: commentBody },
|
||||||
|
);
|
||||||
|
return { updated: true, deleted: duplicates.length, comment: updated };
|
||||||
|
}
|
||||||
|
|
||||||
const created = await githubRequest(
|
const created = await githubRequest(
|
||||||
"POST",
|
"POST",
|
||||||
`/repos/${repo}/issues/${issueNumber}/comments`,
|
`/repos/${repo}/issues/${issueNumber}/comments`,
|
||||||
token,
|
token,
|
||||||
{ body: buildCommentBody(body, marker) },
|
{ body: commentBody },
|
||||||
);
|
);
|
||||||
|
|
||||||
return { deleted: existing.length, created };
|
return { updated: false, deleted: duplicates.length, comment: created };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -258,7 +275,7 @@ async function main() {
|
|||||||
);
|
);
|
||||||
const body = readFileSync(bodyFile, "utf8");
|
const body = readFileSync(bodyFile, "utf8");
|
||||||
|
|
||||||
const result = await replaceJobComment({
|
const result = await upsertJobComment({
|
||||||
repo,
|
repo,
|
||||||
issueNumber: validatedIssueNumber,
|
issueNumber: validatedIssueNumber,
|
||||||
token,
|
token,
|
||||||
@@ -267,11 +284,14 @@ async function main() {
|
|||||||
legacyTitle: args.legacy_title ?? "",
|
legacyTitle: args.legacy_title ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(
|
const action = result.updated ? "Updated" : "Created";
|
||||||
`Deleted ${result.deleted} existing PR comment${
|
const cleanup =
|
||||||
result.deleted === 1 ? "" : "s"
|
result.deleted === 0
|
||||||
}; created PR comment ${result.created.id}.`,
|
? ""
|
||||||
);
|
: ` Deleted ${result.deleted} duplicate PR comment${
|
||||||
|
result.deleted === 1 ? "" : "s"
|
||||||
|
}.`;
|
||||||
|
console.log(`${action} PR comment ${result.comment.id}.${cleanup}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
Reference in New Issue
Block a user