mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 10:38:33 -04:00
docs: Added plan for improving testing.
This commit is contained in:
106
TEST_PLAN.md
Normal file
106
TEST_PLAN.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 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).
|
||||
32
TODOS.md
32
TODOS.md
@@ -67,38 +67,38 @@ Note: These todos can wait until all the subcommands have been ported.
|
||||
## Double-check AI output
|
||||
|
||||
- [ ] cli.odin
|
||||
- [ ] config.odin
|
||||
- [ ] crypto.odin
|
||||
- [ ] db.odin
|
||||
- [ ] features.odin
|
||||
- [ ] main.odin
|
||||
- [ ] prompt.odin
|
||||
- [ ] scan.odin
|
||||
- [ ] sodium.odin
|
||||
- [ ] ssh.odin
|
||||
- [ ] table.odin
|
||||
- [ ] cli_test.odin
|
||||
- [ ] cmd_backup.odin
|
||||
- [ ] cmd_check.odin
|
||||
- [ ] cmd_check_test.odin
|
||||
- [ ] cmd_deps.odin
|
||||
- [ ] cmd_edit_config.odin
|
||||
- [ ] cmd_init.odin
|
||||
- [ ] cmd_list.odin
|
||||
- [ ] cmd_list_test.odin
|
||||
- [ ] cmd_nushell_completion.odin
|
||||
- [ ] cmd_nushell_completion_test.odin
|
||||
- [ ] cmd_remove.odin
|
||||
- [ ] cmd_restore.odin
|
||||
- [ ] cmd_scan.odin
|
||||
- [ ] cmd_sync.odin
|
||||
- [ ] cmd_version.odin
|
||||
- [ ] sqlite/sqlite.odin
|
||||
- [ ] cli_test.odin
|
||||
- [ ] cmd_check_test.odin
|
||||
- [ ] cmd_list_test.odin
|
||||
- [ ] cmd_nushell_completion_test.odin
|
||||
- [x] cmd_version.odin
|
||||
- [ ] config.odin
|
||||
- [ ] config_test.odin
|
||||
- [ ] crypto.odin
|
||||
- [ ] crypto_test.odin
|
||||
- [ ] db.odin
|
||||
- [ ] db_integration_test.odin
|
||||
- [ ] db_test.odin
|
||||
- [ ] features.odin
|
||||
- [ ] features_test.odin
|
||||
- [ ] main.odin
|
||||
- [ ] prompt.odin
|
||||
- [ ] scan.odin
|
||||
- [ ] scan_test.odin
|
||||
- [ ] sodium.odin
|
||||
- [ ] sqlite/sqlite.odin
|
||||
- [ ] ssh.odin
|
||||
- [ ] ssh_test.odin
|
||||
- [ ] table.odin
|
||||
- [ ] table_test.odin
|
||||
|
||||
@@ -5,10 +5,6 @@ import "core:fmt"
|
||||
VERSION :: #load("version.txt", string)
|
||||
|
||||
cmd_version :: proc(cmd: ^Command) {
|
||||
if has_flag(cmd, "long") || has_flag(cmd, "l") {
|
||||
fmt.printf("envr version %s\n", VERSION)
|
||||
} else {
|
||||
fmt.println(VERSION)
|
||||
}
|
||||
fmt.println(VERSION)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user