Artifact 666328d73abb80cf3afbf0586007e33b31dde5f0f90230ad9306346978d91fee:
- File
cli/version_test.go
— part of check-in
[725c694cb9]
at
2026-07-19 23:33:37
on branch trunk
— Add libfossil version command (#99)
* Add libfossil version command
Sources the version string from runtime/debug.ReadBuildInfo (module version for go-install/tagged builds, falling back to a short VCS commit), with a -ldflags override for release builds where BuildInfo alone would collapse to "(devel)". Output is a fixed, single-line, four-field shape ("libfossil <version> <go version> <os>/<arch>") so a caller can rely on strings.Fields(Version()) unconditionally -- the motivating consumer stamps this into every emitted benchmark record.
No global --version flag: repo extract already owns --version as a command-scoped flag (source version to extract), and kong's global flags are visible in every subcommand's context, so a root-level --version would collide with it. version is a subcommand only.
Verified kong's existing parse-error path already exits non-zero (exitUsageError, 80) for unrecognized commands and flags against the pinned kong version -- added regression tests locking that in rather than changing behavior that was already correct.
* Reject malformed -ldflags version overrides at startup
buildVersion was used verbatim with no validation, so a whitespace- or newline-containing -X override silently broke Version()'s promised single-line, four-field contract: a space produced six fields instead of four, and a newline broke the single-line guarantee outright. The stated consumer is a benchmark harness parsing this line as a stable record, so a corrupted version field silently corrupts every record from that build in exactly the field meant to identify it.
A bad -X value is a build-time operator mistake, not a runtime condition, so it's rejected at package init rather than sanitized or worked around: sanitizing would silently mangle the operator's intent, and falling back to build info would report a version that is confidently wrong. Both would hide the mistake in the one field whose job is to be trustworthy. Failing the whole binary at startup surfaces a bad build the first time it runs, for any command, not just when someone happens to run `version`.
Also simplified versionFromBuildInfo: verified empirically (go1.26, `go build .` inside this checkout, with and without -buildvcs and inside/outside a VCS checkout) that Main.Version is only ever the uninformative "(devel)" in exactly the cases where vcs.revision is also absent from BuildInfo.Settings -- whenever a VCS revision is available, Go has already folded it into Main.Version as a pseudo-version. The separate vcs.revision fallback loop was dead code that could only ever duplicate a hash already present in Main.Version; removed it along with the now-unused shortCommit helper. (user: [email protected], size: 3970) [annotate] [blame] [check-ins using]
package cli import ( "io" "os" "runtime" "strings" "testing" ) // TestVersionShape verifies Version() always produces the same fixed shape // -- four whitespace-separated fields -- regardless of what build // information happens to be available, so a downstream parser can rely on // strings.Fields(Version()) unconditionally. func TestVersionShape(t *testing.T) { saved := buildVersion buildVersion = "" defer func() { buildVersion = saved }() v := Version() if strings.Contains(v, "\n") { t.Fatalf("Version() = %q, must be a single line", v) } fields := strings.Fields(v) if len(fields) != 4 { t.Fatalf("Version() = %q, want 4 whitespace-separated fields, got %d", v, len(fields)) } if fields[0] != "libfossil" { t.Errorf("field[0] = %q, want %q", fields[0], "libfossil") } if fields[1] == "" { t.Errorf("field[1] (version) must not be empty") } if !strings.HasPrefix(fields[2], "go") { t.Errorf("field[2] (Go version) = %q, want prefix %q", fields[2], "go") } wantPlatform := runtime.GOOS + "/" + runtime.GOARCH if fields[3] != wantPlatform { t.Errorf("field[3] (platform) = %q, want %q", fields[3], wantPlatform) } } // TestVersionBuildVersionOverrideWins verifies that a build-time ldflags // override is used verbatim as the version field, taking priority over // whatever runtime/debug.ReadBuildInfo would otherwise report -- this is // the release-build contract described in the ldflags comment on // buildVersion. func TestVersionBuildVersionOverrideWins(t *testing.T) { saved := buildVersion buildVersion = "v9.9.9-test" defer func() { buildVersion = saved }() fields := strings.Fields(Version()) if len(fields) != 4 { t.Fatalf("Version() fields = %d, want 4", len(fields)) } if fields[1] != "v9.9.9-test" { t.Errorf("field[1] = %q, want override %q used verbatim", fields[1], "v9.9.9-test") } } // TestAssertValidBuildVersionRejectsWhitespace is the regression test for // the defect where an unsanitized -ldflags override broke Version()'s // single-line, four-field contract: a space-containing value produced six // fields instead of four, and a newline-containing value broke the // single-line guarantee outright. Both must be rejected at the source // (buildVersion, checked in init) rather than reaching Version() at all -- // see the comment on init for why this fails the whole binary rather than // sanitizing or falling back. func TestAssertValidBuildVersionRejectsWhitespace(t *testing.T) { cases := []struct { name string v string }{ {"space", "v1.0 with spaces"}, {"newline", "v1.0\nwith a newline"}, {"tab", "v1.0\twith a tab"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("assertValidBuildVersion(%q) did not panic, want panic", tc.v) } }() assertValidBuildVersion(tc.v) }) } } // TestAssertValidBuildVersionAcceptsCleanValues is the negative-space // counterpart: values with no whitespace -- including empty, meaning no // override was supplied -- must not panic. func TestAssertValidBuildVersionAcceptsCleanValues(t *testing.T) { for _, v := range []string{"", "v0.6.3", "v0.6.3+a1b2c3d4e5f6"} { assertValidBuildVersion(v) // must not panic } } // TestVersionCmdRunPrintsExactlyOneLine verifies the version command's // stdout output is exactly Version() plus a trailing newline -- no extra // diagnostic output a downstream parser would need to filter. func TestVersionCmdRunPrintsExactlyOneLine(t *testing.T) { r, w, err := os.Pipe() if err != nil { t.Fatalf("Pipe: %v", err) } orig := os.Stdout os.Stdout = w var c VersionCmd runErr := c.Run() w.Close() os.Stdout = orig if runErr != nil { t.Fatalf("VersionCmd.Run() error = %v", runErr) } out, err := io.ReadAll(r) if err != nil { t.Fatalf("ReadAll: %v", err) } want := Version() + "\n" if string(out) != want { t.Errorf("VersionCmd.Run() printed %q, want %q", out, want) } }