test: Add test coverage for 16 files (40.3% → 45.5%) (#463)
* test: Add test coverage for 16 files (40.3% -> 45.5%) Add comprehensive tests for previously untested packages: - internal/agent/state_test.go - internal/cmd/errors_test.go - internal/crew/types_test.go - internal/doctor/errors_test.go - internal/dog/types_test.go - internal/mail/bd_test.go - internal/opencode/plugin_test.go - internal/rig/overlay_test.go - internal/runtime/runtime_test.go - internal/session/town_test.go - internal/style/style_test.go - internal/ui/markdown_test.go - internal/ui/terminal_test.go - internal/wisp/io_test.go - internal/wisp/types_test.go - internal/witness/types_test.go style_test.go uses func(...string) to match lipgloss variadic Render signature. * fix(lint): remove unused error return from buildCVSummary buildCVSummary always returned nil for its error value, causing golangci-lint to fail with "result 1 (error) is always nil". The function handles errors internally by returning partial data, so the error return was misleading. Removed it and updated caller.
This commit is contained in:
307
internal/runtime/runtime_test.go
Normal file
307
internal/runtime/runtime_test.go
Normal file
@@ -0,0 +1,307 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
)
|
||||
|
||||
func TestSessionIDFromEnv_Default(t *testing.T) {
|
||||
// Clear all environment variables
|
||||
oldGSEnv := os.Getenv("GT_SESSION_ID_ENV")
|
||||
oldClaudeID := os.Getenv("CLAUDE_SESSION_ID")
|
||||
defer func() {
|
||||
if oldGSEnv != "" {
|
||||
os.Setenv("GT_SESSION_ID_ENV", oldGSEnv)
|
||||
} else {
|
||||
os.Unsetenv("GT_SESSION_ID_ENV")
|
||||
}
|
||||
if oldClaudeID != "" {
|
||||
os.Setenv("CLAUDE_SESSION_ID", oldClaudeID)
|
||||
} else {
|
||||
os.Unsetenv("CLAUDE_SESSION_ID")
|
||||
}
|
||||
}()
|
||||
os.Unsetenv("GT_SESSION_ID_ENV")
|
||||
os.Unsetenv("CLAUDE_SESSION_ID")
|
||||
|
||||
result := SessionIDFromEnv()
|
||||
if result != "" {
|
||||
t.Errorf("SessionIDFromEnv() with no env vars should return empty, got %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionIDFromEnv_ClaudeSessionID(t *testing.T) {
|
||||
oldGSEnv := os.Getenv("GT_SESSION_ID_ENV")
|
||||
oldClaudeID := os.Getenv("CLAUDE_SESSION_ID")
|
||||
defer func() {
|
||||
if oldGSEnv != "" {
|
||||
os.Setenv("GT_SESSION_ID_ENV", oldGSEnv)
|
||||
} else {
|
||||
os.Unsetenv("GT_SESSION_ID_ENV")
|
||||
}
|
||||
if oldClaudeID != "" {
|
||||
os.Setenv("CLAUDE_SESSION_ID", oldClaudeID)
|
||||
} else {
|
||||
os.Unsetenv("CLAUDE_SESSION_ID")
|
||||
}
|
||||
}()
|
||||
|
||||
os.Unsetenv("GT_SESSION_ID_ENV")
|
||||
os.Setenv("CLAUDE_SESSION_ID", "test-session-123")
|
||||
|
||||
result := SessionIDFromEnv()
|
||||
if result != "test-session-123" {
|
||||
t.Errorf("SessionIDFromEnv() = %q, want %q", result, "test-session-123")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionIDFromEnv_CustomEnvVar(t *testing.T) {
|
||||
oldGSEnv := os.Getenv("GT_SESSION_ID_ENV")
|
||||
oldCustomID := os.Getenv("CUSTOM_SESSION_ID")
|
||||
oldClaudeID := os.Getenv("CLAUDE_SESSION_ID")
|
||||
defer func() {
|
||||
if oldGSEnv != "" {
|
||||
os.Setenv("GT_SESSION_ID_ENV", oldGSEnv)
|
||||
} else {
|
||||
os.Unsetenv("GT_SESSION_ID_ENV")
|
||||
}
|
||||
if oldCustomID != "" {
|
||||
os.Setenv("CUSTOM_SESSION_ID", oldCustomID)
|
||||
} else {
|
||||
os.Unsetenv("CUSTOM_SESSION_ID")
|
||||
}
|
||||
if oldClaudeID != "" {
|
||||
os.Setenv("CLAUDE_SESSION_ID", oldClaudeID)
|
||||
} else {
|
||||
os.Unsetenv("CLAUDE_SESSION_ID")
|
||||
}
|
||||
}()
|
||||
|
||||
os.Setenv("GT_SESSION_ID_ENV", "CUSTOM_SESSION_ID")
|
||||
os.Setenv("CUSTOM_SESSION_ID", "custom-session-456")
|
||||
os.Setenv("CLAUDE_SESSION_ID", "claude-session-789")
|
||||
|
||||
result := SessionIDFromEnv()
|
||||
if result != "custom-session-456" {
|
||||
t.Errorf("SessionIDFromEnv() with custom env = %q, want %q", result, "custom-session-456")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSleepForReadyDelay_NilConfig(t *testing.T) {
|
||||
// Should not panic with nil config
|
||||
SleepForReadyDelay(nil)
|
||||
}
|
||||
|
||||
func TestSleepForReadyDelay_ZeroDelay(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Tmux: &config.RuntimeTmuxConfig{
|
||||
ReadyDelayMs: 0,
|
||||
},
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
SleepForReadyDelay(rc)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
// Should return immediately
|
||||
if elapsed > 100*time.Millisecond {
|
||||
t.Errorf("SleepForReadyDelay() with zero delay took too long: %v", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSleepForReadyDelay_WithDelay(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Tmux: &config.RuntimeTmuxConfig{
|
||||
ReadyDelayMs: 10, // 10ms delay
|
||||
},
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
SleepForReadyDelay(rc)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
// Should sleep for at least 10ms
|
||||
if elapsed < 10*time.Millisecond {
|
||||
t.Errorf("SleepForReadyDelay() should sleep for at least 10ms, took %v", elapsed)
|
||||
}
|
||||
// But not too long
|
||||
if elapsed > 50*time.Millisecond {
|
||||
t.Errorf("SleepForReadyDelay() slept too long: %v", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSleepForReadyDelay_NilTmuxConfig(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Tmux: nil,
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
SleepForReadyDelay(rc)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
// Should return immediately
|
||||
if elapsed > 100*time.Millisecond {
|
||||
t.Errorf("SleepForReadyDelay() with nil Tmux config took too long: %v", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartupFallbackCommands_NoHooks(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Hooks: &config.RuntimeHooksConfig{
|
||||
Provider: "none",
|
||||
},
|
||||
}
|
||||
|
||||
commands := StartupFallbackCommands("polecat", rc)
|
||||
if commands == nil {
|
||||
t.Error("StartupFallbackCommands() with no hooks should return commands")
|
||||
}
|
||||
if len(commands) == 0 {
|
||||
t.Error("StartupFallbackCommands() should return at least one command")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartupFallbackCommands_WithHooks(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Hooks: &config.RuntimeHooksConfig{
|
||||
Provider: "claude",
|
||||
},
|
||||
}
|
||||
|
||||
commands := StartupFallbackCommands("polecat", rc)
|
||||
if commands != nil {
|
||||
t.Error("StartupFallbackCommands() with hooks provider should return nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartupFallbackCommands_NilConfig(t *testing.T) {
|
||||
// Nil config defaults to claude provider, which has hooks
|
||||
// So it returns nil (no fallback commands needed)
|
||||
commands := StartupFallbackCommands("polecat", nil)
|
||||
if commands != nil {
|
||||
t.Error("StartupFallbackCommands() with nil config should return nil (defaults to claude with hooks)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartupFallbackCommands_AutonomousRole(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Hooks: &config.RuntimeHooksConfig{
|
||||
Provider: "none",
|
||||
},
|
||||
}
|
||||
|
||||
autonomousRoles := []string{"polecat", "witness", "refinery", "deacon"}
|
||||
for _, role := range autonomousRoles {
|
||||
t.Run(role, func(t *testing.T) {
|
||||
commands := StartupFallbackCommands(role, rc)
|
||||
if commands == nil || len(commands) == 0 {
|
||||
t.Error("StartupFallbackCommands() should return commands for autonomous role")
|
||||
}
|
||||
// Should contain mail check
|
||||
found := false
|
||||
for _, cmd := range commands {
|
||||
if contains(cmd, "mail check --inject") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Commands for %s should contain mail check --inject", role)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartupFallbackCommands_NonAutonomousRole(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Hooks: &config.RuntimeHooksConfig{
|
||||
Provider: "none",
|
||||
},
|
||||
}
|
||||
|
||||
nonAutonomousRoles := []string{"mayor", "crew", "keeper"}
|
||||
for _, role := range nonAutonomousRoles {
|
||||
t.Run(role, func(t *testing.T) {
|
||||
commands := StartupFallbackCommands(role, rc)
|
||||
if commands == nil || len(commands) == 0 {
|
||||
t.Error("StartupFallbackCommands() should return commands for non-autonomous role")
|
||||
}
|
||||
// Should NOT contain mail check
|
||||
for _, cmd := range commands {
|
||||
if contains(cmd, "mail check --inject") {
|
||||
t.Errorf("Commands for %s should NOT contain mail check --inject", role)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartupFallbackCommands_RoleCasing(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Hooks: &config.RuntimeHooksConfig{
|
||||
Provider: "none",
|
||||
},
|
||||
}
|
||||
|
||||
// Role should be lowercased internally
|
||||
commands := StartupFallbackCommands("POLECAT", rc)
|
||||
if commands == nil {
|
||||
t.Error("StartupFallbackCommands() should handle uppercase role")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureSettingsForRole_NilConfig(t *testing.T) {
|
||||
// Should not panic with nil config
|
||||
err := EnsureSettingsForRole("/tmp/test", "polecat", nil)
|
||||
if err != nil {
|
||||
t.Errorf("EnsureSettingsForRole() with nil config should not error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureSettingsForRole_NilHooks(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Hooks: nil,
|
||||
}
|
||||
|
||||
err := EnsureSettingsForRole("/tmp/test", "polecat", rc)
|
||||
if err != nil {
|
||||
t.Errorf("EnsureSettingsForRole() with nil hooks should not error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureSettingsForRole_UnknownProvider(t *testing.T) {
|
||||
rc := &config.RuntimeConfig{
|
||||
Hooks: &config.RuntimeHooksConfig{
|
||||
Provider: "unknown",
|
||||
},
|
||||
}
|
||||
|
||||
err := EnsureSettingsForRole("/tmp/test", "polecat", rc)
|
||||
if err != nil {
|
||||
t.Errorf("EnsureSettingsForRole() with unknown provider should not error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function
|
||||
func contains(s, substr string) bool {
|
||||
return len(s) >= len(substr) && findSubstring(s, substr)
|
||||
}
|
||||
|
||||
func findSubstring(s, substr string) bool {
|
||||
for i := 0; i <= len(s)-len(substr); i++ {
|
||||
match := true
|
||||
for j := 0; j < len(substr); j++ {
|
||||
if s[i+j] != substr[j] {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user