Files
test-samurai.nvim/runner-agents.md
M.Schirmer e315a8e8f2
All checks were successful
tests / test (push) Successful in 8s
update ai prompt for external runner creation
2026-01-03 15:20:01 +01:00

8.4 KiB

runner-agents.md — test-samurai Runner-API

Ziel: Diese Datei beschreibt die öffentliche Runner-API, die ein neuer Runner implementieren muss, damit alle Commands vollständig unterstützt werden.

Modulform (Pflicht)

  • Der Runner ist ein Lua-Modul, das eine Table mit Funktionen zurückgibt.
  • Beispiel:
    • local runner = {}
    • return runner
  • Der Modulpfad in runner_modules muss exakt zum Dateipfad unter lua/ passen.
    • Beispiel: lua/test-samurai-go-runner/init.lua -> require("test-samurai-go-runner").
    • lua/init.lua wäre require("init") und ist kollisionsanfällig; vermeiden.

Pflichtfunktionen (volle Command- und Keymap-Unterstützung)

  • is_test_file(bufnr) -> boolean
    • Wird für die Runner-Auswahl genutzt.
  • find_nearest(bufnr, row, col) -> spec|nil, err?
    • Für TSamNearest.
    • spec.file muss gesetzt sein.
    • Bei Fehler/kein Treffer: nil, "reason" zurückgeben.
  • build_command(spec) -> command_spec
    • Für TSamNearest.
  • build_file_command(bufnr) -> command_spec
    • Für TSamFile.
  • build_all_command(bufnr) -> command_spec
    • Für TSamAll.
  • build_failed_command(last_command, failures, scope_kind) -> command_spec
    • Für TSamFailedOnly.
  • parse_results(output) -> results
    • Fallback-Parser für vollständigen Output.
  • output_parser() -> { on_line, on_complete }
    • Muss Streaming unterstützen (Listing wird live befüllt).
  • parse_test_output(output) -> table
    • Detail-Ausgabe pro Test (Detail-Float und <cr> im Listing).
  • collect_failed_locations(failures, command, scope_kind) -> items
    • Quickfix-Unterstützung (Keymap <leader>qn).

Output-Parsing (Listing + Summary)

Beide Varianten müssen vorhanden sein:

  • parse_results(output) -> results
    • results muss enthalten:
      • passes (Array von Namen)
      • failures (Array von Namen)
      • skips (Array von Namen)
    • Optional:
      • display = { passes = {}, failures = {}, skips = {} }
      • failures_all (für Streaming-Parser, um alle bisherigen Failures zu liefern)
    • Wenn display fehlt, werden passes/failures/skips direkt im Listing angezeigt.
  • oder output_parser() -> { on_line, on_complete }
    • on_line(line, state) kann results liefern (siehe oben).
    • on_complete(output, state) kann results liefern (siehe oben).
    • on_line muss Ergebnisse liefern, damit das Listing-Float immer gestreamt wird.

Hinweis: parse_results darf intern output_parser().on_complete nutzen, aber beide Funktionen müssen existieren.

Listing-Gruppierung fuer Parent/Subtests

  • Wenn Testnamen das Format Parent/Subtest oder verschachtelt Parent/Sub/Subtest haben und der Parent ebenfalls in den Ergebnissen vorhanden ist, gruppiert das Listing:
    • Parent kommt vor seinen direkten Kindern.
    • Mehrstufige Subtests werden hierarchisch gruppiert (Parent -> Kind -> Enkel).
    • Die Reihenfolge der Kinder folgt der Eingangsreihenfolge des Runners.
    • Ohne Parent-Eintrag bleibt die normale Reihenfolge erhalten.

Detail-Output (TSamShowOutput / im Listing)

  • parse_test_output(output) -> table
    • Rückgabeform:
      • { [test_name] = { "line1", "line2", ... } }
    • test_name muss mit results.* korrespondieren (gleiches Namensschema).

Quickfix-Unterstützung (Failures)

  • collect_failed_locations(failures, command, scope_kind) -> items
    • items: Array von Quickfix-Items
      • { filename = "...", lnum = <number>, col = <number>, text = "..." }

Erwartete Datenformen

  • command_spec:
    • { cmd = { "binary", "arg1", ... }, cwd = "..." }
    • cmd darf nicht leer sein.
    • cwd ist optional; wenn nicht gesetzt, nutzt der Core das aktuelle CWD.
  • spec (von find_nearest):
    • Muss mindestens file enthalten, z. B.:
      • { file = "...", cwd = "...", test_name = "...", full_name = "...", kind = "..." }
  • results (für Listing-Float + Keymaps):
    • { passes = { "Name1", ... }, failures = { "Name2", ... }, skips = { "Name3", ... } }
    • Optional: display = { passes = { "DisplayName1", ... }, failures = { "DisplayName2", ... }, skips = { "DisplayName3", ... } }
    • failures steuert [ FAIL ]-Zeilen im Listing und wird von <leader>nf/<leader>pf genutzt.
  • items (für Quickfix):
    • { { filename = "...", lnum = 1, col = 1, text = "..." }, ... }
    • Wird von <leader>qn verwendet.

Keymaps (Datenlieferung)

  • <leader>nf / <leader>pf
    • benötigt [ FAIL ]-Einträge im Listing.
    • Runner muss results.failures (und optional display.failures) liefern.
  • <leader>qn
    • springt in die Quickfix-Liste.
    • Runner muss collect_failed_locations implementieren und gültige items liefern.
  • <cr> im Listing
    • öffnet Detail-Float.
    • Runner muss parse_test_output liefern und Testnamen konsistent zu results.* halten.

Optional empfohlene Metadaten

  • name (String)
    • Wird in Fehlermeldungen und Logs angezeigt.
  • framework (String)
    • Wird zur Framework-Auswahl (z. B. JS) genutzt.

Prompt-Beispiel

"Erstelle mir anhand der runner-agents.md einen neuen Runner für Rust."

Minimaler Runner-Skeleton (Template)

local runner = {
  name = "my-runner",
  framework = "my-framework",
}

function runner.is_test_file(bufnr)
  return false
end

function runner.find_nearest(bufnr, row, col)
  return nil, "no test call found"
end

function runner.build_command(spec)
  return { cmd = { "echo", "not-implemented" }, cwd = spec.cwd }
end

function runner.build_file_command(bufnr)
  return { cmd = { "echo", "not-implemented" } }
end

function runner.build_all_command(bufnr)
  return { cmd = { "echo", "not-implemented" } }
end

function runner.build_failed_command(last_command, failures, scope_kind)
  return { cmd = { "echo", "not-implemented" }, cwd = last_command and last_command.cwd or nil }
end

function runner.parse_results(output)
  return { passes = {}, failures = {}, skips = {}, display = { passes = {}, failures = {}, skips = {} } }
end

function runner.output_parser()
  return {
    on_line = function(_line, _state)
      return nil
    end,
    on_complete = function(_output, _state)
      return runner.parse_results(_output)
    end,
  }
end

function runner.parse_test_output(output)
  return {}
end

function runner.collect_failed_locations(failures, command, scope_kind)
  return {}
end

return runner

Checkliste für neue Runner

  • is_test_file implementiert
  • find_nearest implementiert (setzt spec.file)
  • build_command implementiert
  • build_file_command implementiert
  • build_all_command implementiert
  • build_failed_command implementiert
  • parse_results implementiert
  • output_parser implementiert (Streaming)
  • parse_test_output implementiert
  • collect_failed_locations implementiert
  • command_spec { cmd, cwd } korrekt zurückgegeben

Projekt- und Prozessanforderungen

  • Rolle: TDD-first Entwickler.
    • Jede neue Funktion, jedes neue Kommando und jede Verhaltensänderung muss durch Tests abgesichert sein.
    • Nach jeder Code-Änderung Tests via bash run_test.sh ausführen und bei Fehlern korrigieren, bis alles grün ist.
  • Nicht raten:
    • Bei unklaren oder mehrdeutigen Anforderungen Arbeit stoppen und Klarstellung verlangen.
    • TODO/NOTE im Code ist zulässig, stilles Raten nicht.
  • Keine stillen Änderungen:
    • Bestehende Features dürfen nicht unbemerkt geändert oder ersetzt werden.
    • Notwendige Anpassungen zur Koexistenz mehrerer Features müssen klar erkennbar sein.
  • Antworten immer auf Deutsch.
  • Eine englischsprachige README.md ist zu erstellen und wird bei Änderungen automatisch aktualisiert.
  • TDD-Vorgaben (aus AGENTS.md) uebernehmen:
    • Neue Funktionen/Commands/Verhaltensaenderungen muessen getestet werden.
    • Tests nach jeder Code-Aenderung ausfuehren.
    • Im Runner erstellter Quellcode ist ebenfalls zu testen.
    • Eine eigene run_test.sh wird im Runner-Repo angelegt.
  • Eine Gitea-Action ist zu erstellen, die bei jedem Push die Tests ausfuehrt.
    • Neovim wird per AppImage installiert (kein apt).
    • Runner laeuft auf gitea-act-runner mit Raspberry Pi 5 (ARM).
    • Beispiel (anpassbarer Workflow):
name: tests

on:
  push:
    branches:
      - "**"

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install Neovim (AppImage)
        run: |
          curl -L -o nvim.appimage https://github.com/neovim/neovim/releases/download/v0.11.4/nvim.appimage
          chmod +x nvim.appimage
          sudo mv nvim.appimage /usr/local/bin/nvim
      - name: Run tests
        run: bash run_test.sh