2026-10-06 16:38:15 +00:00
package gotool
import (
2026-10-09 01:44:33 +00:00
"bytes"
2026-10-06 16:38:15 +00:00
"context"
"encoding/json"
2026-10-06 18:59:29 +00:00
"errors"
"fmt"
2026-11-02 03:31:59 +00:00
"io"
2026-10-06 16:38:15 +00:00
"os"
2026-10-06 18:59:29 +00:00
"os/exec"
2026-10-06 16:38:15 +00:00
"path/filepath"
"runtime"
"strings"
2026-11-02 03:31:59 +00:00
"sync"
2026-10-06 16:38:15 +00:00
"testing"
2026-10-06 18:59:29 +00:00
"time"
2026-10-06 16:38:15 +00:00
)
2026-10-06 18:59:29 +00:00
type goPreparationRecord struct {
Arguments [ ] string
Directory string
Workspace string
Toolchain string
Proxy string
Private string
NoSumDB string
ModuleCache string
}
func TestMain ( m * testing . M ) {
if os . Getenv ( "RELEASEKIT_GO_FIXTURE" ) == "1" {
os . Exit ( runGoPreparationFixture ( ) )
}
os . Exit ( m . Run ( ) )
}
func runGoPreparationFixture ( ) int {
record := goPreparationRecord {
Arguments : os . Args [ 1 : ] , Directory : mustGetwd ( ) ,
Workspace : os . Getenv ( "GOWORK" ) , Toolchain : os . Getenv ( "GOTOOLCHAIN" ) ,
Proxy : os . Getenv ( "GOPROXY" ) , Private : os . Getenv ( "GOPRIVATE" ) ,
NoSumDB : os . Getenv ( "GONOSUMDB" ) , ModuleCache : os . Getenv ( "GOMODCACHE" ) ,
}
contents , err := json . Marshal ( record )
if err != nil {
return 10
}
if err := os . WriteFile ( os . Getenv ( "RELEASEKIT_GO_FIXTURE_RECORD" ) , contents , 0 o600 ) ; err != nil {
return 11
}
switch os . Getenv ( "RELEASEKIT_GO_FIXTURE_MODE" ) {
2026-10-07 00:45:23 +00:00
case "discovery" :
settings := make ( map [ string ] string )
for _ , key := range os . Args [ 3 : ] {
settings [ key ] = os . Getenv ( key )
}
settings [ "GOHOSTOS" ] = runtime . GOOS
settings [ "GOHOSTARCH" ] = runtime . GOARCH
fmt . Fprintln ( os . Stderr , "fixture discovery diagnostic" )
if err := json . NewEncoder ( os . Stdout ) . Encode ( settings ) ; err != nil {
return 12
}
return 0
2026-10-06 18:59:29 +00:00
case "fail" :
fmt . Fprintln ( os . Stderr , "fixture dependency failure" )
return 23
2026-10-09 01:44:33 +00:00
case "output" :
fmt . Fprintln ( os . Stdout , "fixture stdout" )
fmt . Fprintln ( os . Stderr , "fixture stderr" )
return 0
case "large-fail" :
_ , _ = fmt . Fprint ( os . Stdout , strings . Repeat ( "x" , 8 << 20 ) )
fmt . Fprintln ( os . Stderr , "fixture large failure" )
return 23
2026-10-06 18:59:29 +00:00
case "wait" :
for {
time . Sleep ( time . Second )
}
default :
return 0
}
}
2026-10-07 00:45:23 +00:00
func TestDiscoveryAcceptsDiagnosticsWithValidConfiguration ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "discovery" )
cache := filepath . Join ( t . TempDir ( ) , "cache" )
t . Setenv ( "GOCACHE" , cache )
tool , err := New ( context . Background ( ) , repository )
if err != nil {
t . Fatalf ( "New() rejected successful discovery: %v" , err )
}
if tool . HostTarget ( ) != ( Target { OS : runtime . GOOS , Arch : runtime . GOARCH } ) || tool . settings [ "GOCACHE" ] != cache {
t . Fatalf ( "discovered host=%v cache=%q" , tool . HostTarget ( ) , tool . settings [ "GOCACHE" ] )
}
}
func TestDiscoveryPreservesFailureDiagnosticsAndCause ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "fail" )
_ , err := New ( context . Background ( ) , repository )
var exitError * exec . ExitError
if ! errors . As ( err , & exitError ) || exitError . ExitCode ( ) != 23 || ! strings . Contains ( err . Error ( ) , "fixture dependency failure" ) {
t . Fatalf ( "New() error = %v" , err )
}
}
2026-10-06 18:59:29 +00:00
func mustGetwd ( ) string {
directory , err := os . Getwd ( )
if err != nil {
return ""
}
return directory
}
func installGoPreparationFixture ( t * testing . T ) ( repository , recordPath string ) {
t . Helper ( )
directory := t . TempDir ( )
repository = t . TempDir ( )
executable , err := os . Executable ( )
if err != nil {
t . Fatal ( err )
}
contents , err := os . ReadFile ( executable )
if err != nil {
t . Fatal ( err )
}
name := "go"
if runtime . GOOS == "windows" {
name += ".exe"
}
if err := os . WriteFile ( filepath . Join ( directory , name ) , contents , 0 o755 ) ; err != nil {
t . Fatal ( err )
}
recordPath = filepath . Join ( t . TempDir ( ) , "go-command.json" )
t . Setenv ( "PATH" , directory + string ( os . PathListSeparator ) + os . Getenv ( "PATH" ) )
t . Setenv ( "RELEASEKIT_GO_FIXTURE" , "1" )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_RECORD" , recordPath )
return repository , recordPath
}
func TestPrepareBuildDependenciesPreservesPreparationEnvironment ( t * testing . T ) {
repository , recordPath := installGoPreparationFixture ( t )
moduleCache := filepath . Join ( t . TempDir ( ) , "module-cache" )
t . Setenv ( "GOWORK" , filepath . Join ( t . TempDir ( ) , "go.work" ) )
t . Setenv ( "GOTOOLCHAIN" , "auto" )
t . Setenv ( "GOPROXY" , "https://proxy.example.invalid" )
t . Setenv ( "GOPRIVATE" , "example.invalid/private" )
t . Setenv ( "GONOSUMDB" , "example.invalid/private" )
t . Setenv ( "GOMODCACHE" , moduleCache )
if err := PrepareBuildDependencies ( context . Background ( ) , repository ) ; err != nil {
t . Fatal ( err )
}
contents , err := os . ReadFile ( recordPath )
if err != nil {
t . Fatal ( err )
}
var record goPreparationRecord
if err := json . Unmarshal ( contents , & record ) ; err != nil {
t . Fatal ( err )
}
2026-11-01 21:12:22 +00:00
if strings . Join ( record . Arguments , " " ) != "mod download -modfile=" || record . Directory != repository {
2026-10-06 18:59:29 +00:00
t . Fatalf ( "Go invocation = %#v" , record )
}
if record . Workspace != "off" || record . Toolchain != "local" {
t . Fatalf ( "preparation Go settings = %#v" , record )
}
if record . Proxy != "https://proxy.example.invalid" || record . Private != "example.invalid/private" || record . NoSumDB != "example.invalid/private" || record . ModuleCache != moduleCache {
t . Fatalf ( "caller dependency settings were not preserved: %#v" , record )
}
}
func TestPrepareBuildDependenciesPropagatesCommandFailure ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "fail" )
err := PrepareBuildDependencies ( context . Background ( ) , repository )
var exitError * exec . ExitError
if ! errors . As ( err , & exitError ) || exitError . ExitCode ( ) != 23 || ! strings . Contains ( err . Error ( ) , "fixture dependency failure" ) {
t . Fatalf ( "PrepareBuildDependencies() error = %v" , err )
}
}
func TestPrepareBuildDependenciesPropagatesCancellation ( t * testing . T ) {
repository , recordPath := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "wait" )
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
done := make ( chan error , 1 )
go func ( ) { done <- PrepareBuildDependencies ( ctx , repository ) } ( )
deadline := time . Now ( ) . Add ( 5 * time . Second )
for {
if _ , err := os . Stat ( recordPath ) ; err == nil {
break
}
if time . Now ( ) . After ( deadline ) {
t . Fatal ( "Go fixture did not start" )
}
time . Sleep ( 10 * time . Millisecond )
}
cancel ( )
select {
case err := <- done :
if ! errors . Is ( err , context . Canceled ) {
t . Fatalf ( "PrepareBuildDependencies() error = %v; want cancellation" , err )
}
case <- time . After ( 5 * time . Second ) :
t . Fatal ( "Go fixture did not stop after cancellation" )
}
}
2026-10-06 16:38:15 +00:00
func TestNativeAndStaticCommandsUseOwnedBaseline ( t * testing . T ) {
clearGoSettings ( t )
t . Setenv ( "GOENV" , "off" )
t . Setenv ( "GOFLAGS" , "-run=^$" )
t . Setenv ( "GOOS" , "windows" )
t . Setenv ( "GOARCH" , "arm64" )
t . Setenv ( "GOAMD64" , "v2" )
t . Setenv ( "GOEXPERIMENT" , "loopvar" )
tool , err := New ( context . Background ( ) , t . TempDir ( ) )
if err != nil {
t . Fatal ( err )
}
if tool . HostTarget ( ) != ( Target { OS : runtime . GOOS , Arch : runtime . GOARCH } ) {
t . Fatalf ( "Go host = %#v, want runtime host %s/%s" , tool . HostTarget ( ) , runtime . GOOS , runtime . GOARCH )
}
native := readSettings ( t , tool , Native , Target { } , "GOENV" , "GOOS" , "GOARCH" , "GOFLAGS" , "GOAMD64" , "GOEXPERIMENT" , "CGO_ENABLED" )
if native [ "GOENV" ] != "" || native [ "GOOS" ] != runtime . GOOS || native [ "GOARCH" ] != runtime . GOARCH {
t . Fatalf ( "native environment = %#v" , native )
}
if native [ "GOFLAGS" ] != "-mod=readonly" || native [ "GOAMD64" ] == "v2" || native [ "GOEXPERIMENT" ] != "" {
t . Fatalf ( "native Go settings = %#v" , native )
}
if native [ "CGO_ENABLED" ] == "" {
t . Fatalf ( "native cgo setting was not resolved: %#v" , native )
}
static := readSettings ( t , tool , Static , Target { OS : "linux" , Arch : "amd64" } , "GOOS" , "GOARCH" , "GOFLAGS" , "CGO_ENABLED" , "GOAMD64" , "GOEXPERIMENT" )
if static [ "GOOS" ] != "linux" || static [ "GOARCH" ] != "amd64" || static [ "CGO_ENABLED" ] != "0" {
t . Fatalf ( "static environment = %#v" , static )
}
if static [ "GOFLAGS" ] != "-mod=readonly" || static [ "GOAMD64" ] != "v1" || static [ "GOEXPERIMENT" ] != "" {
t . Fatalf ( "static baseline settings = %#v" , static )
}
}
func TestPersistentSettingsPreserveCachesAndNativeCTools ( t * testing . T ) {
clearGoSettings ( t )
cache := filepath . Join ( t . TempDir ( ) , "configured-cache" )
moduleCache := filepath . Join ( t . TempDir ( ) , "configured-module-cache" )
temporary := filepath . Join ( t . TempDir ( ) , "configured-temporary" )
if err := os . Mkdir ( temporary , 0 o700 ) ; err != nil {
t . Fatal ( err )
}
goEnv := filepath . Join ( t . TempDir ( ) , "go-env" )
contents := strings . Join ( [ ] string {
"GOFLAGS=-run=^$" ,
"GOOS=windows" ,
"GOARCH=arm64" ,
"GOAMD64=v2" ,
"GOEXPERIMENT=loopvar" ,
"GOCACHE=" + cache ,
"GOMODCACHE=" + moduleCache ,
"GOTMPDIR=" + temporary ,
"CGO_ENABLED=1" ,
"CC=releasekit-test-cc" ,
"CXX=releasekit-test-cxx" ,
"CGO_CFLAGS=-O0 -g" ,
"PKG_CONFIG=releasekit-test-pkg-config" ,
} , "\n" ) + "\n"
if err := os . WriteFile ( goEnv , [ ] byte ( contents ) , 0 o600 ) ; err != nil {
t . Fatal ( err )
}
t . Setenv ( "GOENV" , goEnv )
tool , err := New ( context . Background ( ) , t . TempDir ( ) )
if err != nil {
t . Fatal ( err )
}
if tool . HostTarget ( ) != ( Target { OS : runtime . GOOS , Arch : runtime . GOARCH } ) {
t . Fatalf ( "Go host = %#v, want runtime host %s/%s" , tool . HostTarget ( ) , runtime . GOOS , runtime . GOARCH )
}
native := readSettings ( t , tool , Native , Target { } , "GOENV" , "GOOS" , "GOARCH" , "GOFLAGS" , "GOAMD64" , "GOEXPERIMENT" , "CGO_ENABLED" , "CC" , "CXX" , "CGO_CFLAGS" , "GOCACHE" , "GOMODCACHE" , "GOTMPDIR" , "PKG_CONFIG" )
if native [ "GOENV" ] != "" || native [ "GOOS" ] != runtime . GOOS || native [ "GOARCH" ] != runtime . GOARCH {
t . Fatalf ( "native environment = %#v" , native )
}
if native [ "GOFLAGS" ] != "-mod=readonly" || native [ "GOAMD64" ] == "v2" || native [ "GOEXPERIMENT" ] != "" {
t . Fatalf ( "persisted overrides were not cleared: %#v" , native )
}
if native [ "CGO_ENABLED" ] != "1" || native [ "CC" ] != "releasekit-test-cc" || native [ "CXX" ] != "releasekit-test-cxx" || native [ "CGO_CFLAGS" ] != "-O0 -g" || native [ "PKG_CONFIG" ] != "releasekit-test-pkg-config" {
t . Fatalf ( "persistent native C settings = %#v" , native )
}
if native [ "GOCACHE" ] != cache || native [ "GOMODCACHE" ] != moduleCache || native [ "GOTMPDIR" ] != temporary {
t . Fatalf ( "persistent cache settings = %#v" , native )
}
static := readSettings ( t , tool , Static , Target { OS : "linux" , Arch : "amd64" } , "GOENV" , "GOOS" , "GOARCH" , "GOFLAGS" , "CGO_ENABLED" , "GOAMD64" , "GOEXPERIMENT" , "GOCACHE" )
if static [ "GOENV" ] != "" || static [ "GOOS" ] != "linux" || static [ "GOARCH" ] != "amd64" || static [ "CGO_ENABLED" ] != "0" || static [ "GOAMD64" ] != "v1" || static [ "GOEXPERIMENT" ] != "" || static [ "GOCACHE" ] != cache {
t . Fatalf ( "persistent static environment = %#v" , static )
}
}
2026-10-10 02:43:23 +00:00
func TestOwnerReusesSettingsUntilInheritedEnvironmentChanges ( t * testing . T ) {
clearGoSettings ( t )
t . Setenv ( "GOENV" , "off" )
owner := NewOwner ( t . TempDir ( ) )
first , err := owner . Acquire ( context . Background ( ) )
if err != nil {
t . Fatal ( err )
}
second , err := owner . Acquire ( context . Background ( ) )
if err != nil {
t . Fatal ( err )
}
if first != second {
t . Fatal ( "unchanged Go inputs caused settings discovery to be repeated" )
}
cache := filepath . Join ( t . TempDir ( ) , "changed-cache" )
t . Setenv ( "GOCACHE" , cache )
third , err := owner . Acquire ( context . Background ( ) )
if err != nil {
t . Fatal ( err )
}
if third == first || third . settings [ "GOCACHE" ] != cache {
t . Fatalf ( "changed inherited environment kept stale settings: tool=%p GOCACHE=%q" , third , third . settings [ "GOCACHE" ] )
}
}
2026-10-10 14:11:48 +00:00
func TestOwnerAcceptsGoConfigurationWithoutReadableSnapshot ( t * testing . T ) {
clearGoSettings ( t )
t . Setenv ( "GOENV" , t . TempDir ( ) )
directory := t . TempDir ( )
if _ , err := New ( context . Background ( ) , directory ) ; err != nil {
t . Fatalf ( "underlying Go discovery rejected fixture: %v" , err )
}
owner := NewOwner ( directory )
first , err := owner . Acquire ( context . Background ( ) )
if err != nil {
t . Fatalf ( "owner added a snapshot prerequisite: %v" , err )
}
second , err := owner . Acquire ( context . Background ( ) )
if err != nil || first == second {
t . Fatalf ( "unavailable snapshot should rediscover on demand: first=%p second=%p error=%v" , first , second , err )
}
}
2026-10-10 02:43:23 +00:00
func TestOwnerRefreshesWhenPersistentSettingsChange ( t * testing . T ) {
clearGoSettings ( t )
firstCache := filepath . Join ( t . TempDir ( ) , "first-cache" )
secondCache := filepath . Join ( t . TempDir ( ) , "second-cache" )
settingsFile := filepath . Join ( t . TempDir ( ) , "go-env" )
writeSettings := func ( cache string ) {
t . Helper ( )
if err := os . WriteFile ( settingsFile , [ ] byte ( "GOCACHE=" + cache + "\n" ) , 0 o600 ) ; err != nil {
t . Fatal ( err )
}
}
writeSettings ( firstCache )
t . Setenv ( "GOENV" , settingsFile )
owner := NewOwner ( t . TempDir ( ) )
first , err := owner . Acquire ( context . Background ( ) )
if err != nil {
t . Fatal ( err )
}
if first . settings [ "GOCACHE" ] != firstCache {
t . Fatalf ( "initial GOCACHE = %q, want %q" , first . settings [ "GOCACHE" ] , firstCache )
}
writeSettings ( secondCache )
second , err := owner . Acquire ( context . Background ( ) )
if err != nil {
t . Fatal ( err )
}
if second == first || second . settings [ "GOCACHE" ] != secondCache {
t . Fatalf ( "changed persistent settings were not rediscovered: tool=%p GOCACHE=%q" , second , second . settings [ "GOCACHE" ] )
}
}
2026-10-06 16:38:15 +00:00
func readSettings ( t * testing . T , tool * Tool , mode Mode , target Target , names ... string ) map [ string ] string {
t . Helper ( )
args := append ( [ ] string { "env" , "-json" } , names ... )
2026-10-09 01:44:33 +00:00
output , err := tool . Output ( context . Background ( ) , t . TempDir ( ) , mode , target , nil , args ... )
2026-10-06 16:38:15 +00:00
if err != nil {
t . Fatal ( err )
}
var settings map [ string ] string
if err := json . Unmarshal ( output , & settings ) ; err != nil {
t . Fatalf ( "decode Go environment %q: %v" , output , err )
}
return settings
}
2026-10-09 01:44:33 +00:00
func TestOutputReturnsAndForwardsCombinedOutput ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "output" )
tool := fixtureTool ( )
var forwarded bytes . Buffer
output , err := tool . Output ( context . Background ( ) , repository , Native , Target { } , & forwarded , "version" )
if err != nil {
t . Fatalf ( "Output() error = %v" , err )
}
if ! bytes . Contains ( output , [ ] byte ( "fixture stdout" ) ) || ! bytes . Contains ( output , [ ] byte ( "fixture stderr" ) ) {
t . Fatalf ( "combined output = %q" , output )
}
if ! bytes . Equal ( forwarded . Bytes ( ) , output ) {
t . Fatalf ( "forwarded output differs from capture: forwarded %d bytes, captured %d" , forwarded . Len ( ) , len ( output ) )
}
}
func TestRunForwardsOutputWithoutReturningCapture ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "output" )
tool := fixtureTool ( )
var forwarded bytes . Buffer
if err := tool . Run ( context . Background ( ) , repository , Native , Target { } , & forwarded , "version" ) ; err != nil {
t . Fatalf ( "Run() error = %v" , err )
}
if ! bytes . Contains ( forwarded . Bytes ( ) , [ ] byte ( "fixture stdout" ) ) || ! bytes . Contains ( forwarded . Bytes ( ) , [ ] byte ( "fixture stderr" ) ) {
t . Fatalf ( "forwarded output = %q" , forwarded . String ( ) )
}
}
func TestRunPreservesFailureDiagnosticsAndCause ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "fail" )
tool := fixtureTool ( )
err := tool . Run ( context . Background ( ) , repository , Native , Target { } , nil , "version" )
var exitError * exec . ExitError
if ! errors . As ( err , & exitError ) || exitError . ExitCode ( ) != 23 || ! strings . Contains ( err . Error ( ) , "fixture dependency failure" ) {
t . Fatalf ( "Run() error = %v" , err )
}
}
func TestOutputReturnsLargeFailureDiagnosticsAndCause ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "large-fail" )
tool := fixtureTool ( )
output , err := tool . Output ( context . Background ( ) , repository , Native , Target { } , nil , "version" )
var exitError * exec . ExitError
wantSize := ( 8 << 20 ) + len ( "fixture large failure\n" )
if len ( output ) != wantSize {
t . Fatalf ( "captured output = %d bytes, want %d" , len ( output ) , wantSize )
}
if ! errors . As ( err , & exitError ) || exitError . ExitCode ( ) != 23 || ! strings . Contains ( err . Error ( ) , "fixture large failure" ) {
t . Fatalf ( "Output() error = %v" , err )
}
}
2026-11-02 03:31:59 +00:00
func TestStatusCaptureRetainsTailAndForwardsAll ( t * testing . T ) {
for _ , test := range [ ] struct {
name string
size int
chunkSize int
} {
{ "below" , statusDiagnosticLimit - 1 , 8191 } ,
{ "exact" , statusDiagnosticLimit , 8191 } ,
{ "above" , statusDiagnosticLimit + 1 , 8191 } ,
{ "single oversized" , statusDiagnosticLimit * 2 + 3 , statusDiagnosticLimit * 2 + 3 } ,
{ "wrapped small writes" , statusDiagnosticLimit * 3 + 17 , 113 } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
input := make ( [ ] byte , test . size )
for i := range input {
input [ i ] = byte ( i % 251 )
}
var forwarded bytes . Buffer
writer := & combinedWriter { mode : captureStatus , output : & forwarded }
for start := 0 ; start < len ( input ) ; start += test . chunkSize {
chunk := input [ start : min ( start + test . chunkSize , len ( input ) ) ]
if n , err := writer . Write ( chunk ) ; n != len ( chunk ) || err != nil {
t . Fatalf ( "write = %d, %v" , n , err )
}
}
if writer . omitted ( ) != ( len ( input ) > statusDiagnosticLimit ) || cap ( writer . tail ) > statusDiagnosticLimit {
t . Fatalf ( "omitted = %t, capacity = %d" , writer . omitted ( ) , cap ( writer . tail ) )
}
want := input [ max ( 0 , len ( input ) - statusDiagnosticLimit ) : ]
if got := writer . take ( ) ; ! bytes . Equal ( got , want ) || ! bytes . Equal ( forwarded . Bytes ( ) , input ) {
t . Fatalf ( "capture size = %d, forwarded size = %d" , len ( got ) , forwarded . Len ( ) )
}
} )
}
}
func TestStatusCaptureSerializesConcurrentWriters ( t * testing . T ) {
var forwarded bytes . Buffer
writer := & combinedWriter { mode : captureStatus , output : & forwarded }
var group sync . WaitGroup
for _ , value := range [ ] byte { 'a' , 'b' } {
group . Add ( 1 )
go func ( ) {
defer group . Done ( )
chunk := bytes . Repeat ( [ ] byte { value } , 4096 )
for i := 0 ; i < 300 ; i ++ {
_ , _ = writer . Write ( chunk )
}
} ( )
}
group . Wait ( )
contents := forwarded . Bytes ( )
if ! writer . omitted ( ) || ! bytes . Equal ( writer . take ( ) , contents [ len ( contents ) - statusDiagnosticLimit : ] ) {
t . Fatal ( "concurrent capture differs from forwarded tail" )
}
}
func TestStatusCommandsRetainBoundedFailureDiagnostics ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "large-fail" )
var forwarded bytes . Buffer
err := fixtureTool ( ) . Run ( context . Background ( ) , repository , Native , Target { } , & forwarded , "version" )
var exitError * exec . ExitError
if ! errors . As ( err , & exitError ) || exitError . ExitCode ( ) != 23 || ! strings . Contains ( err . Error ( ) , "earlier Go output omitted" ) || ! strings . Contains ( err . Error ( ) , "fixture large failure" ) || len ( err . Error ( ) ) > statusDiagnosticLimit + 200 || forwarded . Len ( ) != ( 8 << 20 ) + len ( "fixture large failure\n" ) {
t . Fatalf ( "Run() error = %v, forwarded = %d bytes" , err , forwarded . Len ( ) )
}
err = PrepareBuildDependencies ( context . Background ( ) , repository )
if ! errors . As ( err , & exitError ) || exitError . ExitCode ( ) != 23 || ! strings . Contains ( err . Error ( ) , "earlier Go output omitted" ) || ! strings . Contains ( err . Error ( ) , "fixture large failure" ) || len ( err . Error ( ) ) > statusDiagnosticLimit + 200 {
t . Fatalf ( "preparation error did not retain bounded diagnostics: %v" , err )
}
}
func TestStatusCommandPreservesForwardingError ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "output" )
writerErr := errors . New ( "forwarding failed" )
err := fixtureTool ( ) . Run ( context . Background ( ) , repository , Native , Target { } , failingOutputWriter { err : writerErr } , "version" )
if ! errors . Is ( err , writerErr ) {
t . Fatalf ( "Run() error = %v" , err )
}
err = fixtureTool ( ) . Run ( context . Background ( ) , repository , Native , Target { } , shortOutputWriter { } , "version" )
if ! errors . Is ( err , io . ErrShortWrite ) {
t . Fatalf ( "Run() short-write error = %v" , err )
}
}
type shortOutputWriter struct { }
func ( shortOutputWriter ) Write ( [ ] byte ) ( int , error ) { return 0 , nil }
2026-10-09 01:44:33 +00:00
func TestOutputReturnsCancellation ( t * testing . T ) {
repository , recordPath := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "wait" )
tool := fixtureTool ( )
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
done := make ( chan error , 1 )
go func ( ) {
_ , err := tool . Output ( ctx , repository , Native , Target { } , nil , "version" )
done <- err
} ( )
deadline := time . Now ( ) . Add ( 5 * time . Second )
for {
if _ , err := os . Stat ( recordPath ) ; err == nil {
break
}
if time . Now ( ) . After ( deadline ) {
t . Fatal ( "Go fixture did not start" )
}
time . Sleep ( 10 * time . Millisecond )
}
cancel ( )
select {
case err := <- done :
if ! errors . Is ( err , context . Canceled ) {
t . Fatalf ( "Output() error = %v; want cancellation" , err )
}
case <- time . After ( 5 * time . Second ) :
t . Fatal ( "Go fixture did not stop after cancellation" )
}
}
func TestOutputPreservesForwardingWriterFailure ( t * testing . T ) {
repository , _ := installGoPreparationFixture ( t )
t . Setenv ( "RELEASEKIT_GO_FIXTURE_MODE" , "output" )
tool := fixtureTool ( )
writerErr := errors . New ( "fixture output writer failed" )
output , err := tool . Output ( context . Background ( ) , repository , Native , Target { } , failingOutputWriter { err : writerErr } , "version" )
if ! errors . Is ( err , writerErr ) || ! strings . Contains ( err . Error ( ) , "go version" ) {
t . Fatalf ( "Output() error = %v; want wrapped writer error and command" , err )
}
if len ( output ) == 0 {
t . Fatal ( "Output() returned no captured bytes after forwarding failed" )
}
}
type failingOutputWriter struct { err error }
func ( writer failingOutputWriter ) Write ( [ ] byte ) ( int , error ) { return 0 , writer . err }
func fixtureTool ( ) * Tool {
return & Tool { host : Target { OS : runtime . GOOS , Arch : runtime . GOARCH } , settings : map [ string ] string { } }
}
2026-10-06 16:38:15 +00:00
func clearGoSettings ( t * testing . T ) {
t . Helper ( )
for _ , key := range [ ] string {
"GOENV" , "GOFLAGS" , "GOWORK" , "GOTOOLCHAIN" , "GOPROXY" , "GONOPROXY" , "GOSUMDB" ,
"GOOS" , "GOARCH" , "GOEXPERIMENT" , "GOAMD64" , "GOARM" , "GOARM64" , "GO386" , "GOMIPS" , "GOMIPS64" , "GOPPC64" , "GORISCV64" , "GOWASM" ,
"CGO_ENABLED" , "CC" , "CXX" , "CGO_CFLAGS" , "CGO_CPPFLAGS" , "CGO_CXXFLAGS" , "CGO_LDFLAGS" , "GOCACHE" , "GOCACHEPROG" , "GOMODCACHE" , "GOPATH" , "GOROOT" , "GOTMPDIR" , "PKG_CONFIG" ,
} {
value , exists := os . LookupEnv ( key )
if err := os . Unsetenv ( key ) ; err != nil {
t . Fatal ( err )
}
t . Cleanup ( func ( ) {
if exists {
_ = os . Setenv ( key , value )
} else {
_ = os . Unsetenv ( key )
}
} )
}
}