Files
envr/TEST_PLAN.md

4.6 KiB

Test Coverage Plan

Current State

  • 60 tests, all passing
  • Strong coverage: crypto (100%), ssh (80%), scan, features
  • Misleading test files: cmd_check_test, cmd_list_test, cmd_nushell_completion_test don't test their namesake procs
  • Biggest gap: db.odin (15/21 procs untested), all cmd_* handlers untested, parse_args untested

Tier 1 — Easy wins (pure functions, minimal setup)

1. render_table (table.odin)

  • Follow existing render_json_rows test pattern
  • Test cases: normal data (verify box-drawing chars, column alignment), empty rows, wide unicode, single column
  • Assert against strings.Builder output

2. parse_args (cli.odin)

  • Test cases: bare command, --flag value, -f value, positional args, --help/-h, unknown command, no args (prints usage), flag without value (error)
  • High value — this is the entry point for all command dispatch

3. is_encrypted_key (ssh.odin)

  • Test cases: encrypted key (returns true), unencrypted key (returns false), RSA key, malformed key
  • Fills last gap in ssh.odin

Tier 2 — High value, medium effort (fixtures exist)

4. db.odin CRUD layer

Largest gap in the project. Infrastructure already in db_integration_test.odin (fixture_key, fixture_db_path, in-memory DB setup).

Procs to test:

  • db_open / db_close — open in-memory DB, verify handle valid
  • db_insert — insert a row, verify it persists
  • db_fetch — fetch existing row, fetch missing row (returns false)
  • db_delete — delete existing row (returns true), delete missing row (returns false)
  • db_list — list multiple rows, empty DB
  • db_vacuum_to_file — vacuum to temp file, verify file exists and is non-empty

Test pattern: create in-memory DB via db_open, insert fixture rows, query and assert, defer db_close.

5. load_config / save_config (config.odin)

  • save_config: write a Config to temp dir, verify file exists and contents are valid JSON
  • load_config: read back a config written by save_config, round-trip assert
  • load_config error case: missing file returns error
  • Need a temp dir fixture (pattern exists in scan_test.odin)

Tier 3 — Command handlers (need DB + filesystem fixtures)

6. cmd_version (cmd_version.odin)

  • Test default output (prints VERSION)
  • Test --long/-l flag output
  • Capture stdout, assert content

7. cmd_list (cmd_list.odin)

  • Test TTY path: fixture DB with rows, capture table output
  • Test non-TTY path: capture JSON output, unmarshal and verify keys/values
  • Test empty DB: verify clean output (empty table or [])

8. cmd_backup (cmd_backup.odin)

  • Test successful backup: valid path, verify db_insert called
  • Test missing file: verify error message
  • Test duplicate backup: verify rejection or update behavior

9. cmd_remove (cmd_remove.odin)

  • Test successful removal: existing entry, verify db_delete called
  • Test removal of non-existent entry: verify error or no-op

10. cmd_restore (cmd_restore.odin)

  • Test successful restore: entry exists in DB, verify file written to correct path
  • Test restore of missing entry: verify error
  • Test directory creation: restore to path with missing parent dirs

Tier 4 — Hard to test (interactive / external deps)

11. cmd_deps (cmd_deps.odin)

  • Needs git and/or fd in PATH
  • Test TTY and non-TTY paths
  • Skip if dependencies not available (with #assert like TODO 28 suggests)

12. cmd_scan (cmd_scan.odin)

  • Needs fd installed
  • Test with fixture git repo containing .env files
  • Test find_unbacked integration (already partially tested in cmd_check_test.odin)
  • Non-TTY JSON output path

13. cmd_edit_config (cmd_edit_config.odin)

  • Needs refactoring: extract $EDITOR parsing into testable helper (TODO 12)
  • Test multi-word editor values ("code -w")
  • Test missing $EDITOR

14. cmd_init (cmd_init.odin)

  • Interactive prompt makes this hard
  • Needs refactoring: extract SSH key discovery and config generation into testable procs
  • Test --force flag behavior

15. prompt.odin

  • Needs refactoring to be testable
  • render_options could be tested if it accepted an io.Writer
  • read_key could be tested with a pipe/redirect instead of raw stdin
  • multi_select is end-to-end interactive, likely integration test only

Notes

  • All command handler tests will need stdout capture. Consider extracting a helper or using io.Writer injection.
  • DB integration tests should use in-memory SQLite (:memory:) where possible.
  • Temp dir fixtures should follow the pattern in scan_test.odin.
  • External dependency tests (fd, git) should use #assert to ensure the dependency is present rather than silently skipping (TODO 28).