Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions packages/source-control-core/src/server/pullRequestChecks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,40 @@ function entry(
}

describe("dedupeChecks", () => {
it.each([
["2026-10-16T12:00:00Z", "2026-10-16T12:00:00.500Z"],
["2026-10-16T12:00:00+02:00", "2026-10-16T10:30:00Z"],
["2026-10-16T12:00:00.0001Z", "2026-10-16T12:00:00.0002Z"],
["2026-10-16T12:00:00.0001z", "2026-10-16T12:00:00.0002z"],
["2026-10-16T12:00:00.00009Z", "2026-10-16T12:00:00.0001Z"],
])("keeps the newer instant between %s and %s in either listing order", (older, newer) => {
const oldRun = entry("build", "success", { at: older });
const newRun = entry("build", "failure", { at: newer });

expect(
[
[oldRun, newRun],
[newRun, oldRun],
].map((runs) => dedupeChecks(runs)[0]?.status),
).toEqual(["failure", "failure"]);
});

it.each([
["2026-10-16T12:00:00Z", "2026-10-16T12:00:00.000Z"],
["2026-10-16T12:00:00Z", "2026-10-16T14:00:00+02:00"],
])("takes the last copy for equal instants %s and %s", (first, second) => {
const forward = dedupeChecks([
entry("build", "success", { at: first }),
entry("build", "failure", { at: second }),
]);
const reversed = dedupeChecks([
entry("build", "success", { at: second }),
entry("build", "failure", { at: first }),
]);

expect([forward[0]?.status, reversed[0]?.status]).toEqual(["failure", "failure"]);
});

it("keeps the newest run of a check the host listed twice", () => {
const checks = dedupeChecks([
entry("Prepare PR size config", "success", {
Expand All @@ -37,6 +71,15 @@ describe("dedupeChecks", () => {
]);
});

it("preserves text ordering when a host supplies an unparseable timestamp", () => {
expect(
dedupeChecks([
entry("build", "success", { at: "invalid-a" }),
entry("build", "failure", { at: "invalid-b" }),
]).map((check) => check.status),
).toEqual(["failure"]);
});

it("holds a check at the place it first appeared, so a re-run does not reshuffle the list", () => {
const checks = dedupeChecks([
entry("lint", "success", { workflowName: "CI", at: "2026-08-11T16:00:00Z" }),
Expand Down
20 changes: 18 additions & 2 deletions packages/source-control-core/src/server/pullRequestChecks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import type { PullRequestCheck } from "@t3tools/contracts";

/** ISO-8601 timestamps in UTC compare correctly as plain text, which is all the ordering needs. */
// Parse whole seconds separately so sub-millisecond fractions keep their precision.
function timestampParts(value: string) {
const fraction = /\.(\d+)(?=Z$|[+-]\d{2}:\d{2}$)/i.exec(value);
return {
time: Date.parse(fraction ? value.replace(fraction[0], "") : value),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
fraction: fraction?.[1] ?? "",
};
}

function isAtLeastAsNew(candidate: string | null, kept: string | null): boolean {
if (candidate === null) return kept === null;
return kept === null || candidate >= kept;
if (kept === null) return true;
const candidateParts = timestampParts(candidate);
const keptParts = timestampParts(kept);
if (Number.isNaN(candidateParts.time) || Number.isNaN(keptParts.time)) return candidate >= kept;
if (candidateParts.time !== keptParts.time) return candidateParts.time > keptParts.time;
const precision = Math.max(candidateParts.fraction.length, keptParts.fraction.length);
return (
candidateParts.fraction.padEnd(precision, "0") >= keptParts.fraction.padEnd(precision, "0")
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ it.effect("submits a Forgejo review without sending its summary in the prelimina
);
});

it.effect("reads Forgejo checks without repository or viewer requests", () => {
it.effect("reads the newest Forgejo checks without repository or viewer requests", () => {
const paths: string[] = [];
return Effect.gen(function* () {
const provider = yield* ForgejoPullRequestProvider.make;
Expand Down Expand Up @@ -155,7 +155,7 @@ it.effect("reads Forgejo checks without repository or viewer requests", () => {
"head":{"ref":"feature","sha":"head","repo":null},"base":{"ref":"main","sha":"base","repo":null},
"created_at":"2026-09-16T00:00:00Z","updated_at":"2026-09-16T00:00:00Z","closed_at":null,"merged_at":null,"labels":[]}`
: input.path.endsWith("page=1")
? `[{"context":"build","status":"failure","description":null,"target_url":null,"updated_at":"2026-09-16T00:00:00Z"}]`
? `[{"context":"build","status":"failure","description":null,"target_url":null,"updated_at":"2026-09-16T00:00:00.500Z"},{"context":"build","status":"success","description":null,"target_url":null,"updated_at":"2026-09-16T00:00:00Z"}]`
: "[]",
),
);
Expand Down
Loading