100 lines · 5.2 KB
#!/usr/bin/env bun
/**
 * End-to-end check that the merge queue hands a conflicting pull to an AI fix and re-enqueues it after
 * CI passes on the fix commit, on both paths:
 *
 * - propose (default): the run stops at a proposal without pushing; `POST .../ai-fix/apply` commits it,
 *   a repeated apply is a no-op, and only then does the pull rejoin the queue.
 * - auto (repo opt-in): the confident fix lands by itself, as before.
 *
 * Needs a dev server with MERGEFIX_PROVIDER=mock; takes several minutes because the Workflow polls CI
 * every two minutes. Queues the pull through `PUT /pulls/:n/merge_queue`.
 *
 *   D1_CWD=$PWD bun scripts/e2e-mergefix-queue.ts
 */
import { api, assert, d1, fixStatus, poll, prepareConflict } from "./mergefix-harness";
import type { Fix } from "./mergefix-harness";

const REQUEUE_TIMEOUT_MS = 15 * 60 * 1000;
const FIX_TIMEOUT_MS = 5 * 60 * 1000;

const user = await api<{ login: string }>("GET", "/user");
const line = (middle: string) => `header\none\n${middle}\nthree\nfooter\n`;

async function queuedConflict(mode: "propose" | "auto") {
  const name = `mergefix-queue-${mode}-${Date.now().toString(36)}`;
  const prepared = await prepareConflict(user.login, name, [
    { path: "app.txt", base: line("two"), head: line("two (head wording)"), main: line("two (main wording)") },
  ]);
  const repoId = String(d1(`SELECT id FROM repos WHERE name = '${name}'`)[0]?.id);
  d1(
    `INSERT INTO repo_mergefix_settings (repo_id, queue_enabled, allow_private, auto_apply, min_confidence, min_margin, updated_at)
     VALUES ('${repoId}', 1, 0, ${mode === "auto" ? 1 : 0}, 0.8, 0.3, ${Date.now()})`,
  );
  await api(
    "PUT",
    `/repos/${user.login}/${name}/pulls/${prepared.pull.number}/merge_queue`,
    { method: "merge", expected_head: prepared.before },
    [200, 202, 204],
  );
  const settled = await poll(`the queue-triggered ${mode} run to settle`, FIX_TIMEOUT_MS, async () => {