Skip to content

[go-fan] Go Module Review: charmbracelet/x/exp/golden #33244

@github-actions

Description

@github-actions

🐹 Go Fan Report: charmbracelet/x/exp/golden

Module Overview

github.com/charmbracelet/x/exp/golden is a tiny but essential testing helper from the Charmbracelet x monorepo. It provides golden-file assertions for tests that produce terminal output (or any text), with automatic escape-sequence handling and a -update flag for regenerating fixtures. The package surface is intentionally minimal — one generic function (RequireEqual[T []byte | string]) does all the work.

  • Version pinned: v0.0.0-20260503005035-c113ba3d2310 (Go pseudo-version; subpackage has no tagged releases)
  • Repo last push: 2026-05-19 (top of the round-robin sort today, though exp/golden itself has been stable since 2026-03-09)
  • Selected via: round-robin priority by pushed_at desc, skipping modelcontextprotocol/go-sdk (reviewed 2026-05-18)

Current Usage in gh-aw

  • Go test files: 2 (pkg/console/golden_test.go, pkg/workflow/wasm_golden_test.go)
  • Call sites: 8 (all golden.RequireEqual, all passing string)
  • Golden files on disk: 52 under pkg/{console,workflow}/testdata/
  • Wire-up: Makefile targets update-golden, update-wasm-golden, test-wasm-golden; sister Node.js script scripts/test-wasm-golden.mjs
  • Deprecated API usage: none — RequireEqualEscape is correctly unused ✅

Research Findings

Recent Updates

  • 2025-06-02feat(exp/golden): accept strings on RequireEqual introduced the generic T []byte | string parameter (the project already uses it)
  • 2025-02-07 — Windows \r\n\n normalisation fix
  • 2024-11-18 — always show escaped golden files when comparing outputs
  • 2024-05-21 — initial escape-sequence support

The exp/golden subpackage has been stable for ~14 months. The repo-wide pushed_at (2026-05-19) is driven by activity in other subpackages (ansi, cellbuf, ultraviolet).

Best Practices (from upstream)

  • Use generic RequireEqual (do not call deprecated RequireEqualEscape)
  • Subtest names become path components: avoid / in t.Run names
  • Wire the -update flag into a Makefile target so contributors don't have to remember the magic incantation
  • Normalise non-deterministic output (timestamps, randomised tokens, digests) before comparison — already a pattern in wasm_golden_test.go:normalizeOutput

Improvement Opportunities

🏃 Quick Wins

1. Delete 15 orphaned .golden files (4 directories, no matching tests).
The test functions exist only as stub comments in pkg/console/golden_test.go:132,320,322:

Directory Files Status
pkg/console/testdata/TestGolden_LayoutBoxRendering/ 3 no test function
pkg/console/testdata/TestGolden_LayoutComposition/ 3 no test function
pkg/console/testdata/TestGolden_LayoutEmphasisBox/ 4 no test function
pkg/console/testdata/TestGolden_TreeRendering/ 5 no test function

Either restore the tests (the golden files contain the expected output already!) or delete the dead fixtures and the stub comments at golden_test.go:132,320,322.

✨ Feature Opportunities

2. Lock down TestWasmGolden_AllEngines with golden files.
The test at pkg/workflow/wasm_golden_test.go:265-317 currently only checks require.Contains(yamlOutput, "name:") / "on:" / "jobs:" per engine. Engine routing has been a regression hotspot before; a per-engine golden file (one for copilot, claude, codex, gemini, pi) would catch differences earlier at zero runtime cost. The normalisation helper normalizeOutput is already available.

3. Parallelise TestWasmGolden_CompileFixtures.
The subtest body at pkg/workflow/wasm_golden_test.go:62-103 does os.Chdir(absFixturesDir) and then os.Chdir(origDir) immediately before golden.RequireEqual. Because golden.RequireEqual resolves testdata/ relative to the process working directory (global state), t.Parallel() is unsafe today. Two paths forward:

  • Refactor to pass absolute paths to the compiler (the existing compiler.CompileToYAML(wd, fixture) accepts a filename arg) so the os.Chdir dance can be removed → then add t.Parallel().
  • If os.Chdir is truly required, add a comment documenting why parallelism is disabled.

📐 Best Practice Alignment

4. Output normalisation is already well done ✅. normalizeOutput() in pkg/workflow/wasm_golden_test.go:24-27 strips Docker @sha256: pins, AWF image-tag digests, and randomised heredoc delimiters before the golden comparison. This pattern matches upstream guidance and should be preserved.

5. Document the subtest-name convention. pkg/console/golden_test.go consistently uses snake_case subtest names ("simple_table", "wide_table") because subtests with / create unwanted subdirectories under testdata/. A one-line comment in the file header would prevent accidental t.Run("foo/bar", ...) calls from someone unfamiliar with the package's path semantics.

🔧 General Improvements

6. Track skipped wasm fixtures. t.Skipf in wasm_golden_test.go:84,90 silently drops fixtures that fail ParseWorkflowString or CompileToYAML. A single summary t.Logf("WasmGolden skipped %d/%d fixtures", ...) per parent test would surface coverage regressions over time.

7. Pseudo-version refresh cadence. The dependency is pinned by pseudo-version (v0.0.0-20260503005035-...) since exp/golden has no tagged releases for the subpackage. Periodically run go get -u github.com/charmbracelet/x/exp/golden@latest (e.g., via Dependabot which already runs in this repo) to pick up upstream fixes like Windows line-break handling.

Recommendations (prioritised)

  1. Delete the 15 orphaned .golden files (or restore the tests) — pure cleanup, no risk. Stub comments in pkg/console/golden_test.go:132,320,322 should go away too.
  2. Add per-engine golden files to TestWasmGolden_AllEngines — high value, low cost, follows the existing pattern.
  3. Investigate removing the os.Chdir dance in TestWasmGolden_CompileFixtures so it can run with t.Parallel() — bigger refactor but unlocks faster CI.
  4. Add a one-line comment about subtest-name conventions at the top of pkg/console/golden_test.go.

Next Steps

References

Generated by Go Fan 🐹
Module summary saved to: scratchpad/mods/charmbracelet-x-exp-golden.md

Generated by 🐹 Go Fan · ● 10M ·

  • expires on May 20, 2026, 9:00 AM UTC

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions