package deploymentconfig import ( "os" "path/filepath" "strings" "testing" ) const validNativeConfig = `[backend] type = "native" release_dir = "/home/yms/lib/releases" active_jar = "/home/yms/lib/glory-soft-yms.jar" systemctl_path = "/bin/systemctl" [backend.slot.8080] unit = "yms-backend@8080.service" jar = "/home/yms/lib/glory-soft-yms-8080.jar" health_endpoint = "http://127.0.0.1:8080/yms/actuator/health" [backend.slot.8081] unit = "yms-backend@8081.service" jar = "/home/yms/lib/glory-soft-yms-8081.jar" health_endpoint = "http://127.0.0.1:8081/yms/actuator/health" ` func TestLoadValidNativeConfiguration(t *testing.T) { path := writeConfig(t, validNativeConfig) config, err := Load(path) if err != nil { t.Fatalf("load native deployment configuration: %v", err) } if config.Backend.Type != BackendTypeNative || config.Backend.SystemctlPath != "/bin/systemctl" { t.Fatalf("unexpected backend configuration: %+v", config.Backend) } slot8080, err := config.Backend.SlotForPort(BackendPort8080) if err != nil { t.Fatalf("read 8080 slot: %v", err) } if slot8080.Unit != nativeUnit8080 || slot8080.JAR != nativeSlotJAR8080 || slot8080.HealthEndpoint != nativeHealthURL8080 { t.Fatalf("unexpected 8080 slot: %+v", slot8080) } slot8081, err := config.Backend.SlotForPort(BackendPort8081) if err != nil { t.Fatalf("read 8081 slot: %v", err) } if slot8081.Unit != nativeUnit8081 || slot8081.JAR != nativeSlotJAR8081 || slot8081.HealthEndpoint != nativeHealthURL8081 { t.Fatalf("unexpected 8081 slot: %+v", slot8081) } } func TestPackagedNativeConfigurationMatchesContract(t *testing.T) { path, err := filepath.Abs(filepath.Join("..", "..", "packaging", "etc", "yms-daemon", "yms-daemon.toml")) if err != nil { t.Fatalf("resolve packaged native deployment configuration: %v", err) } if _, err := Load(path); err != nil { t.Fatalf("load packaged native deployment configuration: %v", err) } } func TestLoadRejectsUnknownField(t *testing.T) { content := strings.Replace(validNativeConfig, "systemctl_path = \"/bin/systemctl\"", "systemctl_path = \"/bin/systemctl\"\nSystemctlPath = \"/usr/bin/systemctl\"", 1) _, err := Load(writeConfig(t, content)) if err == nil || !strings.Contains(err.Error(), "SystemctlPath") { t.Fatalf("expected exact unknown field rejection, got %v", err) } } func TestLoadRejectsWrongTableCase(t *testing.T) { content := strings.Replace(validNativeConfig, "[backend]", "[Backend]", 1) _, err := Load(writeConfig(t, content)) if err == nil || !strings.Contains(err.Error(), "Backend") { t.Fatalf("expected exact table case rejection, got %v", err) } } func TestLoadRejectsMissingNativeField(t *testing.T) { content := strings.Replace(validNativeConfig, "active_jar = \"/home/yms/lib/glory-soft-yms.jar\"\n", "", 1) _, err := Load(writeConfig(t, content)) if err == nil || !strings.Contains(err.Error(), "backend.active_jar") { t.Fatalf("expected missing active JAR rejection, got %v", err) } } func TestLoadRejectsUnknownSlot(t *testing.T) { content := validNativeConfig + ` [backend.slot.9090] unit = "yms-backend@9090.service" jar = "/home/yms/lib/glory-soft-yms-9090.jar" health_endpoint = "http://127.0.0.1:9090/yms/actuator/health" ` _, err := Load(writeConfig(t, content)) if err == nil || !strings.Contains(err.Error(), "9090") { t.Fatalf("expected unknown slot rejection, got %v", err) } } func TestLoadRejectsChangedSlotEndpoint(t *testing.T) { content := strings.Replace(validNativeConfig, nativeHealthURL8080, nativeHealthURL8081, 1) _, err := Load(writeConfig(t, content)) if err == nil || !strings.Contains(err.Error(), "backend.slot.8080.health_endpoint") { t.Fatalf("expected changed health endpoint rejection, got %v", err) } } func TestLoadAcceptsExplicitUsrBinSystemctlPath(t *testing.T) { content := strings.Replace(validNativeConfig, "/bin/systemctl", "/usr/bin/systemctl", 1) config, err := Load(writeConfig(t, content)) if err != nil { t.Fatalf("load explicit /usr/bin/systemctl path: %v", err) } if config.Backend.SystemctlPath != "/usr/bin/systemctl" { t.Fatalf("unexpected systemctl path: %q", config.Backend.SystemctlPath) } } func TestLoadRejectsChangedBackendTypeCase(t *testing.T) { content := strings.Replace(validNativeConfig, `type = "native"`, `type = "Native"`, 1) _, err := Load(writeConfig(t, content)) if err == nil || !strings.Contains(err.Error(), "backend.type") { t.Fatalf("expected exact backend type rejection, got %v", err) } } func TestLoadRejectsDuplicateField(t *testing.T) { content := strings.Replace(validNativeConfig, "type = \"native\"", "type = \"native\"\ntype = \"native\"", 1) _, err := Load(writeConfig(t, content)) if err == nil { t.Fatal("expected duplicate field rejection") } } func TestLoadRequiresAbsoluteConfigurationPath(t *testing.T) { _, err := Load("yms-daemon.toml") if err == nil || !strings.Contains(err.Error(), "absolute path") { t.Fatalf("expected absolute configuration path rejection, got %v", err) } } func TestSlotForPortRejectsUnsupportedPort(t *testing.T) { config, err := Load(writeConfig(t, validNativeConfig)) if err != nil { t.Fatalf("load native deployment configuration: %v", err) } if _, err := config.Backend.SlotForPort(9090); err == nil { t.Fatal("expected unsupported port rejection") } } func writeConfig(t *testing.T, content string) string { t.Helper() path := filepath.Join(t.TempDir(), "yms-daemon.toml") if err := os.WriteFile(path, []byte(content), 0o600); err != nil { t.Fatalf("write deployment configuration: %v", err) } return path }