Repository navigation
feat(sm): Update check test subcommand to target existing checks - #1706
Conversation
Adds the ability to pass in a check name to execute a test against. Previously, it was impossibly to test an existing check without gathering the full spec and rewriting it.
There was a problem hiding this comment.
Intent. gcx synthetic-monitoring checks test could only run a manifest, so testing a saved check meant exporting its full spec first. The change adds an optional NAME positional that loads the saved check through the same TypedCRUD path checks get uses, and makes NAME and --filename mutually exclusive.
Blocking. One finding: the two new usage errors in testOpts.Validate exit 1, while docs/design/exit-codes.md assigns exit 2 to bad positional arguments and missing required flags. The same command already exits 2 when two names are given, so one class of mistake now reports two codes.
Other findings. loadTestSpec repeats the name check and lookup that checks get performs, and fetches the probe list a second time on the NAME path. Nit only.
Fix summary. Wrap both testOpts.Validate errors in fail.NewCommandUsageError. Optionally factor the shared check lookup out of loadTestSpec and checks get.
Assessment. The approach is a good fit: it reuses the existing resource path rather than adding a second one, and the test coverage (slug name, numeric ID, unknown check, name without an ID, stale probe ID, and both source argument errors) is proportionate to a 35 line production change. The exit code finding is the only thing worth fixing before merge; everything else is optional.
Comments below are prefixed required (1) and nit (1). required means fix before merge; nit means take it or leave it.
Comment @claude review for a fresh review.
| func (o *testOpts) Validate() error { | ||
| if o.File == "" { | ||
| return errors.New("--filename/-f is required") | ||
| func (o *testOpts) Validate(args []string) error { |
There was a problem hiding this comment.
required — Both new branches return a plain errors.New, which no converter in cmd/gcx/fail/convert.go recognises, so the command exits 1. docs/design/exit-codes.md assigns exit 2 to bad positional arguments and missing required flags, and cobra.MaximumNArgs(1) on this same command already exits 2 when two names are given, so one class of mistake now reports two codes. fail.NewCommandUsageError(cmd, msg, nil) fixes it, as it does for the same -f is required case at internal/providers/alert/time_intervals_commands.go:146; it needs cmd in scope, so either move the check into RunE or pass the command in. If synth deliberately keeps exit 1 across the provider (status.go:333 returns a plain error for its own mutually exclusive flags), say so and I will drop this.
| // loadTestSpec returns the check to run: the manifest in --filename, or the | ||
| // saved check named by args[0] (a resource name or numeric ID). Validate has | ||
| // already ensured exactly one of the two is set. | ||
| func loadTestSpec(ctx context.Context, loader smcfg.Loader, file string, args []string) (*CheckSpec, error) { |
There was a problem hiding this comment.
nit — loadTestSpec repeats the lookup checks get performs at commands.go:233-245, down to a byte identical error string, and on the NAME path the probe list is fetched twice: FetchProbeInfo fetches it at the top of RunE, then NewTypedCRUD's GetFn fetches it again to turn the saved check's probe IDs into names, which resolveProbeIDs immediately turns back into IDs. A shared helper that returns a *checkResource for a name would give both commands one place to change. Separately, the function takes args []string and indexes args[0] under an invariant established in Validate; taking name string would keep that contract local. Staying symmetric with checks get is a fair reason to leave all of this as it is.
radiohead
left a comment
There was a problem hiding this comment.
LGTM. Adding NAME is backward compatible with the -f usage. It only reads the saved check, rejects stale probes before running, and the tests cover the source and usage-error matrix well.
Nits:
- The
checks testhint ininternal/agent/command_annotations.go(L681) still says only-f <check.yaml>. Consider<name> | -f <check.yaml>so agents find the saved-check form. - The NAME path fetches probes and the SM proxy config twice (
FetchProbeInfoplusNewTypedCRUD). Minor, fine to leave.
Generated by Claude Code
Adds the ability to pass in a check name to execute a test against. Previously, it was impossibly to test an existing check without gathering the full spec and rewriting it.
What
Why
How it was tested
PR checklist
GCX_AGENT_MODE=false mise run alland verified that it passes.