test: improve coverage for config and git packages
- config: Add tests for GetStringSlice, GetMultiRepoConfig, and nil viper behavior. Coverage improved from 65.3% to 84.0%. - git: Add tests for error paths in RemoveBeadsWorktree, SyncJSONLToWorktree, CheckWorktreeHealth, and sparse checkout functions. Coverage improved from 72.9% to 82.7%. Closes: bd-t3b, bd-4h3, bd-ge7 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -231,16 +231,208 @@ func TestAllSettings(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Initialize() returned error: %v", err)
|
||||
}
|
||||
|
||||
|
||||
Set("custom-key", "custom-value")
|
||||
|
||||
|
||||
settings := AllSettings()
|
||||
if settings == nil {
|
||||
t.Fatal("AllSettings() returned nil")
|
||||
}
|
||||
|
||||
|
||||
// Check that our custom key is in the settings
|
||||
if val, ok := settings["custom-key"]; !ok || val != "custom-value" {
|
||||
t.Errorf("AllSettings() missing or incorrect custom-key: got %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStringSlice(t *testing.T) {
|
||||
err := Initialize()
|
||||
if err != nil {
|
||||
t.Fatalf("Initialize() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Test with Set
|
||||
Set("test-slice", []string{"a", "b", "c"})
|
||||
got := GetStringSlice("test-slice")
|
||||
if len(got) != 3 || got[0] != "a" || got[1] != "b" || got[2] != "c" {
|
||||
t.Errorf("GetStringSlice(test-slice) = %v, want [a b c]", got)
|
||||
}
|
||||
|
||||
// Test with non-existent key - should return empty/nil slice
|
||||
got = GetStringSlice("nonexistent-key")
|
||||
if len(got) != 0 {
|
||||
t.Errorf("GetStringSlice(nonexistent-key) = %v, want empty slice", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStringSliceFromConfig(t *testing.T) {
|
||||
// Create a temporary directory for config file
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create a config file with string slice
|
||||
configContent := `
|
||||
repos:
|
||||
primary: /path/to/primary
|
||||
additional:
|
||||
- /path/to/repo1
|
||||
- /path/to/repo2
|
||||
`
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0750); err != nil {
|
||||
t.Fatalf("failed to create .beads directory: %v", err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(beadsDir, "config.yaml")
|
||||
if err := os.WriteFile(configPath, []byte(configContent), 0600); err != nil {
|
||||
t.Fatalf("failed to write config file: %v", err)
|
||||
}
|
||||
|
||||
// Change to tmp directory
|
||||
origDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get working directory: %v", err)
|
||||
}
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
if err := os.Chdir(tmpDir); err != nil {
|
||||
t.Fatalf("failed to change directory: %v", err)
|
||||
}
|
||||
|
||||
// Initialize viper
|
||||
err = Initialize()
|
||||
if err != nil {
|
||||
t.Fatalf("Initialize() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Test that string slice is loaded correctly
|
||||
got := GetStringSlice("repos.additional")
|
||||
if len(got) != 2 || got[0] != "/path/to/repo1" || got[1] != "/path/to/repo2" {
|
||||
t.Errorf("GetStringSlice(repos.additional) = %v, want [/path/to/repo1 /path/to/repo2]", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMultiRepoConfig(t *testing.T) {
|
||||
err := Initialize()
|
||||
if err != nil {
|
||||
t.Fatalf("Initialize() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Test when repos.primary is not set (single-repo mode)
|
||||
config := GetMultiRepoConfig()
|
||||
if config != nil {
|
||||
t.Errorf("GetMultiRepoConfig() with no repos.primary = %+v, want nil", config)
|
||||
}
|
||||
|
||||
// Test when repos.primary is set (multi-repo mode)
|
||||
Set("repos.primary", "/path/to/primary")
|
||||
Set("repos.additional", []string{"/path/to/repo1", "/path/to/repo2"})
|
||||
|
||||
config = GetMultiRepoConfig()
|
||||
if config == nil {
|
||||
t.Fatal("GetMultiRepoConfig() returned nil when repos.primary is set")
|
||||
}
|
||||
|
||||
if config.Primary != "/path/to/primary" {
|
||||
t.Errorf("GetMultiRepoConfig().Primary = %q, want \"/path/to/primary\"", config.Primary)
|
||||
}
|
||||
|
||||
if len(config.Additional) != 2 || config.Additional[0] != "/path/to/repo1" || config.Additional[1] != "/path/to/repo2" {
|
||||
t.Errorf("GetMultiRepoConfig().Additional = %v, want [/path/to/repo1 /path/to/repo2]", config.Additional)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMultiRepoConfigFromFile(t *testing.T) {
|
||||
// Create a temporary directory for config file
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create a config file with multi-repo config
|
||||
configContent := `
|
||||
repos:
|
||||
primary: /main/repo
|
||||
additional:
|
||||
- /extra/repo1
|
||||
- /extra/repo2
|
||||
- /extra/repo3
|
||||
`
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0750); err != nil {
|
||||
t.Fatalf("failed to create .beads directory: %v", err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(beadsDir, "config.yaml")
|
||||
if err := os.WriteFile(configPath, []byte(configContent), 0600); err != nil {
|
||||
t.Fatalf("failed to write config file: %v", err)
|
||||
}
|
||||
|
||||
// Change to tmp directory
|
||||
origDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get working directory: %v", err)
|
||||
}
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
if err := os.Chdir(tmpDir); err != nil {
|
||||
t.Fatalf("failed to change directory: %v", err)
|
||||
}
|
||||
|
||||
// Initialize viper
|
||||
err = Initialize()
|
||||
if err != nil {
|
||||
t.Fatalf("Initialize() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Test that multi-repo config is loaded correctly
|
||||
config := GetMultiRepoConfig()
|
||||
if config == nil {
|
||||
t.Fatal("GetMultiRepoConfig() returned nil")
|
||||
}
|
||||
|
||||
if config.Primary != "/main/repo" {
|
||||
t.Errorf("GetMultiRepoConfig().Primary = %q, want \"/main/repo\"", config.Primary)
|
||||
}
|
||||
|
||||
if len(config.Additional) != 3 {
|
||||
t.Errorf("GetMultiRepoConfig().Additional has %d items, want 3", len(config.Additional))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilViperBehavior(t *testing.T) {
|
||||
// Save the current viper instance
|
||||
savedV := v
|
||||
|
||||
// Set viper to nil to test nil-safety
|
||||
v = nil
|
||||
defer func() { v = savedV }()
|
||||
|
||||
// All getters should return zero values without panicking
|
||||
if got := GetString("any-key"); got != "" {
|
||||
t.Errorf("GetString with nil viper = %q, want \"\"", got)
|
||||
}
|
||||
|
||||
if got := GetBool("any-key"); got != false {
|
||||
t.Errorf("GetBool with nil viper = %v, want false", got)
|
||||
}
|
||||
|
||||
if got := GetInt("any-key"); got != 0 {
|
||||
t.Errorf("GetInt with nil viper = %d, want 0", got)
|
||||
}
|
||||
|
||||
if got := GetDuration("any-key"); got != 0 {
|
||||
t.Errorf("GetDuration with nil viper = %v, want 0", got)
|
||||
}
|
||||
|
||||
if got := GetStringSlice("any-key"); got == nil || len(got) != 0 {
|
||||
t.Errorf("GetStringSlice with nil viper = %v, want empty slice", got)
|
||||
}
|
||||
|
||||
if got := AllSettings(); got == nil || len(got) != 0 {
|
||||
t.Errorf("AllSettings with nil viper = %v, want empty map", got)
|
||||
}
|
||||
|
||||
if got := GetMultiRepoConfig(); got != nil {
|
||||
t.Errorf("GetMultiRepoConfig with nil viper = %+v, want nil", got)
|
||||
}
|
||||
|
||||
// Set should not panic
|
||||
Set("any-key", "any-value") // Should be a no-op
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user