The env-vars check was using AgentEnvSimple which doesn't know the actual TownRoot and BeadsDir paths. This could cause false positive mismatches when comparing expected (empty paths) vs actual (real paths). - Use config.AgentEnv with proper TownRoot and BeadsDir from CheckContext - Rig-level roles resolve beads dir from rig path - Update tests to use expectedEnv helper that generates full env vars Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
351 lines
9.1 KiB
Go
351 lines
9.1 KiB
Go
package doctor
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/steveyegge/gastown/internal/config"
|
|
)
|
|
|
|
// mockEnvReader implements SessionEnvReader for testing.
|
|
type mockEnvReader struct {
|
|
sessions []string
|
|
sessionEnvs map[string]map[string]string
|
|
listErr error
|
|
envErrs map[string]error
|
|
}
|
|
|
|
func (m *mockEnvReader) ListSessions() ([]string, error) {
|
|
if m.listErr != nil {
|
|
return nil, m.listErr
|
|
}
|
|
return m.sessions, nil
|
|
}
|
|
|
|
func (m *mockEnvReader) GetAllEnvironment(session string) (map[string]string, error) {
|
|
if m.envErrs != nil {
|
|
if err, ok := m.envErrs[session]; ok {
|
|
return nil, err
|
|
}
|
|
}
|
|
if m.sessionEnvs != nil {
|
|
if env, ok := m.sessionEnvs[session]; ok {
|
|
return env, nil
|
|
}
|
|
}
|
|
return map[string]string{}, nil
|
|
}
|
|
|
|
// testTownRoot is the town root used in tests.
|
|
// Tests use this fixed path so expected values match what the check generates.
|
|
const testTownRoot = "/town"
|
|
|
|
// expectedEnv generates expected env vars matching what the check generates.
|
|
// For town-level roles (mayor, deacon), beadsDir is /town/.beads
|
|
// For rig-level roles, beadsDir is /town/rigName/.beads
|
|
func expectedEnv(role, rig, agentName string) map[string]string {
|
|
var beadsDir string
|
|
if rig != "" {
|
|
beadsDir = testTownRoot + "/" + rig + "/.beads"
|
|
} else {
|
|
beadsDir = testTownRoot + "/.beads"
|
|
}
|
|
return config.AgentEnv(config.AgentEnvConfig{
|
|
Role: role,
|
|
Rig: rig,
|
|
AgentName: agentName,
|
|
TownRoot: testTownRoot,
|
|
BeadsDir: beadsDir,
|
|
})
|
|
}
|
|
|
|
// testCtx returns a CheckContext with the test town root.
|
|
func testCtx() *CheckContext {
|
|
return &CheckContext{TownRoot: testTownRoot}
|
|
}
|
|
|
|
func TestEnvVarsCheck_NoSessions(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
sessions: []string{},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
if result.Message != "No Gas Town sessions running" {
|
|
t.Errorf("Message = %q, want %q", result.Message, "No Gas Town sessions running")
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_ListSessionsError(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
listErr: errors.New("tmux not running"),
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
// No tmux server is valid (Gas Town can be down)
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
if result.Message != "No tmux sessions running" {
|
|
t.Errorf("Message = %q, want %q", result.Message, "No tmux sessions running")
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_NonGasTownSessions(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"other-session", "my-dev"},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
if result.Message != "No Gas Town sessions running" {
|
|
t.Errorf("Message = %q, want %q", result.Message, "No Gas Town sessions running")
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_MayorCorrect(t *testing.T) {
|
|
expected := expectedEnv("mayor", "", "")
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"hq-mayor"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"hq-mayor": expected,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_MayorMissing(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"hq-mayor"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"hq-mayor": {}, // Missing all env vars
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusWarning {
|
|
t.Errorf("Status = %v, want StatusWarning", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_WitnessCorrect(t *testing.T) {
|
|
expected := expectedEnv("witness", "myrig", "")
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-myrig-witness"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"gt-myrig-witness": expected,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_WitnessMismatch(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-myrig-witness"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"gt-myrig-witness": {
|
|
"GT_ROLE": "witness",
|
|
"GT_RIG": "wrongrig", // Wrong rig
|
|
},
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusWarning {
|
|
t.Errorf("Status = %v, want StatusWarning", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_RefineryCorrect(t *testing.T) {
|
|
expected := expectedEnv("refinery", "myrig", "")
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-myrig-refinery"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"gt-myrig-refinery": expected,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_PolecatCorrect(t *testing.T) {
|
|
expected := expectedEnv("polecat", "myrig", "Toast")
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-myrig-Toast"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"gt-myrig-Toast": expected,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_PolecatMissing(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-myrig-Toast"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"gt-myrig-Toast": {
|
|
"GT_ROLE": "polecat",
|
|
// Missing GT_RIG, GT_POLECAT, BD_ACTOR, GIT_AUTHOR_NAME
|
|
},
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusWarning {
|
|
t.Errorf("Status = %v, want StatusWarning", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_CrewCorrect(t *testing.T) {
|
|
expected := expectedEnv("crew", "myrig", "worker1")
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-myrig-crew-worker1"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"gt-myrig-crew-worker1": expected,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_MultipleSessions(t *testing.T) {
|
|
mayorEnv := expectedEnv("mayor", "", "")
|
|
witnessEnv := expectedEnv("witness", "rig1", "")
|
|
polecatEnv := expectedEnv("polecat", "rig1", "Toast")
|
|
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"hq-mayor", "gt-rig1-witness", "gt-rig1-Toast"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"hq-mayor": mayorEnv,
|
|
"gt-rig1-witness": witnessEnv,
|
|
"gt-rig1-Toast": polecatEnv,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
if result.Message != "All 3 session(s) have correct environment variables" {
|
|
t.Errorf("Message = %q", result.Message)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_MixedCorrectAndMismatch(t *testing.T) {
|
|
mayorEnv := expectedEnv("mayor", "", "")
|
|
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"hq-mayor", "gt-rig1-witness"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"hq-mayor": mayorEnv,
|
|
"gt-rig1-witness": {
|
|
"GT_ROLE": "witness",
|
|
// Missing GT_RIG and other vars
|
|
},
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusWarning {
|
|
t.Errorf("Status = %v, want StatusWarning", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_DeaconCorrect(t *testing.T) {
|
|
expected := expectedEnv("deacon", "", "")
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"hq-deacon"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"hq-deacon": expected,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_DeaconMissing(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"hq-deacon"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"hq-deacon": {}, // Missing all env vars
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusWarning {
|
|
t.Errorf("Status = %v, want StatusWarning", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_GetEnvError(t *testing.T) {
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-myrig-witness"},
|
|
envErrs: map[string]error{
|
|
"gt-myrig-witness": errors.New("session not found"),
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusWarning {
|
|
t.Errorf("Status = %v, want StatusWarning", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnvVarsCheck_HyphenatedRig(t *testing.T) {
|
|
// Test rig name with hyphens: "foo-bar"
|
|
expected := expectedEnv("witness", "foo-bar", "")
|
|
reader := &mockEnvReader{
|
|
sessions: []string{"gt-foo-bar-witness"},
|
|
sessionEnvs: map[string]map[string]string{
|
|
"gt-foo-bar-witness": expected,
|
|
},
|
|
}
|
|
check := NewEnvVarsCheckWithReader(reader)
|
|
result := check.Run(testCtx())
|
|
|
|
if result.Status != StatusOK {
|
|
t.Errorf("Status = %v, want StatusOK", result.Status)
|
|
}
|
|
}
|