fix(web): harden gateway version probing and tests
This commit is contained in:
parent
6c58b23946
commit
6f974195a8
3 changed files with 165 additions and 174 deletions
|
|
@ -50,10 +50,11 @@ func isProcessRunning(pid int) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return strings.Contains(string(output), fmt.Sprintf(" %d ", pid))
|
return strings.Contains(string(output), fmt.Sprintf(" %d ", pid))
|
||||||
|
default:
|
||||||
|
// Linux and other unix-like systems.
|
||||||
|
_, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
|
||||||
|
return err == nil
|
||||||
}
|
}
|
||||||
// Linux
|
|
||||||
_, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
|
|
||||||
return err == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGatewayStatus() gatewayStatus {
|
func getGatewayStatus() gatewayStatus {
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,10 @@ type cachedSystemVersion struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type systemVersionCache struct {
|
type systemVersionCache struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
current cachedSystemVersion
|
current cachedSystemVersion
|
||||||
hasCurrent bool
|
hasCurrent bool
|
||||||
inflightCh chan struct{}
|
inflightCh chan struct{}
|
||||||
monitorPID int
|
|
||||||
monitorCancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSystemVersionCache() *systemVersionCache {
|
func newSystemVersionCache() *systemVersionCache {
|
||||||
|
|
@ -47,12 +45,13 @@ var (
|
||||||
// giving slow/embedded hosts enough time for first command invocation while
|
// giving slow/embedded hosts enough time for first command invocation while
|
||||||
// staying independent from cross-file init ordering.
|
// staying independent from cross-file init ordering.
|
||||||
versionCmdTimeout = 15 * time.Second
|
versionCmdTimeout = 15 * time.Second
|
||||||
findPicoclawBinaryForInfo = utils.FindPicoclawBinary
|
maxVersionResolveAttempts = 3
|
||||||
|
findPicoclawBinaryForInfo = resolveGatewayBinaryForVersionInfo
|
||||||
runPicoclawVersionOutput = executePicoclawVersion
|
runPicoclawVersionOutput = executePicoclawVersion
|
||||||
currentGatewayVersionState = gatewayVersionState
|
currentGatewayVersionState = gatewayVersionState
|
||||||
versionCacheMonitorInterval = 5 * time.Second
|
launcherBuildInfoForVersion = fallbackSystemVersionInfoFromConfig
|
||||||
versionInfoCache = newSystemVersionCache()
|
versionInfoCache = newSystemVersionCache()
|
||||||
versionLinePattern = regexp.MustCompile(`\bpicoclaw\s+([^\s(]+)(?:\s+\(git:\s*([^)]+)\))?`)
|
versionLinePattern = regexp.MustCompile(`^(?:[^A-Za-z0-9]*\s*)?picoclaw(?:\.exe)?\s+([^\s(]+)(?:\s+\(git:\s*([^)]+)\))?\s*$`)
|
||||||
ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*m`)
|
ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*m`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -74,7 +73,7 @@ func (h *Handler) handleGetVersion(w http.ResponseWriter, r *http.Request) {
|
||||||
// resolveSystemVersionInfo prefers the actual picoclaw binary version output,
|
// resolveSystemVersionInfo prefers the actual picoclaw binary version output,
|
||||||
// and falls back to launcher build metadata when command execution fails.
|
// and falls back to launcher build metadata when command execution fails.
|
||||||
func (h *Handler) resolveSystemVersionInfo(ctx context.Context) systemVersionResponse {
|
func (h *Handler) resolveSystemVersionInfo(ctx context.Context) systemVersionResponse {
|
||||||
for {
|
for range maxVersionResolveAttempts {
|
||||||
gatewayPID, gatewayAlive := currentGatewayVersionState()
|
gatewayPID, gatewayAlive := currentGatewayVersionState()
|
||||||
if cached, ok := versionInfoCache.get(gatewayPID, gatewayAlive); ok {
|
if cached, ok := versionInfoCache.get(gatewayPID, gatewayAlive); ok {
|
||||||
return cached
|
return cached
|
||||||
|
|
@ -93,6 +92,8 @@ func (h *Handler) resolveSystemVersionInfo(ctx context.Context) systemVersionRes
|
||||||
versionInfoCache.finishResolve(resolved, gatewayPID, gatewayAlive)
|
versionInfoCache.finishResolve(resolved, gatewayPID, gatewayAlive)
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fallbackSystemVersionInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) resolveSystemVersionInfoUncached(ctx context.Context) systemVersionResponse {
|
func (h *Handler) resolveSystemVersionInfoUncached(ctx context.Context) systemVersionResponse {
|
||||||
|
|
@ -131,6 +132,10 @@ func (h *Handler) resolveSystemVersionInfoUncached(ctx context.Context) systemVe
|
||||||
}
|
}
|
||||||
|
|
||||||
func fallbackSystemVersionInfo() systemVersionResponse {
|
func fallbackSystemVersionInfo() systemVersionResponse {
|
||||||
|
return launcherBuildInfoForVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
func fallbackSystemVersionInfoFromConfig() systemVersionResponse {
|
||||||
buildTime, goVer := config.FormatBuildInfo()
|
buildTime, goVer := config.FormatBuildInfo()
|
||||||
return systemVersionResponse{
|
return systemVersionResponse{
|
||||||
Version: config.GetVersion(),
|
Version: config.GetVersion(),
|
||||||
|
|
@ -140,6 +145,24 @@ func fallbackSystemVersionInfo() systemVersionResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveGatewayBinaryForVersionInfo uses the same executable as the launcher
|
||||||
|
// gateway start path when available, then falls back to launcher binary lookup.
|
||||||
|
// This keeps version probing aligned with the actual gateway startup behavior,
|
||||||
|
// so web and gateway do not drift onto different binaries.
|
||||||
|
func resolveGatewayBinaryForVersionInfo() string {
|
||||||
|
gateway.mu.Lock()
|
||||||
|
cmd := gateway.cmd
|
||||||
|
gateway.mu.Unlock()
|
||||||
|
|
||||||
|
if cmd != nil {
|
||||||
|
if execPath := strings.TrimSpace(cmd.Path); execPath != "" {
|
||||||
|
return execPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.FindPicoclawBinary()
|
||||||
|
}
|
||||||
|
|
||||||
func gatewayVersionState() (int, bool) {
|
func gatewayVersionState() (int, bool) {
|
||||||
gateway.mu.Lock()
|
gateway.mu.Lock()
|
||||||
defer gateway.mu.Unlock()
|
defer gateway.mu.Unlock()
|
||||||
|
|
@ -200,7 +223,6 @@ func (c *systemVersionCache) finishResolve(value systemVersionResponse, gatewayP
|
||||||
if gatewayAlive && gatewayPID > 0 {
|
if gatewayAlive && gatewayPID > 0 {
|
||||||
c.current = cachedSystemVersion{value: value, gatewayPID: gatewayPID}
|
c.current = cachedSystemVersion{value: value, gatewayPID: gatewayPID}
|
||||||
c.hasCurrent = true
|
c.hasCurrent = true
|
||||||
c.ensureMonitorLocked(gatewayPID)
|
|
||||||
} else {
|
} else {
|
||||||
c.clearCurrentLocked()
|
c.clearCurrentLocked()
|
||||||
}
|
}
|
||||||
|
|
@ -217,41 +239,6 @@ func (c *systemVersionCache) finishResolve(value systemVersionResponse, gatewayP
|
||||||
func (c *systemVersionCache) clearCurrentLocked() {
|
func (c *systemVersionCache) clearCurrentLocked() {
|
||||||
c.hasCurrent = false
|
c.hasCurrent = false
|
||||||
c.current = cachedSystemVersion{}
|
c.current = cachedSystemVersion{}
|
||||||
c.stopMonitorLocked()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *systemVersionCache) ensureMonitorLocked(gatewayPID int) {
|
|
||||||
if c.monitorPID == gatewayPID && c.monitorCancel != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.stopMonitorLocked()
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
c.monitorPID = gatewayPID
|
|
||||||
c.monitorCancel = cancel
|
|
||||||
|
|
||||||
go monitorGatewayVersionCache(ctx, gatewayPID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *systemVersionCache) stopMonitorLocked() {
|
|
||||||
if c.monitorCancel != nil {
|
|
||||||
c.monitorCancel()
|
|
||||||
c.monitorCancel = nil
|
|
||||||
}
|
|
||||||
c.monitorPID = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *systemVersionCache) invalidateForPID(gatewayPID int) {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
if c.hasCurrent && c.current.gatewayPID == gatewayPID {
|
|
||||||
c.current = cachedSystemVersion{}
|
|
||||||
c.hasCurrent = false
|
|
||||||
}
|
|
||||||
if c.monitorPID == gatewayPID {
|
|
||||||
c.stopMonitorLocked()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *systemVersionCache) resetForTest() {
|
func (c *systemVersionCache) resetForTest() {
|
||||||
|
|
@ -260,31 +247,12 @@ func (c *systemVersionCache) resetForTest() {
|
||||||
|
|
||||||
c.current = cachedSystemVersion{}
|
c.current = cachedSystemVersion{}
|
||||||
c.hasCurrent = false
|
c.hasCurrent = false
|
||||||
c.stopMonitorLocked()
|
|
||||||
if c.inflightCh != nil {
|
if c.inflightCh != nil {
|
||||||
close(c.inflightCh)
|
close(c.inflightCh)
|
||||||
c.inflightCh = nil
|
c.inflightCh = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func monitorGatewayVersionCache(ctx context.Context, gatewayPID int) {
|
|
||||||
ticker := time.NewTicker(versionCacheMonitorInterval)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
currentPID, alive := currentGatewayVersionState()
|
|
||||||
if !alive || currentPID != gatewayPID {
|
|
||||||
versionInfoCache.invalidateForPID(gatewayPID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// executePicoclawVersion runs the version subcommand against the
|
// executePicoclawVersion runs the version subcommand against the
|
||||||
// discovered picoclaw executable.
|
// discovered picoclaw executable.
|
||||||
func executePicoclawVersion(ctx context.Context, execPath string) (string, error) {
|
func executePicoclawVersion(ctx context.Context, execPath string) (string, error) {
|
||||||
|
|
@ -309,7 +277,11 @@ func parsePicoclawVersionOutput(raw string) (systemVersionResponse, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if match := versionLinePattern.FindStringSubmatch(line); len(match) > 0 {
|
if match := versionLinePattern.FindStringSubmatch(line); len(match) > 0 {
|
||||||
result.Version = strings.TrimSpace(match[1])
|
candidateVersion := strings.TrimSpace(match[1])
|
||||||
|
if !isLikelyVersionValue(candidateVersion) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result.Version = candidateVersion
|
||||||
if len(match) > 2 {
|
if len(match) > 2 {
|
||||||
result.GitCommit = strings.TrimSpace(match[2])
|
result.GitCommit = strings.TrimSpace(match[2])
|
||||||
}
|
}
|
||||||
|
|
@ -326,9 +298,45 @@ func parsePicoclawVersionOutput(raw string) (systemVersionResponse, bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return systemVersionResponse{}, false
|
||||||
|
}
|
||||||
|
|
||||||
if result.Version == "" {
|
if result.Version == "" {
|
||||||
return systemVersionResponse{}, false
|
return systemVersionResponse{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, true
|
return result, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isLikelyVersionValue(value string) bool {
|
||||||
|
v := strings.TrimSpace(strings.ToLower(value))
|
||||||
|
if v == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v == "dev" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept git-like short/long hashes even when they contain only letters (a-f).
|
||||||
|
if len(v) >= 7 && len(v) <= 40 {
|
||||||
|
allHex := true
|
||||||
|
for _, ch := range v {
|
||||||
|
if (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
allHex = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if allHex {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ch := range v {
|
||||||
|
if ch >= '0' && ch <= '9' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,51 +7,36 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sipeed/picoclaw/pkg/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupVersionTestIsolation(t *testing.T) {
|
func setupVersionTestIsolation(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
originalGatewayState := currentGatewayVersionState
|
originalGatewayState := currentGatewayVersionState
|
||||||
originalMonitorInterval := versionCacheMonitorInterval
|
originalFinder := findPicoclawBinaryForInfo
|
||||||
|
originalRunner := runPicoclawVersionOutput
|
||||||
|
originalFallback := launcherBuildInfoForVersion
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
currentGatewayVersionState = originalGatewayState
|
currentGatewayVersionState = originalGatewayState
|
||||||
versionCacheMonitorInterval = originalMonitorInterval
|
findPicoclawBinaryForInfo = originalFinder
|
||||||
|
runPicoclawVersionOutput = originalRunner
|
||||||
|
launcherBuildInfoForVersion = originalFallback
|
||||||
versionInfoCache.resetForTest()
|
versionInfoCache.resetForTest()
|
||||||
})
|
})
|
||||||
|
|
||||||
currentGatewayVersionState = func() (int, bool) { return 0, false }
|
currentGatewayVersionState = func() (int, bool) { return 0, false }
|
||||||
versionCacheMonitorInterval = 10 * time.Millisecond
|
|
||||||
versionInfoCache.resetForTest()
|
versionInfoCache.resetForTest()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSystemVersionUsesPicoclawBinaryInfo(t *testing.T) {
|
func TestGetSystemVersionUsesPicoclawBinaryInfo(t *testing.T) {
|
||||||
setupVersionTestIsolation(t)
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
originalVersion := config.Version
|
launcherBuildInfoForVersion = func() systemVersionResponse {
|
||||||
originalGitCommit := config.GitCommit
|
return systemVersionResponse{Version: "fallback", GoVersion: "go-fallback"}
|
||||||
originalBuildTime := config.BuildTime
|
}
|
||||||
originalGoVersion := config.GoVersion
|
|
||||||
originalFinder := findPicoclawBinaryForInfo
|
|
||||||
originalRunner := runPicoclawVersionOutput
|
|
||||||
t.Cleanup(func() {
|
|
||||||
config.Version = originalVersion
|
|
||||||
config.GitCommit = originalGitCommit
|
|
||||||
config.BuildTime = originalBuildTime
|
|
||||||
config.GoVersion = originalGoVersion
|
|
||||||
findPicoclawBinaryForInfo = originalFinder
|
|
||||||
runPicoclawVersionOutput = originalRunner
|
|
||||||
})
|
|
||||||
|
|
||||||
config.Version = "dev"
|
|
||||||
config.GitCommit = ""
|
|
||||||
config.BuildTime = ""
|
|
||||||
config.GoVersion = ""
|
|
||||||
|
|
||||||
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
||||||
runPicoclawVersionOutput = func(_ context.Context, _ string) (string, error) {
|
runPicoclawVersionOutput = func(_ context.Context, _ string) (string, error) {
|
||||||
|
|
@ -92,25 +77,13 @@ func TestGetSystemVersionUsesPicoclawBinaryInfo(t *testing.T) {
|
||||||
func TestGetSystemVersionFallsBackToLauncherInfoWhenCommandFails(t *testing.T) {
|
func TestGetSystemVersionFallsBackToLauncherInfoWhenCommandFails(t *testing.T) {
|
||||||
setupVersionTestIsolation(t)
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
originalVersion := config.Version
|
expected := systemVersionResponse{
|
||||||
originalGitCommit := config.GitCommit
|
Version: "v9.9.9",
|
||||||
originalBuildTime := config.BuildTime
|
GitCommit: "cafebabe",
|
||||||
originalGoVersion := config.GoVersion
|
BuildTime: "2026-03-27T10:43:34+0000",
|
||||||
originalFinder := findPicoclawBinaryForInfo
|
GoVersion: "go1.25.8",
|
||||||
originalRunner := runPicoclawVersionOutput
|
}
|
||||||
t.Cleanup(func() {
|
launcherBuildInfoForVersion = func() systemVersionResponse { return expected }
|
||||||
config.Version = originalVersion
|
|
||||||
config.GitCommit = originalGitCommit
|
|
||||||
config.BuildTime = originalBuildTime
|
|
||||||
config.GoVersion = originalGoVersion
|
|
||||||
findPicoclawBinaryForInfo = originalFinder
|
|
||||||
runPicoclawVersionOutput = originalRunner
|
|
||||||
})
|
|
||||||
|
|
||||||
config.Version = "v9.9.9"
|
|
||||||
config.GitCommit = "cafebabe"
|
|
||||||
config.BuildTime = "2026-03-27T10:43:34+0000"
|
|
||||||
config.GoVersion = "go1.25.8"
|
|
||||||
|
|
||||||
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
||||||
runPicoclawVersionOutput = func(_ context.Context, _ string) (string, error) {
|
runPicoclawVersionOutput = func(_ context.Context, _ string) (string, error) {
|
||||||
|
|
@ -134,17 +107,17 @@ func TestGetSystemVersionFallsBackToLauncherInfoWhenCommandFails(t *testing.T) {
|
||||||
t.Fatalf("unmarshal response: %v", err)
|
t.Fatalf("unmarshal response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if got.Version != config.Version {
|
if got.Version != expected.Version {
|
||||||
t.Fatalf("version = %q, want %q", got.Version, config.Version)
|
t.Fatalf("version = %q, want %q", got.Version, expected.Version)
|
||||||
}
|
}
|
||||||
if got.GitCommit != config.GitCommit {
|
if got.GitCommit != expected.GitCommit {
|
||||||
t.Fatalf("git_commit = %q, want %q", got.GitCommit, config.GitCommit)
|
t.Fatalf("git_commit = %q, want %q", got.GitCommit, expected.GitCommit)
|
||||||
}
|
}
|
||||||
if got.BuildTime != config.BuildTime {
|
if got.BuildTime != expected.BuildTime {
|
||||||
t.Fatalf("build_time = %q, want %q", got.BuildTime, config.BuildTime)
|
t.Fatalf("build_time = %q, want %q", got.BuildTime, expected.BuildTime)
|
||||||
}
|
}
|
||||||
if got.GoVersion != config.GoVersion {
|
if got.GoVersion != expected.GoVersion {
|
||||||
t.Fatalf("go_version = %q, want %q", got.GoVersion, config.GoVersion)
|
t.Fatalf("go_version = %q, want %q", got.GoVersion, expected.GoVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,28 +143,38 @@ func TestParsePicoclawVersionOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParsePicoclawVersionOutputIgnoresUsageLine(t *testing.T) {
|
||||||
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
|
raw := "Usage: picoclaw version [flags]\n"
|
||||||
|
got, ok := parsePicoclawVersionOutput(raw)
|
||||||
|
if ok {
|
||||||
|
t.Fatalf("parsePicoclawVersionOutput() parsed usage line unexpectedly: %#v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsePicoclawVersionOutputAcceptsLetterOnlyHashVersion(t *testing.T) {
|
||||||
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
|
raw := "picoclaw abcdefa (git: abcdefabcdefabcdefabcdefabcdefabcdefabcd)\n"
|
||||||
|
got, ok := parsePicoclawVersionOutput(raw)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("parsePicoclawVersionOutput() should parse letter-only hash version")
|
||||||
|
}
|
||||||
|
if got.Version != "abcdefa" {
|
||||||
|
t.Fatalf("version = %q, want %q", got.Version, "abcdefa")
|
||||||
|
}
|
||||||
|
if got.GitCommit != "abcdefabcdefabcdefabcdefabcdefabcdefabcd" {
|
||||||
|
t.Fatalf("git_commit = %q, want %q", got.GitCommit, "abcdefabcdefabcdefabcdefabcdefabcdefabcd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResolveSystemVersionInfoFallsBackRuntimeGoVersion(t *testing.T) {
|
func TestResolveSystemVersionInfoFallsBackRuntimeGoVersion(t *testing.T) {
|
||||||
setupVersionTestIsolation(t)
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
originalVersion := config.Version
|
launcherBuildInfoForVersion = func() systemVersionResponse {
|
||||||
originalGitCommit := config.GitCommit
|
return systemVersionResponse{Version: "dev", GoVersion: ""}
|
||||||
originalBuildTime := config.BuildTime
|
}
|
||||||
originalGoVersion := config.GoVersion
|
|
||||||
originalFinder := findPicoclawBinaryForInfo
|
|
||||||
originalRunner := runPicoclawVersionOutput
|
|
||||||
t.Cleanup(func() {
|
|
||||||
config.Version = originalVersion
|
|
||||||
config.GitCommit = originalGitCommit
|
|
||||||
config.BuildTime = originalBuildTime
|
|
||||||
config.GoVersion = originalGoVersion
|
|
||||||
findPicoclawBinaryForInfo = originalFinder
|
|
||||||
runPicoclawVersionOutput = originalRunner
|
|
||||||
})
|
|
||||||
|
|
||||||
config.Version = "dev"
|
|
||||||
config.GitCommit = ""
|
|
||||||
config.BuildTime = ""
|
|
||||||
config.GoVersion = ""
|
|
||||||
|
|
||||||
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
||||||
runPicoclawVersionOutput = func(_ context.Context, _ string) (string, error) {
|
runPicoclawVersionOutput = func(_ context.Context, _ string) (string, error) {
|
||||||
|
|
@ -208,18 +191,9 @@ func TestResolveSystemVersionInfoFallsBackRuntimeGoVersion(t *testing.T) {
|
||||||
func TestResolveSystemVersionInfoCachesWhileGatewayAlive(t *testing.T) {
|
func TestResolveSystemVersionInfoCachesWhileGatewayAlive(t *testing.T) {
|
||||||
setupVersionTestIsolation(t)
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
originalVersion := config.Version
|
launcherBuildInfoForVersion = func() systemVersionResponse {
|
||||||
originalFinder := findPicoclawBinaryForInfo
|
return systemVersionResponse{Version: "dev", GoVersion: "go-fallback"}
|
||||||
originalRunner := runPicoclawVersionOutput
|
}
|
||||||
originalGatewayState := currentGatewayVersionState
|
|
||||||
t.Cleanup(func() {
|
|
||||||
config.Version = originalVersion
|
|
||||||
findPicoclawBinaryForInfo = originalFinder
|
|
||||||
runPicoclawVersionOutput = originalRunner
|
|
||||||
currentGatewayVersionState = originalGatewayState
|
|
||||||
})
|
|
||||||
|
|
||||||
config.Version = "dev"
|
|
||||||
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
||||||
|
|
||||||
pid := 4321
|
pid := 4321
|
||||||
|
|
@ -249,18 +223,9 @@ func TestResolveSystemVersionInfoCachesWhileGatewayAlive(t *testing.T) {
|
||||||
func TestResolveSystemVersionInfoInvalidatesCacheWhenGatewayStops(t *testing.T) {
|
func TestResolveSystemVersionInfoInvalidatesCacheWhenGatewayStops(t *testing.T) {
|
||||||
setupVersionTestIsolation(t)
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
originalVersion := config.Version
|
launcherBuildInfoForVersion = func() systemVersionResponse {
|
||||||
originalFinder := findPicoclawBinaryForInfo
|
return systemVersionResponse{Version: "dev", GoVersion: "go-fallback"}
|
||||||
originalRunner := runPicoclawVersionOutput
|
}
|
||||||
originalGatewayState := currentGatewayVersionState
|
|
||||||
t.Cleanup(func() {
|
|
||||||
config.Version = originalVersion
|
|
||||||
findPicoclawBinaryForInfo = originalFinder
|
|
||||||
runPicoclawVersionOutput = originalRunner
|
|
||||||
currentGatewayVersionState = originalGatewayState
|
|
||||||
})
|
|
||||||
|
|
||||||
config.Version = "dev"
|
|
||||||
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
||||||
|
|
||||||
alive := true
|
alive := true
|
||||||
|
|
@ -302,16 +267,9 @@ func TestResolveSystemVersionInfoInvalidatesCacheWhenGatewayStops(t *testing.T)
|
||||||
func TestResolveSystemVersionInfoSkipsCommandWhenContextCanceled(t *testing.T) {
|
func TestResolveSystemVersionInfoSkipsCommandWhenContextCanceled(t *testing.T) {
|
||||||
setupVersionTestIsolation(t)
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
originalVersion := config.Version
|
launcherBuildInfoForVersion = func() systemVersionResponse {
|
||||||
originalFinder := findPicoclawBinaryForInfo
|
return systemVersionResponse{Version: "v3.0.0", GoVersion: "go-fallback"}
|
||||||
originalRunner := runPicoclawVersionOutput
|
}
|
||||||
t.Cleanup(func() {
|
|
||||||
config.Version = originalVersion
|
|
||||||
findPicoclawBinaryForInfo = originalFinder
|
|
||||||
runPicoclawVersionOutput = originalRunner
|
|
||||||
})
|
|
||||||
|
|
||||||
config.Version = "v3.0.0"
|
|
||||||
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
findPicoclawBinaryForInfo = func() string { return "picoclaw" }
|
||||||
|
|
||||||
runCount := 0
|
runCount := 0
|
||||||
|
|
@ -333,3 +291,27 @@ func TestResolveSystemVersionInfoSkipsCommandWhenContextCanceled(t *testing.T) {
|
||||||
t.Fatalf("version = %q, want fallback %q", got.Version, "v3.0.0")
|
t.Fatalf("version = %q, want fallback %q", got.Version, "v3.0.0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveGatewayBinaryForVersionInfoPrefersGatewayCommandPath(t *testing.T) {
|
||||||
|
setupVersionTestIsolation(t)
|
||||||
|
|
||||||
|
originalFinder := findPicoclawBinaryForInfo
|
||||||
|
t.Cleanup(func() {
|
||||||
|
findPicoclawBinaryForInfo = originalFinder
|
||||||
|
})
|
||||||
|
|
||||||
|
gateway.mu.Lock()
|
||||||
|
originalCmd := gateway.cmd
|
||||||
|
gateway.cmd = &exec.Cmd{Path: "/tmp/picoclaw-from-gateway"}
|
||||||
|
gateway.mu.Unlock()
|
||||||
|
t.Cleanup(func() {
|
||||||
|
gateway.mu.Lock()
|
||||||
|
gateway.cmd = originalCmd
|
||||||
|
gateway.mu.Unlock()
|
||||||
|
})
|
||||||
|
|
||||||
|
got := resolveGatewayBinaryForVersionInfo()
|
||||||
|
if got != "/tmp/picoclaw-from-gateway" {
|
||||||
|
t.Fatalf("exec path = %q, want %q", got, "/tmp/picoclaw-from-gateway")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue