// Parallels Phase Runner tests cover bounded in-memory phase log tails.
import { afterEach, expect, it, vi } from "vitest";
import { PhaseRunner } from "../../scripts/e2e/parallels/phase-runner.ts";
import { createTempDirTracker } from "../helpers/temp-dir.js";

const tempRoots = createTempDirTracker();

afterEach(() => {
  tempRoots.cleanup();
  vi.restoreAllMocks();
});

async function captureFailedPhaseTail(runner: PhaseRunner, text: string): Promise<string> {
  const written: string[] = [];
  vi.spyOn(process.stderr, "write").mockImplementation((chunk) => {
    written.push(String(chunk));
    return true;
  });
  await expect(
    runner.phase("utf8-tail", 60, () => {
      runner.append(text);
      throw new Error("boom");
    }),
  ).rejects.toThrow("boom");
  return written.join("");
}

it("keeps the truncated phase tail UTF-8 safe when the byte cut splits a character", async () => {
  // 134 bytes total; the retained window starts one byte into the 4-byte
  // emoji, so a byte-naive decode would emit replacement characters.
  const tail = await captureFailedPhaseTail(
    new PhaseRunner(tempRoots.make("openclaw-parallels-phase-runner-"), 128),
    `${"x".repeat(50)}😀${"y".repeat(79)}`,
  );

  expect(tail).toContain("[phase log tail truncated to last 128 bytes]");
  expect(tail).not.toContain("�");
  expect(tail).toContain("y".repeat(79));
});
