package rig import ( "encoding/json" "os" "path/filepath" "testing" "github.com/steveyegge/gastown/internal/wisp" ) func TestGetConfig_SystemDefaults(t *testing.T) { // Create a temp rig with no wisp or bead config tmpDir := t.TempDir() rigPath := filepath.Join(tmpDir, "testrig") if err := os.MkdirAll(rigPath, 0755); err != nil { t.Fatal(err) } rig := &Rig{ Name: "testrig", Path: rigPath, } // Should get system defaults result := rig.GetConfigWithSource("status") if result.Source != SourceSystem { t.Errorf("expected source SourceSystem, got %s", result.Source) } if result.Value != "operational" { t.Errorf("expected value 'operational', got %v", result.Value) } // Test boolean default if !rig.GetBoolConfig("auto_restart") { t.Error("expected auto_restart to be true by default") } // Test int default maxPolecats := rig.GetIntConfig("max_polecats") if maxPolecats != 10 { t.Errorf("expected max_polecats=10, got %d", maxPolecats) } } func TestGetConfig_WispOverride(t *testing.T) { tmpDir := t.TempDir() rigPath := filepath.Join(tmpDir, "testrig") if err := os.MkdirAll(rigPath, 0755); err != nil { t.Fatal(err) } rig := &Rig{ Name: "testrig", Path: rigPath, } // Create wisp config with override wispCfg := wisp.NewConfig(tmpDir, "testrig") if err := wispCfg.Set("status", "parked"); err != nil { t.Fatal(err) } // Should get wisp value result := rig.GetConfigWithSource("status") if result.Source != SourceWisp { t.Errorf("expected source SourceWisp, got %s", result.Source) } if result.Value != "parked" { t.Errorf("expected value 'parked', got %v", result.Value) } } func TestGetConfig_WispBlocked(t *testing.T) { tmpDir := t.TempDir() rigPath := filepath.Join(tmpDir, "testrig") if err := os.MkdirAll(rigPath, 0755); err != nil { t.Fatal(err) } rig := &Rig{ Name: "testrig", Path: rigPath, } // Block auto_restart at wisp layer wispCfg := wisp.NewConfig(tmpDir, "testrig") if err := wispCfg.Block("auto_restart"); err != nil { t.Fatal(err) } // Should return nil (blocked) result := rig.GetConfigWithSource("auto_restart") if result.Source != SourceBlocked { t.Errorf("expected source SourceBlocked, got %s", result.Source) } if result.Value != nil { t.Errorf("expected nil value for blocked key, got %v", result.Value) } // Bool getter should return false for blocked if rig.GetBoolConfig("auto_restart") { t.Error("expected auto_restart to be false when blocked") } } func TestGetIntConfig_Stacking(t *testing.T) { tmpDir := t.TempDir() rigPath := filepath.Join(tmpDir, "testrig") if err := os.MkdirAll(rigPath, 0755); err != nil { t.Fatal(err) } rig := &Rig{ Name: "testrig", Path: rigPath, } // Set wisp adjustment wispCfg := wisp.NewConfig(tmpDir, "testrig") if err := wispCfg.Set("priority_adjustment", 5); err != nil { t.Fatal(err) } // priority_adjustment uses stacking: base (0) + wisp (5) = 5 result := rig.GetIntConfig("priority_adjustment") if result != 5 { t.Errorf("expected priority_adjustment=5, got %d", result) } } func TestGetBoolConfig_StringConversion(t *testing.T) { tmpDir := t.TempDir() rigPath := filepath.Join(tmpDir, "testrig") if err := os.MkdirAll(rigPath, 0755); err != nil { t.Fatal(err) } rig := &Rig{ Name: "testrig", Path: rigPath, } // Set string "true" in wisp wispCfg := wisp.NewConfig(tmpDir, "testrig") if err := wispCfg.Set("custom_bool", "true"); err != nil { t.Fatal(err) } if !rig.GetBoolConfig("custom_bool") { t.Error("expected 'true' string to convert to bool true") } // Set string "false" if err := wispCfg.Set("custom_bool", "false"); err != nil { t.Fatal(err) } if rig.GetBoolConfig("custom_bool") { t.Error("expected 'false' string to convert to bool false") } } func TestGetConfig_UnknownKey(t *testing.T) { tmpDir := t.TempDir() rigPath := filepath.Join(tmpDir, "testrig") if err := os.MkdirAll(rigPath, 0755); err != nil { t.Fatal(err) } rig := &Rig{ Name: "testrig", Path: rigPath, } result := rig.GetConfigWithSource("nonexistent_key") if result.Source != SourceNone { t.Errorf("expected source SourceNone, got %s", result.Source) } if result.Value != nil { t.Errorf("expected nil value for unknown key, got %v", result.Value) } } func TestGetStringConfig(t *testing.T) { tmpDir := t.TempDir() rigPath := filepath.Join(tmpDir, "testrig") if err := os.MkdirAll(rigPath, 0755); err != nil { t.Fatal(err) } rig := &Rig{ Name: "testrig", Path: rigPath, } // System default for status status := rig.GetStringConfig("status") if status != "operational" { t.Errorf("expected status='operational', got %s", status) } // Unknown key unknown := rig.GetStringConfig("nonexistent") if unknown != "" { t.Errorf("expected empty string for unknown key, got %s", unknown) } } func TestToInt(t *testing.T) { tests := []struct { input interface{} expected int }{ {nil, 0}, {0, 0}, {42, 42}, {int64(100), 100}, {float64(3.14), 3}, {"123", 123}, {"abc", 0}, {true, 0}, // bools don't convert to int } for _, tc := range tests { result := toInt(tc.input) if result != tc.expected { t.Errorf("toInt(%v) = %d, expected %d", tc.input, result, tc.expected) } } } // TestGetConfig_BeadLabel tests reading config from rig bead labels. // This requires a more complex setup with a full beads database. func TestGetConfig_BeadLabel(t *testing.T) { tmpDir := t.TempDir() townDir := tmpDir rigPath := filepath.Join(townDir, "testrig") beadsDir := filepath.Join(rigPath, ".beads") // Create directory structure if err := os.MkdirAll(beadsDir, 0755); err != nil { t.Fatal(err) } // Create a minimal issues.jsonl with a rig identity bead issuesPath := filepath.Join(beadsDir, "issues.jsonl") rigBead := map[string]interface{}{ "id": "gt-rig-testrig", "type": "rig", "title": "testrig", "status": "open", "labels": []string{"status:docked", "priority:high"}, } data, _ := json.Marshal(rigBead) if err := os.WriteFile(issuesPath, data, 0644); err != nil { t.Fatal(err) } // Note: This test demonstrates the structure but bd Show requires // a proper beads database. In production, use bd commands or mocks. // For now, we test that getBeadLabel returns nil gracefully when // beads is not fully set up. rig := &Rig{ Name: "testrig", Path: rigPath, } // Without full beads setup, should fall back to system defaults result := rig.GetConfigWithSource("status") // Either SourceBead (if beads is set up) or SourceSystem if result.Source != SourceBead && result.Source != SourceSystem { t.Logf("source is %s (expected SourceBead or SourceSystem)", result.Source) } }