harn-cli 0.6.3

CLI for the Harn programming language — run, test, REPL, format, and lint
import { render, screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { IntlProvider } from "react-intl"
import { MemoryRouter } from "react-router-dom"
import { afterEach, describe, expect, it, vi } from "vitest"

import { App } from "./App"

const runsPayload = {
  stats: {
    total_runs: 2,
    completed_runs: 1,
    active_runs: 0,
    failed_runs: 1,
    avg_duration_ms: 1200,
  },
  filtered_count: 2,
  pagination: {
    page: 1,
    page_size: 25,
    total_pages: 1,
    total_runs: 2,
    has_previous: false,
    has_next: false,
  },
  runs: [
    {
      path: "failed.json",
      id: "run-failed",
      workflow_name: "demo",
      status: "failed",
      last_stage_node_id: "verify",
      failure_summary: "verify failed: assertion failed",
      started_at: "2026-04-04T10:00:00Z",
      finished_at: "2026-04-04T10:00:05Z",
      duration_ms: 5000,
      stage_count: 2,
      child_run_count: 0,
      call_count: 3,
      input_tokens: 100,
      output_tokens: 40,
      models: ["gpt-5"],
      updated_at_ms: 1,
    },
    {
      path: "ok.json",
      id: "run-ok",
      workflow_name: "demo",
      status: "completed",
      last_stage_node_id: "finalize",
      failure_summary: null,
      started_at: "2026-04-04T09:00:00Z",
      finished_at: "2026-04-04T09:00:02Z",
      duration_ms: 2000,
      stage_count: 1,
      child_run_count: 0,
      call_count: 1,
      input_tokens: 20,
      output_tokens: 10,
      models: ["gpt-5"],
      updated_at_ms: 2,
    },
  ],
}

const detailPayload = {
  summary: runsPayload.runs[0],
  task: "Fix issue",
  workflow_id: "wf",
  parent_run_id: null,
  root_run_id: null,
  policy_summary: {
    tools: [],
    capabilities: [],
    workspace_roots: [],
    side_effect_level: null,
    recursion_limit: null,
    tool_arg_constraints: [],
    validation_valid: true,
    validation_errors: [],
    validation_warnings: [],
    reachable_nodes: ["verify"],
  },
  replay_summary: {
    fixture_id: "fixture-1",
    source_run_id: "run-failed",
    created_at: "2026-04-04T10:01:00Z",
    expected_status: "failed",
    stage_assertions: [],
  },
  execution: null,
  insights: [],
  stages: [],
  spans: [],
  activities: [],
  transitions: [],
  checkpoints: [],
  artifacts: [],
  execution_summary: null,
  transcript_steps: [],
  story: [],
  child_runs: [],
}

afterEach(() => {
  vi.unstubAllGlobals()
})

describe("App", () => {
  it("shows a paginated runs page and navigates into run detail", async () => {
    const fetchMock = vi.fn(async (input: string) => {
      if (input.startsWith("/api/runs")) {
        return { ok: true, json: async () => runsPayload }
      }
      if (input === "/api/meta") {
        return {
          ok: true,
          json: async () => ({ workspace_root: "/workspace/harn", run_dir: ".harn-runs/portal-demo" }),
        }
      }
      if (input === "/api/llm/options") {
        return {
          ok: true,
          json: async () => ({
            preferred_provider: "local",
            preferred_model: "gpt-4o",
            providers: [
              {
                name: "local",
                base_url: "http://localhost:8000",
                base_url_env: "LOCAL_LLM_BASE_URL",
                auth_style: "none",
                auth_envs: [],
                auth_configured: true,
                viable: true,
                local: true,
                models: ["gpt-4o"],
                aliases: [],
                default_model: "gpt-4o",
              },
            ],
          }),
        }
      }
      if (input.startsWith("/api/run?path=failed.json")) {
        return { ok: true, json: async () => detailPayload }
      }
      if (input === "/api/launch/targets") {
        return { ok: true, json: async () => ({ targets: [] }) }
      }
      if (input === "/api/launch/jobs") {
        return { ok: true, json: async () => ({ jobs: [] }) }
      }
      if (input.startsWith("/api/compare?")) {
        return {
          ok: true,
          json: async () => ({
            identical: false,
            left_status: "completed",
            right_status: "failed",
            stage_diffs: [],
            transition_count_delta: 1,
            artifact_count_delta: 0,
            checkpoint_count_delta: 0,
          }),
        }
      }
      throw new Error(`unexpected fetch ${input}`)
    })
    vi.stubGlobal("fetch", fetchMock)

    render(
      <MemoryRouter initialEntries={["/runs"]}>
        <IntlProvider locale="en">
          <App />
        </IntlProvider>
      </MemoryRouter>,
    )

    expect(await screen.findByText("Persisted runs")).toBeInTheDocument()
    expect(await screen.findByText("failed.json")).toBeInTheDocument()

    await userEvent.click(screen.getAllByRole("button", { name: "Inspect" })[0])

    await waitFor(() => {
      expect(screen.getByText("Inspect persisted run")).toBeInTheDocument()
    })
    expect(screen.getByText("harn replay .harn-runs/failed.json")).toBeInTheDocument()
  })
})