Files
wild-central/internal/config/config_test.go

269 lines
6.3 KiB
Go

package config
import (
"os"
"path/filepath"
"strings"
"testing"
)
// Test: LoadState loads valid state
func TestLoadState(t *testing.T) {
tests := []struct {
name string
stateYAML string
verify func(t *testing.T, config *State)
wantErr bool
}{
{
name: "loads complete state",
stateYAML: `operator:
email: "admin@example.com"
cloud:
central:
domain: "central.example.com"
`,
verify: func(t *testing.T, config *State) {
if config.Operator.Email != "admin@example.com" {
t.Error("operator email not loaded correctly")
}
if config.Cloud.Central.Domain != "central.example.com" {
t.Error("central domain not loaded correctly")
}
},
wantErr: false,
},
{
name: "loads minimal state",
stateYAML: `operator:
email: "admin@example.com"
`,
verify: func(t *testing.T, config *State) {
if config.Operator.Email != "admin@example.com" {
t.Error("operator email not loaded correctly")
}
},
wantErr: false,
},
{
name: "loads empty state",
stateYAML: "{}\n",
verify: func(t *testing.T, config *State) {
if config.Operator.Email != "" {
t.Error("expected empty operator email")
}
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
statePath := filepath.Join(tempDir, "state.yaml")
if err := os.WriteFile(statePath, []byte(tt.stateYAML), 0644); err != nil {
t.Fatalf("setup failed: %v", err)
}
config, err := LoadState(statePath)
if tt.wantErr {
if err == nil {
t.Error("expected error, got nil")
}
return
}
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
if config == nil {
t.Fatal("config is nil")
}
if tt.verify != nil {
tt.verify(t, config)
}
})
}
}
// Test: LoadState error cases
func TestLoadState_Errors(t *testing.T) {
tests := []struct {
name string
setupFunc func(t *testing.T) string
errContains string
}{
{
name: "non-existent file",
setupFunc: func(t *testing.T) string {
return filepath.Join(t.TempDir(), "nonexistent.yaml")
},
errContains: "reading config file",
},
{
name: "invalid yaml",
setupFunc: func(t *testing.T) string {
tempDir := t.TempDir()
statePath := filepath.Join(tempDir, "state.yaml")
content := `invalid: yaml: [[[`
if err := os.WriteFile(statePath, []byte(content), 0644); err != nil {
t.Fatalf("setup failed: %v", err)
}
return statePath
},
errContains: "parsing config file",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
statePath := tt.setupFunc(t)
_, err := LoadState(statePath)
if err == nil {
t.Error("expected error, got nil")
} else if !strings.Contains(err.Error(), tt.errContains) {
t.Errorf("error %q does not contain %q", err.Error(), tt.errContains)
}
})
}
}
// Test: SaveState saves state correctly
func TestSaveState(t *testing.T) {
tests := []struct {
name string
config *State
verify func(t *testing.T, statePath string)
}{
{
name: "saves complete state",
config: func() *State {
cfg := &State{}
cfg.Operator.Email = "admin@example.com"
cfg.Cloud.Central.Domain = "central.example.com"
return cfg
}(),
verify: func(t *testing.T, statePath string) {
content, err := os.ReadFile(statePath)
if err != nil {
t.Fatalf("failed to read saved state: %v", err)
}
contentStr := string(content)
if !strings.Contains(contentStr, "admin@example.com") {
t.Error("saved state missing operator email")
}
if !strings.Contains(contentStr, "central.example.com") {
t.Error("saved state missing central domain")
}
},
},
{
name: "saves empty state",
config: &State{},
verify: func(t *testing.T, statePath string) {
if _, err := os.Stat(statePath); os.IsNotExist(err) {
t.Error("state file not created")
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
statePath := filepath.Join(tempDir, "subdir", "state.yaml")
err := SaveState(tt.config, statePath)
if err != nil {
t.Errorf("SaveState failed: %v", err)
return
}
// Verify file exists
if _, err := os.Stat(statePath); err != nil {
t.Errorf("state file not created: %v", err)
return
}
// Verify file permissions
info, err := os.Stat(statePath)
if err != nil {
t.Fatalf("failed to stat state file: %v", err)
}
if info.Mode().Perm() != 0644 {
t.Errorf("expected permissions 0644, got %v", info.Mode().Perm())
}
// Verify content can be loaded back
loadedConfig, err := LoadState(statePath)
if err != nil {
t.Errorf("failed to reload saved state: %v", err)
} else if loadedConfig == nil {
t.Error("loaded config is nil")
}
if tt.verify != nil {
tt.verify(t, statePath)
}
})
}
}
// Test: SaveState creates directory
func TestSaveState_CreatesDirectory(t *testing.T) {
tempDir := t.TempDir()
statePath := filepath.Join(tempDir, "nested", "dirs", "state.yaml")
config := &State{}
err := SaveState(config, statePath)
if err != nil {
t.Fatalf("SaveState failed: %v", err)
}
// Verify nested directories were created
if _, err := os.Stat(filepath.Dir(statePath)); err != nil {
t.Errorf("directory not created: %v", err)
}
// Verify file exists
if _, err := os.Stat(statePath); err != nil {
t.Errorf("state file not created: %v", err)
}
}
// Test: Round-trip save and load preserves data
func TestState_RoundTrip(t *testing.T) {
tempDir := t.TempDir()
statePath := filepath.Join(tempDir, "state.yaml")
// Create config with all fields
original := &State{}
original.Operator.Email = "admin@example.com"
original.Cloud.Central.Domain = "central.example.com"
// Save
if err := SaveState(original, statePath); err != nil {
t.Fatalf("SaveState failed: %v", err)
}
// Load
loaded, err := LoadState(statePath)
if err != nil {
t.Fatalf("LoadState failed: %v", err)
}
// Verify all fields match
if loaded.Operator.Email != original.Operator.Email {
t.Errorf("email mismatch: got %q, want %q", loaded.Operator.Email, original.Operator.Email)
}
if loaded.Cloud.Central.Domain != original.Cloud.Central.Domain {
t.Errorf("central domain mismatch: got %q, want %q", loaded.Cloud.Central.Domain, original.Cloud.Central.Domain)
}
}