Skip to content

No test suite #9

Description

@sinhaparth5

There is no test target. Verification is done by driving the TUI under script -qec with piped
keystrokes and reading the last rendered frame, which works but is manual, slow, and only ever run
against the thing being changed at that moment.

This is recorded as an open question in progress.md ("Test strategy: unit tests against fixture
repos created at test time? Which framework?") and as a known limit in the 2026.08.0 changelog. It
is the largest single gap in the project.

Why it is worth doing now rather than later

Three bugs fixed recently were all invariant violations that a test would have caught immediately
and that manual driving nearly missed:

  • The CI job drill-down indexed the unfiltered pipelines_.runs with a cursor that indexes the
    visible list, so with a / filter active, enter opened a different run than the one
    highlighted.
  • RebuildFilter() was missing from EnsureHistory, CollectPipelines and CollectPulls, so a
    filtered view kept drawing the response before the one that just landed.
  • Two hardcoded glyphs ("❯" in app.cpp, "…" in signin_panel.cpp) bypassed the glyph table
    and would render as tofu on a terminal that had been given the ascii set.

None of these needed a UI to find. All three are assertions about pure functions.

Where the value is concentrated

The codebase is unusually well set up for this, because the expensive parts are already pure:

  • Reads return snapshots and touch no UI stateReadStatus, ReadHistory, FetchRepoInfo,
    FetchPipelines, FetchPulls. Each is a function from a fixture to a value.
  • Provider normalization is the highest-value target. model::RunStatus folds GitHub's split
    status/conclusion and GitLab's eleven-value single field into one type in
    remote/pipelines.cpp. That is a table of cases, it is exactly where a provider change will break
    things silently, and it needs no repository and no network — just recorded JSON.
  • Text measurement. TextWidth, Truncate, Fit, Rjust are cell-accurate and the whole
    point is that they do not tear a double-width glyph. Trivially testable, currently untested.
  • The config reader/writer round-trips a strict TOML subset and reports every rejection by name.
  • Filtering. RebuildFilter and the Visible* mirrors, which is where two of the three bugs
    above lived.
  • Lane assignment in graph.cpp.

Proposed

  • Pick a framework. Catch2 or doctest — both are header-only and both drop into the existing
    FetchContent arrangement without a new system dependency, which matters given the build currently
    needs only libcurl and zlib.
  • Fixture repositories built at test time with libgit2 rather than checked in, so they are readable
    as code and cannot rot into a binary blob nobody can regenerate.
  • Recorded provider JSON for the normalization tests. The stub-API approach already described in
    CLAUDE.md is the right shape; the responses it serves should become fixtures.
  • Wire it to CTest and run it in the release workflow before packaging. The workflow currently
    builds and packages without ever executing a test.

Not in scope: driving the full TUI in CI. The manual approach stays useful for render checks; this
issue is about the layer underneath it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions