summaryrefslogtreecommitdiff
path: root/cmd/ask/main.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-26 18:16:11 +0300
committerPaul Buetow <paul@buetow.org>2026-04-26 18:16:11 +0300
commitc4b872c5ec54340b1e62d7578ace400340573ce2 (patch)
treed224707b3dd25ac68693d44f084dbe85a0edd685 /cmd/ask/main.go
parentf77d73244fa6585af0456fd374988b8b04b72646 (diff)
test: bring every package above the 80% coverage target
Per-package coverage was below the AGENTS.md target in six packages: cmd/ask 0.0% -> 83.3% cmd/hexai-tmux-edit 10.0% -> 93.3% cmd/hexai-tmux-action 27.8% -> 95.7% cmd/hexai-mcp-server 41.9% -> 88.2% internal/taskproxy 61.8% -> 98.2% internal/filelock 77.3% -> 100.0% The four cmd packages each had a main() that mixed flag parsing, struct construction, and runtime delegation, so nothing called from a test hit those statements. Each main() is now a one-line wrapper around a testable runMain(args, stdin, stdout, stderr) int that uses flag.NewFlagSet (instead of the global flag.Parse) so tests can drive it repeatedly. The deprecation banner in hexai-mcp-server is now a package-level constant, kept identical, so tests can assert on it directly without redirecting os.Stderr. The internal packages got new tests for paths that were previously unreachable: filelock's retry-then-success and non-EWOULDBLOCK error branches, and taskproxy's NewRunner / findTaskBinary / detectRepoRoot / runTaskCommand helpers (the ones that shell out to git and task). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'cmd/ask/main.go')
-rw-r--r--cmd/ask/main.go31
1 files changed, 22 insertions, 9 deletions
diff --git a/cmd/ask/main.go b/cmd/ask/main.go
index afab992..fbd5bb0 100644
--- a/cmd/ask/main.go
+++ b/cmd/ask/main.go
@@ -3,20 +3,33 @@ package main
import (
"context"
"fmt"
+ "io"
"os"
"codeberg.org/snonux/hexai/internal/askcli"
)
-func main() {
- d := askcli.NewDispatcher(nil)
- code, err := d.Dispatch(context.Background(), os.Args[1:], os.Stdin, os.Stdout, os.Stderr)
+// dispatcher is the minimal interface runMain depends on; it matches
+// (*askcli.Dispatcher).Dispatch so a real dispatcher satisfies it directly.
+type dispatcher interface {
+ Dispatch(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error)
+}
+
+// dispatcherFactory is a test seam: override to inject a fake dispatcher so
+// runMain can be exercised without a real `task` binary on PATH.
+var dispatcherFactory = func() dispatcher {
+ return askcli.NewDispatcher(nil)
+}
+
+func main() { os.Exit(runMain(os.Args[1:], os.Stdin, os.Stdout, os.Stderr)) }
+
+// runMain dispatches the command and returns the process exit code; errors
+// are printed to stderr. The dispatcher's exit code is returned regardless
+// of err so callers see Taskwarrior's own exit code on failure paths.
+func runMain(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
+ code, err := dispatcherFactory().Dispatch(context.Background(), args, stdin, stdout, stderr)
if err != nil {
- // Print the internal error so callers get a useful diagnostic message.
- fmt.Fprintln(os.Stderr, err)
- os.Exit(code)
- }
- if code != 0 {
- os.Exit(code)
+ fmt.Fprintln(stderr, err)
}
+ return code
}