← back to Cli Printing Press
fix(generator): rename trailing '_test' stems to avoid Go test-file exclusion (#1020) (#1021)
a03a7b81685062ec0abd6faa5fb182d021ae04d7 · 2026-05-11 00:34:24 -0600 · Juany899
safeResourceFileStem already renames stems ending in a GOOS or GOARCH token
to dodge Go's filename-based build constraints. The 'test' suffix follows the
same logic: Go treats *_test.go as a test file and excludes it from the normal
package build, so any operationId whose lowercase prefix is 'test' (e.g.,
testWebhook -> internal/cli/webhook_test.go) yields an unresolved constructor
in the parent grouper file:
internal/cli/webhook.go:20: undefined: newWebhookTestCmd
Treat trailing 'test' the same way: rename to *_test_cmd.go. Caller surfaces
(webhook.go's AddCommand and webhook_test_cmd.go's constructor) line up.
Repro discovered while generating a CLI for Salesforce Spiff; the
testWebhook operationId is the natural name in the upstream Spiff docs.
Co-authored-by: Juan Andrés Fernández Camacho <juanandresfernandezcamacho@Juans-MacBook-Pro.local>
Files touched
M internal/generator/generator.goM internal/generator/safe_filename_test.go
Diff
commit a03a7b81685062ec0abd6faa5fb182d021ae04d7
Author: Juany899 <77075893+Juany899@users.noreply.github.com>
Date: Mon May 11 00:34:24 2026 -0600
fix(generator): rename trailing '_test' stems to avoid Go test-file exclusion (#1020) (#1021)
safeResourceFileStem already renames stems ending in a GOOS or GOARCH token
to dodge Go's filename-based build constraints. The 'test' suffix follows the
same logic: Go treats *_test.go as a test file and excludes it from the normal
package build, so any operationId whose lowercase prefix is 'test' (e.g.,
testWebhook -> internal/cli/webhook_test.go) yields an unresolved constructor
in the parent grouper file:
internal/cli/webhook.go:20: undefined: newWebhookTestCmd
Treat trailing 'test' the same way: rename to *_test_cmd.go. Caller surfaces
(webhook.go's AddCommand and webhook_test_cmd.go's constructor) line up.
Repro discovered while generating a CLI for Salesforce Spiff; the
testWebhook operationId is the natural name in the upstream Spiff docs.
Co-authored-by: Juan Andrés Fernández Camacho <juanandresfernandezcamacho@Juans-MacBook-Pro.local>
---
internal/generator/generator.go | 19 ++++++++++++++-----
internal/generator/safe_filename_test.go | 24 ++++++++++++++++++++++++
2 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index 8ffa6d30..28a0885f 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -3653,22 +3653,25 @@ var goarchTokens = map[string]struct{}{
// safeResourceFileStem returns a basename (without .go) safe to write under
// internal/cli/, suffixing "_cmd" if the bare stem matches Go's filename-based
-// build-constraint pattern (*_<GOOS>.go, *_<GOARCH>.go, *_<GOOS>_<GOARCH>.go).
-// Without this rename, a file like scheduling_windows.go would get an implicit
-// Windows-only build tag and be silently excluded on macOS/Linux builds.
+// build-constraint pattern (*_<GOOS>.go, *_<GOARCH>.go, *_<GOOS>_<GOARCH>.go)
+// or Go's test-file pattern (*_test.go). Without this rename a file like
+// scheduling_windows.go would get an implicit Windows-only build tag and be
+// silently excluded on macOS/Linux builds, and a file like webhook_test.go
+// would be treated as a test file and excluded from the normal package build.
//
// The reserved-name collision is handled separately at spec-parse time
// (see ReservedCLIResourceNames) because the function-name collision needs a
// hard error rather than a silent rename — `new<Name>Cmd` would clash with
// the reserved template's identically-named cobra builder.
//
-// The suffix "_cmd" is never itself a GOOS or GOARCH token, so a single
-// application is sufficient.
+// The suffix "_cmd" is never itself a GOOS, GOARCH, or "test" token, so a
+// single application is sufficient.
//
// Examples:
//
// safeResourceFileStem("scheduling_windows") -> "scheduling_windows_cmd"
// safeResourceFileStem("foo_linux_amd64") -> "foo_linux_amd64_cmd"
+// safeResourceFileStem("webhook_test") -> "webhook_test_cmd"
// safeResourceFileStem("scheduling_window_days") -> "scheduling_window_days" (no change)
// safeResourceFileStem("feedback") -> "feedback" (no change; rejected at parse)
func safeResourceFileStem(stem string) string {
@@ -3681,6 +3684,12 @@ func safeResourceFileStem(stem string) string {
if _, isArch := goarchTokens[last]; isArch {
return stem + "_cmd"
}
+ if last == "test" {
+ // Go treats *_test.go as a test file and excludes it from
+ // the normal package build. Suffix "_cmd" keeps the file in
+ // the normal build.
+ return stem + "_cmd"
+ }
}
if len(parts) >= 3 {
// Match the *_GOOS_GOARCH.go pattern (e.g., foo_linux_amd64.go).
diff --git a/internal/generator/safe_filename_test.go b/internal/generator/safe_filename_test.go
index 7122cd24..4e3b6df4 100644
--- a/internal/generator/safe_filename_test.go
+++ b/internal/generator/safe_filename_test.go
@@ -86,6 +86,30 @@ func TestSafeResourceFileStem(t *testing.T) {
expected: "store_arm64_endpoint",
why: "'endpoint' is not a GOARCH/GOOS; only triggers on the last token",
},
+ {
+ name: "trailing 'test' triggers rename",
+ stem: "webhook_test",
+ expected: "webhook_test_cmd",
+ why: "Go treats *_test.go as a test file and excludes it from the normal package build",
+ },
+ {
+ name: "trailing 'test' from multi-segment stem triggers rename",
+ stem: "connector_verify_test",
+ expected: "connector_verify_test_cmd",
+ why: "only the trailing token matters; nested operationIds ending in 'test' also collide",
+ },
+ {
+ name: "embedded 'test' in middle position unchanged",
+ stem: "test_helpers_endpoint",
+ expected: "test_helpers_endpoint",
+ why: "'test' must be the trailing token for the *_test.go rule to apply",
+ },
+ {
+ name: "stem with 'tester' suffix unchanged",
+ stem: "load_tester",
+ expected: "load_tester",
+ why: "'tester' is not 'test'; only exact-suffix tokens trigger",
+ },
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
← 4fac827e feat(cli): point users at where to get a token (URL + instru
·
back to Cli Printing Press
·
fix(cli): sync skips resources with unresolved {key} placeho 5fabad63 →