Files
logseq/docs/agent-guide/064-logseq-cli-integration-test-shell-refactor.md

176 lines
15 KiB
Markdown

# Logseq CLI Integration Test Shell Refactor Implementation Plan
Goal: Build a new `cli-e2e` test suite that refactors `logseq.cli.integration-test` into babashka-driven shell e2e tests for compiled `logseq-cli` and `db-worker-node`, excluding `sync`, `login`, and `logout`, with full in-scope subcommand coverage and key-option coverage.
Architecture: Use a declarative command case manifest in `cli-e2e/` and execute every case via explicit shell command strings so test execution is transparent and copy-pasteable.
Architecture: Run a build preflight that compiles `logseq-cli` and `db-worker-node` artifacts before every e2e run, then run cases against those compiled artifacts only.
Architecture: Enforce completeness with a coverage checker that fails when any in-scope subcommand or any declared key option is not covered by at least one case.
Tech Stack: Babashka, babashka.cli, babashka.process, EDN manifests, Node.js, shadow-cljs build targets `logseq-cli` and `db-worker-node`, existing Logseq CLI and db-worker-node runtime.
Related: Relates to `/Users/rcmerci/gh-repos/logseq/docs/agent-guide/053-logseq-cli-async-test-isolation.md`.
Related: Relates to `/Users/rcmerci/gh-repos/logseq/docs/agent-guide/058-db-worker-node-revision-and-cli-server-list.md`.
Related: Relates to `/Users/rcmerci/gh-repos/logseq/docs/agent-guide/060-cli-graph-list-legacy-graph-dir-rename-command.md`.
## Problem statement
The current `src/test/logseq/cli/integration_test.cljs` suite mostly calls `logseq.cli.main/run!` directly instead of executing `logseq-cli` as a shell command.
This makes command-line behavior, quoting, piping, process lifecycle, and compiled artifact boundaries under-tested.
The current suite also relies heavily on `with-redefs` for `sync` and auth flows, which hides process-level integration issues.
The suite location and runtime are tied to `shadow-cljs :test`, while the new requirement is an independent `cli-e2e/` babashka flow that tests compiled binaries.
The new requirement also asks for in-scope subcommand completeness with key option coverage enforcement, which is not explicitly guaranteed today.
## Current implementation snapshot
| Area | Current file | Current behavior | Gap against requirement |
| --- | --- | --- | --- |
| CLI integration tests | `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` | Runs in cljs test bundle and invokes `run!` directly for most tests. | Not fully shell-first and not isolated under `cli-e2e/`. |
| CLI compiled artifact | `/Users/rcmerci/gh-repos/logseq/static/logseq-cli.js` | Generated by `shadow-cljs` target `logseq-cli`. | Not currently enforced as precondition in integration test flow. |
| db-worker runtime used by CLI server lifecycle | `/Users/rcmerci/gh-repos/logseq/dist/db-worker-node.js` | Spawned by `logseq.cli.server/db-worker-script-path`. | Must be rebuilt before e2e to ensure latest runtime. |
| Existing integration runner entrypoint | `/Users/rcmerci/gh-repos/logseq/package.json` (`cljs:run-integration-test`) | Runs `node static/tests.js -r '^(logseq.cli.integration-test).*'`. | Keeps tests inside cljs test harness instead of babashka shell runner. |
## In-scope command and key option inventory
The e2e suite target excludes every `sync *` command and excludes `login` and `logout`.
The suite treats global options and command options as separate coverage buckets.
The option inventory below is the key-option scope for this phase.
| Scope | Commands | Options to cover |
| --- | --- | --- |
| Global | Any in-scope command invocation | `--help`, `--version`, `--config`, `--graph`, `--data-dir`, `--output`, `--verbose`. |
| graph | `graph list`, `graph create`, `graph switch`, `graph remove`, `graph validate`, `graph info`, `graph export`, `graph import` | `--fix`, `--type`, `--file`, `--input`. |
| list | `list page`, `list tag`, `list property` | `--expand`, `--fields`, `--limit`, `--offset`, `--sort`, `--order`, `--journal-only`, `--with-properties`, `--with-extends`, `--with-classes`, `--with-type`. |
| upsert | `upsert block`, `upsert page`, `upsert tag`, `upsert property` | `--id`, `--uuid`, `--target-id`, `--target-uuid`, `--target-page`, `--pos`, `--content`, `--blocks-file`, `--status`, `--update-tags`, `--update-properties`, `--remove-tags`, `--remove-properties`, `--page`, `--name`, `--type`, `--cardinality`, `--hide`, `--public`. |
| remove | `remove block`, `remove page`, `remove tag`, `remove property` | `--id`, `--uuid`, `--name`. |
| query | `query`, `query list` | `--query`, `--name`, `--inputs`. |
| show | `show` | `--id`, `--uuid`, `--page`, `--linked-references`, `--level`, stdin `--id` input path. |
| server | `server list`, `server status`, `server start`, `server stop`, `server restart` | Command-specific graph use through `--graph`. |
| doctor | `doctor` | `--dev-script`. |
| completion | `completion` | Positional `zsh` or `bash`, and `--shell`. |
## Testing Plan
I will follow `@test-driven-development` for this refactor and write failing tests first in the new `cli-e2e` harness before implementation logic.
I will keep the tests behavior-first by asserting process exit code, stdout or stderr payload, file-system side effects, and db state changes after shell commands.
I will add a failing manifest coverage test that enumerates all in-scope subcommands and key options and fails when any item is missing from executed cases.
I will add failing runner tests that assert commands are executed as explicit shell strings and that each case logs the exact command line.
I will add failing preflight tests that assert build preflight runs before cases and that required compiled artifacts exist.
I will add failing e2e cases for each in-scope command and each key option from the inventory table, using positive and validation-error scenarios.
I will add regression tests for piping and stdin (`query | show --id`) to preserve shell-native workflows.
I will add tests that verify db-worker-node lifecycle commands (`server start/status/restart/stop`) operate against compiled runtime.
NOTE: I will write *all* tests before I add any implementation behavior.
## Target layout and integration points
The new harness will live under `/Users/rcmerci/gh-repos/logseq/cli-e2e/`.
The existing `src/test/logseq/cli/integration_test.cljs` namespace will be reduced or split so in-scope command responsibilities move to the new harness.
```text
+----------------------+ shell exec +------------------------------+
| cli-e2e bb runner | -----------------------> | node static/logseq-cli.js |
| (bb + EDN manifests) | +--------------+---------------+
+----------+-----------+ |
| | spawns
| preflight build v
| +------------------------------+
+---------------------------------------> | dist/db-worker-node.js |
| via logseq.cli.server |
+------------------------------+
```
## Detailed implementation plan
1. Create `/Users/rcmerci/gh-repos/logseq/cli-e2e/` with `bb.edn`, `README.md`, and minimal source directories.
2. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/bb.edn` tasks `build`, `test`, and `list-cases`.
3. Use `@clojure-babashka-cli` patterns in `bb.edn` to parse `--case`, `--include`, `--skip-build`, and `--verbose`.
4. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/src/logseq/cli/e2e/paths.clj` to resolve absolute repo-root aware paths.
5. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/src/logseq/cli/e2e/shell.clj` with a single shell execution utility that records exact command strings.
6. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/src/logseq/cli/e2e/preflight.clj` to run build preflight commands.
7. Implement preflight commands as `clojure -M:cljs compile logseq-cli db-worker-node` followed by `yarn db-worker-node:compile:bundle`.
8. Add preflight artifact existence checks for `/Users/rcmerci/gh-repos/logseq/static/logseq-cli.js`, `/Users/rcmerci/gh-repos/logseq/static/db-worker-node.js`, `/Users/rcmerci/gh-repos/logseq/dist/db-worker-node.js`, and `/Users/rcmerci/gh-repos/logseq/dist/db-worker-node-assets.json`.
9. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/spec/non_sync_inventory.edn` to declare required command and key-option coverage excluding sync, login, and logout.
10. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/spec/non_sync_cases.edn` to declare all shell cases with `:cmd`, `:expect`, and `:covers`.
11. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/src/logseq/cli/e2e/coverage.clj` that computes missing command and key-option coverage from manifests.
12. Write a failing test that coverage checker reports missing entries before cases are added.
13. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/src/logseq/cli/e2e/fixtures.clj` for temp data-dir, temp config, and graph seed helpers.
14. Implement graph seed helper using explicit shell commands only, not direct namespace calls.
15. Add base cases for global key-option coverage in `/Users/rcmerci/gh-repos/logseq/cli-e2e/spec/non_sync_cases.edn`.
16. Add base cases for every `graph` subcommand and its options, including negative validation paths.
17. Add base cases for every `list` subcommand and key list options with deterministic assertions.
18. Add base cases for every `upsert` subcommand and key options, including selector conflict errors and file-based block insertion.
19. Add base cases for every `remove` subcommand and key options, including uuid and name resolution behavior.
20. Add base cases for `query` and `query list`, including `--query`, `--name`, and `--inputs` paths.
21. Add base cases for `show` including `--id`, `--uuid`, `--page`, `--linked-references`, `--level`, and stdin id input.
22. Add base cases for `server` commands and assert lifecycle transitions through `server status`.
23. Add base cases for `doctor --dev-script` and assert readable script check behavior.
24. Add base cases for `completion` positional and `--shell` usage and assert shell snippet markers.
25. Add case-runner assertions for stdout parsing in `json`, `edn`, and `human` output modes.
26. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/src/logseq/cli/e2e/report.clj` to print pass or fail summary with missing coverage details.
27. Add `/Users/rcmerci/gh-repos/logseq/cli-e2e/README.md` with setup, run commands, and expected artifacts.
28. Add root task wiring in `/Users/rcmerci/gh-repos/logseq/bb.edn` for `dev:cli-e2e` delegating to `cli-e2e/bb.edn`.
29. Add package script wiring in `/Users/rcmerci/gh-repos/logseq/package.json` for `cli:e2e` and `cli:e2e:skip-build`.
30. Move or slim in-scope tests out of `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` and keep only compatibility coverage that still belongs in cljs unit scope.
31. Add a deprecation note in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` pointing engineers to `cli-e2e/`.
32. Run the full new `cli-e2e` suite with build preflight enabled and confirm zero missing coverage.
33. Run a focused subset using `--case` to validate developer ergonomics and rerun speed.
34. Run `bb dev:lint-and-test` to confirm the migration does not break existing unit test workflows.
## Verification commands and expected outcomes
| Command | Expected outcome |
| --- | --- |
| `bb -f /Users/rcmerci/gh-repos/logseq/cli-e2e/bb.edn build` | Compiles CLI and db-worker artifacts and passes artifact existence checks. |
| `bb -f /Users/rcmerci/gh-repos/logseq/cli-e2e/bb.edn test` | Runs all in-scope cases and prints zero missing command or key-option coverage. |
| `bb -f /Users/rcmerci/gh-repos/logseq/cli-e2e/bb.edn test --case show-stdin-id` | Runs one targeted case with exact shell command output shown. |
| `bb dev:cli-e2e` | Delegates to `cli-e2e` full run from repository root. |
| `bb dev:lint-and-test` | Existing lint and unit suites remain green after migration. |
## Edge cases to include in case manifests
1. Graph names and data-dir paths that include spaces and legacy-encoded names.
2. `show --id` stdin behavior with empty stdin and invalid EDN input.
3. Boolean option coercion for `--with-type`, `--linked-references`, `--hide`, and `--public`.
4. Mutually exclusive selectors such as `upsert page --id` with `--page` and `remove block --id` with `--uuid`.
5. Output format assertions for `--output human`, `--output json`, and `--output edn`.
6. `query` argument validation for `--query` and `--name` conflict and invalid `--inputs` EDN.
7. `completion` unsupported shell error path.
8. `server` command behavior when the target graph server is missing or already running.
9. `doctor --dev-script` when static script exists and when path is unreadable in a controlled failure case.
10. Ensure excluded commands (`sync`, `login`, `logout`) are rejected by coverage checker if accidentally added to in-scope inventory.
## Clarifications and assumptions captured in implementation
1. Key option coverage means each in-scope key option must appear in at least one behaviorally asserted case and not merely in help text.
2. Combinatorial explosion is controlled by one-option-at-least-once coverage plus targeted interaction cases for known conflicts.
3. Sync commands plus `login` and `logout` are explicitly excluded from this plan and remain covered by other test scopes.
4. The canonical runtime under test for CLI-managed server lifecycle is `dist/db-worker-node.js`, so bundle build is part of preflight.
5. The new suite is the source of truth for shell e2e behavior, while cljs namespace tests remain unit and component level.
## Testing Details
The new tests validate real behavior at process boundaries by invoking compiled binaries through shell commands and asserting outputs, files, and db effects.
Coverage checks verify every declared in-scope subcommand and key option has at least one asserted case.
Failure output will include missing coverage entries and the exact failing shell command for fast diagnosis.
## Implementation Details
- Use `cli-e2e/spec/non_sync_inventory.edn` as the single source of required in-scope command and key-option coverage.
- Use `cli-e2e/spec/non_sync_cases.edn` as declarative executable test cases with explicit `:covers`.
- Use one shared shell executor to keep command logging and error reporting consistent.
- Build preflight must run by default and may be skipped only with explicit `--skip-build`.
- Keep all test setup through shell commands to satisfy shell-first requirement.
- Use temp dirs per case to avoid cross-test contamination.
- Keep `login` and `logout` out of this suite by explicit inventory exclusions and checker guards.
- Preserve at least one pipeline test that uses stdout to stdin command chaining.
- Keep root task wiring minimal and avoid coupling `cli-e2e` to existing cljs test runner.
- Document command examples and troubleshooting in `cli-e2e/README.md`.
## Question
Do you want `login` and `logout` to stay permanently out of `cli-e2e`, or should we plan a separate optional suite for them under a dedicated `:manual` or `:network` tag?
---