It's state, not config.
This commit is contained in:
@@ -9,36 +9,35 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Test: LoadGlobalConfig loads valid configuration
|
||||
func TestLoadGlobalConfig(t *testing.T) {
|
||||
// Test: LoadState loads valid state
|
||||
func TestLoadState(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
configYAML string
|
||||
verify func(t *testing.T, config *GlobalConfig)
|
||||
wantErr bool
|
||||
name string
|
||||
stateYAML string
|
||||
verify func(t *testing.T, config *GlobalConfig)
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "loads complete configuration",
|
||||
configYAML: `operator:
|
||||
name: "loads complete state",
|
||||
stateYAML: `operator:
|
||||
email: "admin@example.com"
|
||||
cloud:
|
||||
router:
|
||||
ip: "192.168.1.254"
|
||||
dynamicDns: "example.dyndns.org"
|
||||
central:
|
||||
domain: "central.example.com"
|
||||
`,
|
||||
verify: func(t *testing.T, config *GlobalConfig) {
|
||||
if config.Operator.Email != "admin@example.com" {
|
||||
t.Error("operator email not loaded correctly")
|
||||
}
|
||||
if config.Cloud.Router.IP != "192.168.1.254" {
|
||||
t.Error("router IP not loaded correctly")
|
||||
if config.Cloud.Central.Domain != "central.example.com" {
|
||||
t.Error("central domain not loaded correctly")
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "loads minimal configuration",
|
||||
configYAML: `operator:
|
||||
name: "loads minimal state",
|
||||
stateYAML: `operator:
|
||||
email: "admin@example.com"
|
||||
`,
|
||||
verify: func(t *testing.T, config *GlobalConfig) {
|
||||
@@ -49,9 +48,8 @@ cloud:
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "loads empty configuration",
|
||||
configYAML: `{}
|
||||
`,
|
||||
name: "loads empty state",
|
||||
stateYAML: "{}\n",
|
||||
verify: func(t *testing.T, config *GlobalConfig) {
|
||||
if config.Operator.Email != "" {
|
||||
t.Error("expected empty operator email")
|
||||
@@ -64,13 +62,13 @@ cloud:
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
configPath := filepath.Join(tempDir, "config.yaml")
|
||||
statePath := filepath.Join(tempDir, "state.yaml")
|
||||
|
||||
if err := os.WriteFile(configPath, []byte(tt.configYAML), 0644); err != nil {
|
||||
if err := os.WriteFile(statePath, []byte(tt.stateYAML), 0644); err != nil {
|
||||
t.Fatalf("setup failed: %v", err)
|
||||
}
|
||||
|
||||
config, err := LoadGlobalConfig(configPath)
|
||||
config, err := LoadState(statePath)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Error("expected error, got nil")
|
||||
@@ -94,8 +92,8 @@ cloud:
|
||||
}
|
||||
}
|
||||
|
||||
// Test: LoadGlobalConfig error cases
|
||||
func TestLoadGlobalConfig_Errors(t *testing.T) {
|
||||
// Test: LoadState error cases
|
||||
func TestLoadState_Errors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFunc func(t *testing.T) string
|
||||
@@ -112,12 +110,12 @@ func TestLoadGlobalConfig_Errors(t *testing.T) {
|
||||
name: "invalid yaml",
|
||||
setupFunc: func(t *testing.T) string {
|
||||
tempDir := t.TempDir()
|
||||
configPath := filepath.Join(tempDir, "config.yaml")
|
||||
statePath := filepath.Join(tempDir, "state.yaml")
|
||||
content := `invalid: yaml: [[[`
|
||||
if err := os.WriteFile(configPath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(statePath, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("setup failed: %v", err)
|
||||
}
|
||||
return configPath
|
||||
return statePath
|
||||
},
|
||||
errContains: "parsing config file",
|
||||
},
|
||||
@@ -125,8 +123,8 @@ func TestLoadGlobalConfig_Errors(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
configPath := tt.setupFunc(t)
|
||||
_, err := LoadGlobalConfig(configPath)
|
||||
statePath := tt.setupFunc(t)
|
||||
_, err := LoadState(statePath)
|
||||
|
||||
if err == nil {
|
||||
t.Error("expected error, got nil")
|
||||
@@ -137,42 +135,41 @@ func TestLoadGlobalConfig_Errors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test: SaveGlobalConfig saves configuration correctly
|
||||
func TestSaveGlobalConfig(t *testing.T) {
|
||||
// Test: SaveState saves state correctly
|
||||
func TestSaveState(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *GlobalConfig
|
||||
verify func(t *testing.T, configPath string)
|
||||
verify func(t *testing.T, statePath string)
|
||||
}{
|
||||
{
|
||||
name: "saves complete configuration",
|
||||
name: "saves complete state",
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Operator.Email = "admin@example.com"
|
||||
cfg.Cloud.Router.IP = "192.168.1.254"
|
||||
cfg.Cloud.Router.DynamicDns = "example.dyndns.org"
|
||||
cfg.Cloud.Central.Domain = "central.example.com"
|
||||
return cfg
|
||||
}(),
|
||||
verify: func(t *testing.T, configPath string) {
|
||||
content, err := os.ReadFile(configPath)
|
||||
verify: func(t *testing.T, statePath string) {
|
||||
content, err := os.ReadFile(statePath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read saved config: %v", err)
|
||||
t.Fatalf("failed to read saved state: %v", err)
|
||||
}
|
||||
contentStr := string(content)
|
||||
if !strings.Contains(contentStr, "admin@example.com") {
|
||||
t.Error("saved config missing operator email")
|
||||
t.Error("saved state missing operator email")
|
||||
}
|
||||
if !strings.Contains(contentStr, "192.168.1.254") {
|
||||
t.Error("saved config missing router IP")
|
||||
if !strings.Contains(contentStr, "central.example.com") {
|
||||
t.Error("saved state missing central domain")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "saves empty configuration",
|
||||
name: "saves empty state",
|
||||
config: &GlobalConfig{},
|
||||
verify: func(t *testing.T, configPath string) {
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
t.Error("config file not created")
|
||||
verify: func(t *testing.T, statePath string) {
|
||||
if _, err := os.Stat(statePath); os.IsNotExist(err) {
|
||||
t.Error("state file not created")
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -181,63 +178,63 @@ func TestSaveGlobalConfig(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
configPath := filepath.Join(tempDir, "subdir", "config.yaml")
|
||||
statePath := filepath.Join(tempDir, "subdir", "state.yaml")
|
||||
|
||||
err := SaveGlobalConfig(tt.config, configPath)
|
||||
err := SaveState(tt.config, statePath)
|
||||
if err != nil {
|
||||
t.Errorf("SaveGlobalConfig failed: %v", err)
|
||||
t.Errorf("SaveState failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify file exists
|
||||
if _, err := os.Stat(configPath); err != nil {
|
||||
t.Errorf("config file not created: %v", err)
|
||||
if _, err := os.Stat(statePath); err != nil {
|
||||
t.Errorf("state file not created: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify file permissions
|
||||
info, err := os.Stat(configPath)
|
||||
info, err := os.Stat(statePath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat config file: %v", err)
|
||||
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 := LoadGlobalConfig(configPath)
|
||||
loadedConfig, err := LoadState(statePath)
|
||||
if err != nil {
|
||||
t.Errorf("failed to reload saved config: %v", err)
|
||||
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, configPath)
|
||||
tt.verify(t, statePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test: SaveGlobalConfig creates directory
|
||||
func TestSaveGlobalConfig_CreatesDirectory(t *testing.T) {
|
||||
// Test: SaveState creates directory
|
||||
func TestSaveState_CreatesDirectory(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
configPath := filepath.Join(tempDir, "nested", "dirs", "config.yaml")
|
||||
statePath := filepath.Join(tempDir, "nested", "dirs", "state.yaml")
|
||||
|
||||
config := &GlobalConfig{}
|
||||
err := SaveGlobalConfig(config, configPath)
|
||||
err := SaveState(config, statePath)
|
||||
if err != nil {
|
||||
t.Fatalf("SaveGlobalConfig failed: %v", err)
|
||||
t.Fatalf("SaveState failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify nested directories were created
|
||||
if _, err := os.Stat(filepath.Dir(configPath)); err != nil {
|
||||
if _, err := os.Stat(filepath.Dir(statePath)); err != nil {
|
||||
t.Errorf("directory not created: %v", err)
|
||||
}
|
||||
|
||||
// Verify file exists
|
||||
if _, err := os.Stat(configPath); err != nil {
|
||||
t.Errorf("config file not created: %v", err)
|
||||
if _, err := os.Stat(statePath); err != nil {
|
||||
t.Errorf("state file not created: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,10 +256,10 @@ func TestGlobalConfig_IsEmpty(t *testing.T) {
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "config with only router IP is not empty",
|
||||
name: "config with only central domain is not empty",
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Cloud.Router.IP = "192.168.1.254"
|
||||
cfg.Cloud.Central.Domain = "central.example.com"
|
||||
return cfg
|
||||
}(),
|
||||
want: false,
|
||||
@@ -280,7 +277,7 @@ func TestGlobalConfig_IsEmpty(t *testing.T) {
|
||||
name: "config with all fields is not empty",
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Cloud.Router.IP = "192.168.1.254"
|
||||
cfg.Cloud.Central.Domain = "central.example.com"
|
||||
cfg.Operator.Email = "admin@example.com"
|
||||
return cfg
|
||||
}(),
|
||||
@@ -482,34 +479,30 @@ func TestSaveCloudConfig(t *testing.T) {
|
||||
// Test: Round-trip save and load preserves data
|
||||
func TestGlobalConfig_RoundTrip(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
configPath := filepath.Join(tempDir, "config.yaml")
|
||||
statePath := filepath.Join(tempDir, "state.yaml")
|
||||
|
||||
// Create config with all fields
|
||||
original := &GlobalConfig{}
|
||||
original.Operator.Email = "admin@example.com"
|
||||
original.Cloud.Router.IP = "192.168.1.254"
|
||||
original.Cloud.Router.DynamicDns = "example.dyndns.org"
|
||||
original.Cloud.Central.Domain = "central.example.com"
|
||||
|
||||
// Save config
|
||||
if err := SaveGlobalConfig(original, configPath); err != nil {
|
||||
t.Fatalf("SaveGlobalConfig failed: %v", err)
|
||||
// Save
|
||||
if err := SaveState(original, statePath); err != nil {
|
||||
t.Fatalf("SaveState failed: %v", err)
|
||||
}
|
||||
|
||||
// Load config
|
||||
loaded, err := LoadGlobalConfig(configPath)
|
||||
// Load
|
||||
loaded, err := LoadState(statePath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadGlobalConfig failed: %v", err)
|
||||
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.Router.IP != original.Cloud.Router.IP {
|
||||
t.Errorf("router IP mismatch: got %q, want %q", loaded.Cloud.Router.IP, original.Cloud.Router.IP)
|
||||
}
|
||||
if loaded.Cloud.Router.DynamicDns != original.Cloud.Router.DynamicDns {
|
||||
t.Errorf("dynamic DNS mismatch: got %q, want %q", loaded.Cloud.Router.DynamicDns, original.Cloud.Router.DynamicDns)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +554,7 @@ func TestDeepMerge(t *testing.T) {
|
||||
name: "nested maps merge recursively",
|
||||
dst: map[string]any{
|
||||
"cloud": map[string]any{
|
||||
"router": map[string]any{"ip": "192.168.1.1"},
|
||||
"central": map[string]any{"domain": "central.example.com"},
|
||||
},
|
||||
},
|
||||
src: map[string]any{
|
||||
@@ -571,8 +564,8 @@ func TestDeepMerge(t *testing.T) {
|
||||
},
|
||||
expected: map[string]any{
|
||||
"cloud": map[string]any{
|
||||
"router": map[string]any{"ip": "192.168.1.1"},
|
||||
"domain": "example.com",
|
||||
"central": map[string]any{"domain": "central.example.com"},
|
||||
"domain": "example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -620,11 +613,11 @@ func TestLoadMergedInstanceConfig(t *testing.T) {
|
||||
instanceConfigDir := filepath.Join(dataDir, "instances", instanceName, "config")
|
||||
os.MkdirAll(instanceConfigDir, 0755)
|
||||
|
||||
globalConfig := `operator:
|
||||
globalState := `operator:
|
||||
email: test@example.com
|
||||
cloud:
|
||||
router:
|
||||
ip: 192.168.1.1
|
||||
central:
|
||||
domain: central.example.com
|
||||
`
|
||||
instanceConfig := `cluster:
|
||||
name: test
|
||||
@@ -635,7 +628,7 @@ cloud:
|
||||
domain: cloud.example.com
|
||||
`
|
||||
|
||||
os.WriteFile(filepath.Join(dataDir, "config.yaml"), []byte(globalConfig), 0644)
|
||||
os.WriteFile(filepath.Join(dataDir, "state.yaml"), []byte(globalState), 0644)
|
||||
os.WriteFile(filepath.Join(instanceConfigDir, "instance.yaml"), []byte(instanceConfig), 0644)
|
||||
|
||||
merged, err := LoadMergedInstanceConfig(dataDir, instanceName)
|
||||
@@ -648,12 +641,12 @@ cloud:
|
||||
t.Fatal("missing cloud key in merged config")
|
||||
}
|
||||
|
||||
router, ok := cloud["router"].(map[string]any)
|
||||
central, ok := cloud["central"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("missing cloud.router key — global config not merged")
|
||||
t.Fatal("missing cloud.central key — global state not merged")
|
||||
}
|
||||
if router["ip"] != "192.168.1.1" {
|
||||
t.Errorf("cloud.router.ip = %v, want 192.168.1.1", router["ip"])
|
||||
if central["domain"] != "central.example.com" {
|
||||
t.Errorf("cloud.central.domain = %v, want central.example.com", central["domain"])
|
||||
}
|
||||
|
||||
if cloud["domain"] != "cloud.example.com" {
|
||||
@@ -670,14 +663,14 @@ cloud:
|
||||
|
||||
operator, ok := merged["operator"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("missing operator key — global config not merged")
|
||||
t.Fatal("missing operator key — global state not merged")
|
||||
}
|
||||
if operator["email"] != "test@example.com" {
|
||||
t.Errorf("operator.email = %v, want test@example.com", operator["email"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadMergedInstanceConfig_NoGlobalConfig(t *testing.T) {
|
||||
func TestLoadMergedInstanceConfig_NoGlobalState(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
instanceConfigDir := filepath.Join(dataDir, "instances", "test", "config")
|
||||
os.MkdirAll(instanceConfigDir, 0755)
|
||||
|
||||
Reference in New Issue
Block a user