|
|
Slice 1 of docs/cli-design.md: the only part with logic worth testing hard.
Pure functions, no subprocesses, so the tests hit them directly.
purpose.ts tab identity as a zellij tab purpose carrying the project's
root-relative path
discover.ts depth-1 scan for .sl0ppi/ markers
plan.ts discovered projects vs list-tabs -> {create, close, keep, foreign}
Identity carries the *path* because `list-panes --json` exposes no cwd (checked
against a live session, not assumed), so there is otherwise no way back from a
tab to a directory on disk. Slashes survive the fork's sanitiser, so nested
paths encode fine.
Two safety properties, both fuzzed over 5000 random names:
- a purpose never exceeds 64 chars and always survives re-sanitising, so what
we generate is what the terminal stores
- projectRelPath() reconstructs the original path exactly or returns null --
never a different path. Wrong answers close the wrong tab; refusing only
leaves a stale one.
The fuzz caught a real bug in the second property. The first implementation
detected lossy encodings by re-encoding and comparing, which silently failed:
`project:<47 a's>.<hash8>` is itself 64 legal characters, so it re-encodes to
itself, the check passed, and a bogus path was "reconstructed" -- which would
have closed a live tab. Lossy purposes now carry a structural marker (a leading
dot, which no discovered path can have) instead of relying on a comparison.
Plus the mount guard from the design: when the ROOT itself is missing, close
nothing. An unmounted filesystem is indistinguishable from mass deletion, and
one of those two readings eats your whole session.
31 unit tests + 2 fuzz properties, 10178 assertions, green.
|