Newer
Older
navi-1 / docs / e2e.md

End-to-end tests

The e2e suite drives the real stack in a browser: a real Navi backend, the built webclient, a real model from llm.json, and the actual file tools. A test sends a message, the agent calls filesystem_read / filesystem_write / file_ai, and the assertions check both the rendered tool cards and the file on disk.

It lives in e2e/, outside testpaths = ["tests"], so the normal suite never collects it. Run it explicitly:

uv run pytest e2e -q

What one run does

e2e/conftest.py            # database, server process, browser, per-test screenshots
e2e/support.py             # session/HTTP/UI helpers
e2e/test_agent_file_tools.py

Per run the fixtures:

  1. Create a throwaway PostgreSQL database navidb_e2e_<pid>_<hex> from the DSN in .env (or E2E_DATABASE_URL) and drop it afterwards.
  2. Start navi-server on a free port with a temporary working directory, its own temp workspace/ and session_files/, and NAVI_AUTH_ENABLED=false (the local anonymous admin). Because the working directory is temporary, mcp_servers.d/ is not found, so no MCP server is started and the run does not depend on their credentials.
  3. Serve the built SPA from webclient/dist — the same assets production serves. The suite refuses to run when webclient/src is newer than dist/index.html; rebuild first:

    cd webclient && npm run build
  4. Launch Chromium through Playwright with /usr/bin/chromium (E2E_CHROMIUM overrides the path) and a fresh context per test. Service workers are blocked so a cached build cannot answer.

Scenarios

Test What it proves
test_agent_writes_then_reads_file filesystem_write write creates the file, filesystem_read read renders its numbered content, the file matches on disk
test_agent_edits_file_and_reads_diff filesystem_write edit changes the file, filesystem_read diff renders the unified diff
test_agent_uses_file_ai_to_edit file_ai edit applies an instruction through the AI helper and renders its diff

Requirements

  • Local PostgreSQL reachable through DATABASE_URL, and a role allowed to CREATE DATABASE (the local dev role is).
  • A working real LLM route: llm.json plus the credentials in .env.
  • psql on PATH.
  • Playwright's Python package (already a dependency) and Chromium at /usr/bin/chromium.
  • A fresh webclient/dist.

Tuning and diagnostics

Variable Default Meaning
E2E_DATABASE_URL .env's DATABASE_URL DSN used to create/drop the throwaway DB
E2E_CHROMIUM /usr/bin/chromium Browser binary
E2E_SERVER_TIMEOUT 120 Seconds to wait for /health
E2E_TURN_TIMEOUT_MS 300000 Milliseconds one agent turn may take
E2E_SHOTS_DIR /tmp/navi-e2e-shots Screenshots and browser logs on failure
E2E_LOG_LEVEL INFO Backend log level; the log lives in the run's temp dir

Why the tests can fail without a code change

The model is the production route, not a stub. It can pick terminal instead of filesystem_write, refuse, or ask a question. The prompts are explicit ("use the tools yourself", exact action/arguments), but this is the price of an honest end-to-end check. On failure the suite saves a screenshot and the browser console log under E2E_SHOTS_DIR, and the backend log is in the run's temp directory (/tmp/pytest-*/navi-e2e*/server.log); both make the model's actual choice visible.