moadim 3.6.0

Loop engine for AI agents — routines over REST, MCP, and a built-in web UI
import { render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { describe, expect, it } from "vitest";
import type { FleetRunSummary } from "../../api/hooks";
import { RunsGantt } from "./RunsGantt";
import { buildGantt } from "./ganttMath";

function run(o: Partial<FleetRunSummary>): FleetRunSummary {
  return {
    routine_id: "r1",
    routine_title: "Nightly Audit",
    workbench: "wb1",
    started_at: 100,
    started_at_local: "",
    finished_at: 200,
    finished_at_local: "",
    status: "success",
    exit_code: 0,
    ...o,
  };
}

const RUNS = [
  run({}),
  run({ workbench: "wb2", routine_id: "r2", routine_title: "Digest", started_at: 150, finished_at: null, status: "running" }),
];

function renderGantt(runs: FleetRunSummary[], cap: number | undefined) {
  return render(
    <MemoryRouter>
      <RunsGantt gantt={buildGantt(runs, 1000)} cap={cap} />
    </MemoryRouter>,
  );
}

describe("RunsGantt", () => {
  it("renders nothing without lanes", () => {
    const { container } = renderGantt([], 2);
    expect(container).toBeEmptyDOMElement();
  });

  it("draws one lane per routine with bars linking to the run", () => {
    renderGantt(RUNS, 4);
    expect(screen.getByText("Digest")).toBeInTheDocument();
    const bar = screen.getByRole("link", { name: /Nightly Audit: started .*, success/ });
    expect(bar).toHaveAttribute("href", "/runs/r1/wb1");
    expect(screen.getByRole("link", { name: /Digest: .*running, running/ })).toHaveClass("runs-gantt-bar", "running");
    expect(screen.getByText(/peak concurrency 2 \/ cap 4/)).toBeInTheDocument();
    expect(document.querySelector(".runs-gantt-cap")).toBeNull();
  });

  it("warns and draws the cap line when peak reaches the cap", () => {
    renderGantt(RUNS, 2);
    expect(screen.getByText(/runs may have queued/)).toHaveClass("c-amber");
    expect(document.querySelector(".runs-gantt-cap")).not.toBeNull();
  });

  it("labels an unbounded cap and a loading cap", () => {
    const { unmount } = renderGantt(RUNS, 0);
    expect(screen.getByText(/peak concurrency 2 \(no cap\)/)).toBeInTheDocument();
    unmount();
    renderGantt(RUNS, undefined);
    expect(screen.getByText("peak concurrency 2")).toBeInTheDocument();
  });
});