mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 20:15:07 +00:00
feat: add JSON output tests and README docs for analyze and status (#556)
* feat: add JSON output tests and README docs for analyze and status Add 7 BATS tests covering `--json` output for `mo analyze` and `mo status`: - schema structure - field types - pipe auto-detection. Also document the `--json` flag in a new "Machine-Readable Output" README section, including the auto-detection behavior when piped. * chore: use waitgroup go in status collector --------- Co-authored-by: Tw93 <hitw93@gmail.com>
This commit is contained in:
36
README.md
36
README.md
@@ -210,6 +210,42 @@ Health score is based on CPU, memory, disk, temperature, and I/O load, with colo
|
|||||||
|
|
||||||
Shortcuts: In `mo status`, press `k` to toggle the cat and save the preference, and `q` to quit.
|
Shortcuts: In `mo status`, press `k` to toggle the cat and save the preference, and `q` to quit.
|
||||||
|
|
||||||
|
#### Machine-Readable Output
|
||||||
|
|
||||||
|
Both `mo analyze` and `mo status` support a `--json` flag for scripting and automation.
|
||||||
|
|
||||||
|
`mo status` also auto-detects when its output is piped (not a terminal) and switches to JSON automatically.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Disk analysis as JSON
|
||||||
|
$ mo analyze --json ~/Documents
|
||||||
|
{
|
||||||
|
"path": "/Users/you/Documents",
|
||||||
|
"entries": [
|
||||||
|
{ "name": "Library", "path": "...", "size": 80939438080, "is_dir": true },
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"total_size": 168393441280,
|
||||||
|
"total_files": 42187
|
||||||
|
}
|
||||||
|
|
||||||
|
# System status as JSON
|
||||||
|
$ mo status --json
|
||||||
|
{
|
||||||
|
"host": "MacBook-Pro",
|
||||||
|
"health_score": 92,
|
||||||
|
"cpu": { "usage": 45.2, "logical_cpu": 8, ... },
|
||||||
|
"memory": { "total": 25769803776, "used": 15049334784, "used_percent": 58.4 },
|
||||||
|
"disks": [ ... ],
|
||||||
|
"uptime": "3d 12h 45m",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
# Auto-detected JSON when piped
|
||||||
|
$ mo status | jq '.health_score'
|
||||||
|
92
|
||||||
|
```
|
||||||
|
|
||||||
### Project Artifact Purge
|
### Project Artifact Purge
|
||||||
|
|
||||||
Clean old build artifacts such as `node_modules`, `target`, `build`, and `dist` to free up disk space.
|
Clean old build artifacts such as `node_modules`, `target`, `build`, and `dist` to free up disk space.
|
||||||
|
|||||||
@@ -255,9 +255,7 @@ func (c *Collector) Collect() (MetricsSnapshot, error) {
|
|||||||
|
|
||||||
// Helper to launch concurrent collection.
|
// Helper to launch concurrent collection.
|
||||||
collect := func(fn func() error) {
|
collect := func(fn func() error) {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
errMu.Lock()
|
errMu.Lock()
|
||||||
@@ -279,7 +277,7 @@ func (c *Collector) Collect() (MetricsSnapshot, error) {
|
|||||||
}
|
}
|
||||||
errMu.Unlock()
|
errMu.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch independent collection tasks.
|
// Launch independent collection tasks.
|
||||||
|
|||||||
141
tests/cli.bats
141
tests/cli.bats
@@ -11,6 +11,19 @@ setup_file() {
|
|||||||
export HOME
|
export HOME
|
||||||
|
|
||||||
mkdir -p "$HOME"
|
mkdir -p "$HOME"
|
||||||
|
|
||||||
|
# Build Go binaries from current source for JSON tests.
|
||||||
|
# Point GOPATH/GOMODCACHE at the real home so go build doesn't write
|
||||||
|
# module caches into the fake HOME under tests/.
|
||||||
|
if command -v go > /dev/null 2>&1; then
|
||||||
|
ANALYZE_BIN="$(mktemp "${TMPDIR:-/tmp}/analyze-go.XXXXXX")"
|
||||||
|
STATUS_BIN="$(mktemp "${TMPDIR:-/tmp}/status-go.XXXXXX")"
|
||||||
|
GOPATH="${ORIGINAL_HOME}/go" GOMODCACHE="${ORIGINAL_HOME}/go/pkg/mod" \
|
||||||
|
go build -o "$ANALYZE_BIN" "$PROJECT_ROOT/cmd/analyze" 2>/dev/null
|
||||||
|
GOPATH="${ORIGINAL_HOME}/go" GOMODCACHE="${ORIGINAL_HOME}/go/pkg/mod" \
|
||||||
|
go build -o "$STATUS_BIN" "$PROJECT_ROOT/cmd/status" 2>/dev/null
|
||||||
|
export ANALYZE_BIN STATUS_BIN
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown_file() {
|
teardown_file() {
|
||||||
@@ -19,6 +32,7 @@ teardown_file() {
|
|||||||
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
||||||
export HOME="$ORIGINAL_HOME"
|
export HOME="$ORIGINAL_HOME"
|
||||||
fi
|
fi
|
||||||
|
rm -f "${ANALYZE_BIN:-}" "${STATUS_BIN:-}"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_fake_utils() {
|
create_fake_utils() {
|
||||||
@@ -278,3 +292,130 @@ EOF
|
|||||||
run grep "pam_tid.so" "$pam_file"
|
run grep "pam_tid.so" "$pam_file"
|
||||||
[ "$status" -ne 0 ]
|
[ "$status" -ne 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- JSON output mode tests ---
|
||||||
|
|
||||||
|
@test "mo analyze --json outputs valid JSON with expected fields" {
|
||||||
|
if [[ ! -x "${ANALYZE_BIN:-}" ]]; then
|
||||||
|
skip "analyze binary not available (go not installed?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run "$ANALYZE_BIN" --json /tmp
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
# Validate it is parseable JSON
|
||||||
|
echo "$output" | python3 -c "import sys, json; json.load(sys.stdin)"
|
||||||
|
|
||||||
|
# Check required top-level keys
|
||||||
|
echo "$output" | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
assert 'path' in data, 'missing path'
|
||||||
|
assert 'entries' in data, 'missing entries'
|
||||||
|
assert 'total_size' in data, 'missing total_size'
|
||||||
|
assert 'total_files' in data, 'missing total_files'
|
||||||
|
assert isinstance(data['entries'], list), 'entries is not a list'
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "mo analyze --json entries contain required fields" {
|
||||||
|
if [[ ! -x "${ANALYZE_BIN:-}" ]]; then
|
||||||
|
skip "analyze binary not available (go not installed?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run "$ANALYZE_BIN" --json /tmp
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
echo "$output" | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
for entry in data['entries']:
|
||||||
|
assert 'name' in entry, 'entry missing name'
|
||||||
|
assert 'path' in entry, 'entry missing path'
|
||||||
|
assert 'size' in entry, 'entry missing size'
|
||||||
|
assert 'is_dir' in entry, 'entry missing is_dir'
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "mo analyze --json path reflects target directory" {
|
||||||
|
if [[ ! -x "${ANALYZE_BIN:-}" ]]; then
|
||||||
|
skip "analyze binary not available (go not installed?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run "$ANALYZE_BIN" --json /tmp
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
echo "$output" | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
assert data['path'] == '/tmp' or data['path'] == '/private/tmp', \
|
||||||
|
f\"unexpected path: {data['path']}\"
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "mo status --json outputs valid JSON with expected fields" {
|
||||||
|
if [[ ! -x "${STATUS_BIN:-}" ]]; then
|
||||||
|
skip "status binary not available (go not installed?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run "$STATUS_BIN" --json
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
# Validate it is parseable JSON
|
||||||
|
echo "$output" | python3 -c "import sys, json; json.load(sys.stdin)"
|
||||||
|
|
||||||
|
# Check required top-level keys
|
||||||
|
echo "$output" | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
for key in ['cpu', 'memory', 'disks', 'health_score', 'host', 'uptime']:
|
||||||
|
assert key in data, f'missing key: {key}'
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "mo status --json cpu section has expected structure" {
|
||||||
|
if [[ ! -x "${STATUS_BIN:-}" ]]; then
|
||||||
|
skip "status binary not available (go not installed?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run "$STATUS_BIN" --json
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
echo "$output" | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
cpu = data['cpu']
|
||||||
|
assert 'usage' in cpu, 'cpu missing usage'
|
||||||
|
assert 'logical_cpu' in cpu, 'cpu missing logical_cpu'
|
||||||
|
assert isinstance(cpu['usage'], (int, float)), 'cpu usage is not a number'
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "mo status --json memory section has expected structure" {
|
||||||
|
if [[ ! -x "${STATUS_BIN:-}" ]]; then
|
||||||
|
skip "status binary not available (go not installed?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run "$STATUS_BIN" --json
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
echo "$output" | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
mem = data['memory']
|
||||||
|
assert 'total' in mem, 'memory missing total'
|
||||||
|
assert 'used' in mem, 'memory missing used'
|
||||||
|
assert 'used_percent' in mem, 'memory missing used_percent'
|
||||||
|
assert mem['total'] > 0, 'memory total should be positive'
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "mo status --json piped to stdout auto-detects JSON mode" {
|
||||||
|
if [[ ! -x "${STATUS_BIN:-}" ]]; then
|
||||||
|
skip "status binary not available (go not installed?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# When piped (not a tty), status should auto-detect and output JSON
|
||||||
|
output=$("$STATUS_BIN" 2>/dev/null)
|
||||||
|
echo "$output" | python3 -c "import sys, json; json.load(sys.stdin)"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user