too much for one commit
This commit is contained in:
@@ -0,0 +1,634 @@
|
||||
using EntKube.Clusters.Domain;
|
||||
using EntKube.Clusters.Features.AdoptCluster;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace EntKube.Clusters.Tests.Features;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the component configuration schema system. Schemas define what
|
||||
/// parameters each component accepts, their types, validation constraints,
|
||||
/// and how they're grouped in the UI. This enables the frontend to render
|
||||
/// proper typed forms instead of raw key/value editors.
|
||||
/// </summary>
|
||||
public class ConfigurationSchemaTests
|
||||
{
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithRequiredString_ValidatesPresence()
|
||||
{
|
||||
// Arrange — A required parameter with no value provided.
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "email",
|
||||
DisplayName: "Email Address",
|
||||
Description: "The email registered with the certificate authority",
|
||||
Type: ParameterType.String,
|
||||
Group: "General",
|
||||
IsRequired: true);
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate(null);
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Error.Should().Contain("required");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithRequiredString_PassesWhenProvided()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "email",
|
||||
DisplayName: "Email Address",
|
||||
Description: "The email registered with the certificate authority",
|
||||
Type: ParameterType.String,
|
||||
Group: "General",
|
||||
IsRequired: true);
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate("admin@example.com");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithIntegerType_RejectsNonNumeric()
|
||||
{
|
||||
// Arrange — An integer parameter with a non-numeric value.
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "replicas",
|
||||
DisplayName: "Replicas",
|
||||
Description: "Number of pod replicas",
|
||||
Type: ParameterType.Integer,
|
||||
Group: "Scaling",
|
||||
IsRequired: true,
|
||||
Min: 1,
|
||||
Max: 10);
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate("not-a-number");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Error.Should().Contain("integer");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithIntegerType_RejectsOutOfRange()
|
||||
{
|
||||
// Arrange — An integer outside the allowed range.
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "replicas",
|
||||
DisplayName: "Replicas",
|
||||
Description: "Number of pod replicas",
|
||||
Type: ParameterType.Integer,
|
||||
Group: "Scaling",
|
||||
IsRequired: true,
|
||||
Min: 1,
|
||||
Max: 10);
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate("15");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Error.Should().Contain("10");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithIntegerType_AcceptsValidValue()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "replicas",
|
||||
DisplayName: "Replicas",
|
||||
Description: "Number of pod replicas",
|
||||
Type: ParameterType.Integer,
|
||||
Group: "Scaling",
|
||||
IsRequired: true,
|
||||
Min: 1,
|
||||
Max: 10);
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate("3");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithBooleanType_RejectsInvalidValues()
|
||||
{
|
||||
// Arrange — A boolean parameter with a non-boolean value.
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "grafanaEnabled",
|
||||
DisplayName: "Enable Grafana",
|
||||
Description: "Whether to deploy Grafana alongside Prometheus",
|
||||
Type: ParameterType.Boolean,
|
||||
Group: "Features");
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate("maybe");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Error.Should().Contain("true");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithBooleanType_AcceptsTrueAndFalse()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "grafanaEnabled",
|
||||
DisplayName: "Enable Grafana",
|
||||
Description: "Whether to deploy Grafana alongside Prometheus",
|
||||
Type: ParameterType.Boolean,
|
||||
Group: "Features");
|
||||
|
||||
// Act & Assert
|
||||
|
||||
param.Validate("true").IsValid.Should().BeTrue();
|
||||
param.Validate("false").IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithSelectType_RejectsUnknownOption()
|
||||
{
|
||||
// Arrange — A select parameter with allowed values.
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "provider",
|
||||
DisplayName: "Ingress Provider",
|
||||
Description: "Which ingress controller to use",
|
||||
Type: ParameterType.Select,
|
||||
Group: "General",
|
||||
IsRequired: true,
|
||||
AllowedValues: new List<string> { "traefik", "istio" });
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate("nginx");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Error.Should().Contain("traefik");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_WithSelectType_AcceptsAllowedValue()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "provider",
|
||||
DisplayName: "Ingress Provider",
|
||||
Description: "Which ingress controller to use",
|
||||
Type: ParameterType.Select,
|
||||
Group: "General",
|
||||
IsRequired: true,
|
||||
AllowedValues: new List<string> { "traefik", "istio" });
|
||||
|
||||
// Act
|
||||
|
||||
ParameterValidationResult result = param.Validate("istio");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationParameter_Optional_AcceptsNullOrEmpty()
|
||||
{
|
||||
// Arrange — An optional parameter with no value is valid.
|
||||
|
||||
ConfigurationParameter param = new(
|
||||
Key: "storageSize",
|
||||
DisplayName: "Storage Size",
|
||||
Description: "PVC storage size",
|
||||
Type: ParameterType.String,
|
||||
Group: "Storage",
|
||||
IsRequired: false,
|
||||
DefaultValue: "50Gi");
|
||||
|
||||
// Act & Assert
|
||||
|
||||
param.Validate(null).IsValid.Should().BeTrue();
|
||||
param.Validate("").IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationSchema_ValidateAll_ReturnsAllErrors()
|
||||
{
|
||||
// Arrange — A schema with multiple parameters, some invalid.
|
||||
|
||||
ConfigurationSchema schema = new(
|
||||
ComponentName: "monitoring",
|
||||
DisplayName: "Monitoring (kube-prometheus-stack)",
|
||||
Description: "Prometheus, Grafana, and Alertmanager stack",
|
||||
Parameters: new List<ConfigurationParameter>
|
||||
{
|
||||
new(Key: "retention", DisplayName: "Retention", Description: "Data retention period",
|
||||
Type: ParameterType.String, Group: "Storage", IsRequired: true),
|
||||
new(Key: "replicas", DisplayName: "Prometheus Replicas", Description: "Number of Prometheus replicas",
|
||||
Type: ParameterType.Integer, Group: "Scaling", IsRequired: true, Min: 1, Max: 10),
|
||||
new(Key: "grafanaEnabled", DisplayName: "Enable Grafana", Description: "Deploy Grafana",
|
||||
Type: ParameterType.Boolean, Group: "Features")
|
||||
});
|
||||
|
||||
Dictionary<string, string> values = new()
|
||||
{
|
||||
["replicas"] = "999",
|
||||
["grafanaEnabled"] = "invalid"
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
SchemaValidationResult result = schema.Validate(values);
|
||||
|
||||
// Assert — Three errors: retention missing, replicas out of range, grafana invalid.
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Errors.Should().HaveCount(3);
|
||||
result.Errors.Should().ContainKey("retention");
|
||||
result.Errors.Should().ContainKey("replicas");
|
||||
result.Errors.Should().ContainKey("grafanaEnabled");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationSchema_ValidateAll_PassesWithValidValues()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
ConfigurationSchema schema = new(
|
||||
ComponentName: "monitoring",
|
||||
DisplayName: "Monitoring (kube-prometheus-stack)",
|
||||
Description: "Prometheus, Grafana, and Alertmanager stack",
|
||||
Parameters: new List<ConfigurationParameter>
|
||||
{
|
||||
new(Key: "retention", DisplayName: "Retention", Description: "Data retention period",
|
||||
Type: ParameterType.String, Group: "Storage", IsRequired: true),
|
||||
new(Key: "replicas", DisplayName: "Prometheus Replicas", Description: "Number of Prometheus replicas",
|
||||
Type: ParameterType.Integer, Group: "Scaling", IsRequired: true, Min: 1, Max: 10)
|
||||
});
|
||||
|
||||
Dictionary<string, string> values = new()
|
||||
{
|
||||
["retention"] = "30d",
|
||||
["replicas"] = "2"
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
SchemaValidationResult result = schema.Validate(values);
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsValid.Should().BeTrue();
|
||||
result.Errors.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigurationSchema_GroupsParameters_ByGroupName()
|
||||
{
|
||||
// Arrange — Schema parameters can be grouped for UI rendering.
|
||||
|
||||
ConfigurationSchema schema = new(
|
||||
ComponentName: "monitoring",
|
||||
DisplayName: "Monitoring",
|
||||
Description: "Prometheus stack",
|
||||
Parameters: new List<ConfigurationParameter>
|
||||
{
|
||||
new(Key: "retention", DisplayName: "Retention", Description: "Retention",
|
||||
Type: ParameterType.String, Group: "Storage"),
|
||||
new(Key: "storageSize", DisplayName: "Storage Size", Description: "PVC size",
|
||||
Type: ParameterType.String, Group: "Storage"),
|
||||
new(Key: "replicas", DisplayName: "Replicas", Description: "Replicas",
|
||||
Type: ParameterType.Integer, Group: "Scaling", Min: 1, Max: 10),
|
||||
new(Key: "grafanaEnabled", DisplayName: "Grafana", Description: "Enable Grafana",
|
||||
Type: ParameterType.Boolean, Group: "Features")
|
||||
});
|
||||
|
||||
// Act
|
||||
|
||||
Dictionary<string, List<ConfigurationParameter>> groups = schema.GetParametersByGroup();
|
||||
|
||||
// Assert
|
||||
|
||||
groups.Should().HaveCount(3);
|
||||
groups["Storage"].Should().HaveCount(2);
|
||||
groups["Scaling"].Should().HaveCount(1);
|
||||
groups["Features"].Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CertManagerSchema_DoesNotContainLetsEncryptParameters()
|
||||
{
|
||||
// Arrange & Act — The cert-manager schema should only have scaling
|
||||
// parameters. Let's Encrypt configuration belongs to the letsencrypt component.
|
||||
|
||||
ConfigurationSchema schema = ComponentSchemas.GetSchema("cert-manager");
|
||||
|
||||
// Assert — No Let's Encrypt-related keys should exist.
|
||||
|
||||
List<string> paramKeys = schema.Parameters.Select(p => p.Key).ToList();
|
||||
paramKeys.Should().NotContain("letsEncryptEmail");
|
||||
paramKeys.Should().NotContain("httpSolverEnabled");
|
||||
paramKeys.Should().NotContain("dnsSolverEnabled");
|
||||
paramKeys.Should().NotContain("dnsSolverProvider");
|
||||
paramKeys.Should().Contain("replicas");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LetsEncryptSchema_ContainsSolverAndEmailParameters()
|
||||
{
|
||||
// Arrange & Act — The letsencrypt schema should own all the ACME/solver
|
||||
// configuration that was previously in cert-manager.
|
||||
|
||||
ConfigurationSchema schema = ComponentSchemas.GetSchema("letsencrypt");
|
||||
|
||||
// Assert
|
||||
|
||||
List<string> paramKeys = schema.Parameters.Select(p => p.Key).ToList();
|
||||
paramKeys.Should().Contain("email");
|
||||
paramKeys.Should().Contain("httpSolverEnabled");
|
||||
paramKeys.Should().Contain("dnsSolverEnabled");
|
||||
paramKeys.Should().Contain("dnsSolverProvider");
|
||||
schema.Parameters.First(p => p.Key == "email").IsRequired.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CertManagerSchema_OnlyHasScalingGroup()
|
||||
{
|
||||
// The cert-manager component is purely the runtime engine — its schema
|
||||
// should only expose scaling settings, not TLS/issuer configuration.
|
||||
|
||||
ConfigurationSchema schema = ComponentSchemas.GetSchema("cert-manager");
|
||||
|
||||
Dictionary<string, List<ConfigurationParameter>> groups = schema.GetParametersByGroup();
|
||||
|
||||
groups.Should().HaveCount(1);
|
||||
groups.Keys.Should().Contain("Scaling");
|
||||
}
|
||||
|
||||
// ─── Contextual Schema Enrichment ─────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_WithIstioIngress_DefaultsToGatewayHTTPRoute()
|
||||
{
|
||||
// Arrange — A cluster where the ingress component reports Istio as the
|
||||
// provider, with an internal gateway detected. When configuring Let's
|
||||
// Encrypt on this cluster, the HTTP-01 solver should default to
|
||||
// gatewayHTTPRoute mode with the internal gateway pre-selected.
|
||||
|
||||
List<ClusterComponent> components = new()
|
||||
{
|
||||
BuildComponent("ingress", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["provider"] = "istio",
|
||||
["hasInternalGateway"] = "True",
|
||||
["hasExternalGateway"] = "True"
|
||||
})
|
||||
};
|
||||
|
||||
// Act — Enrich the letsencrypt schema using the cluster's component state.
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("letsencrypt", components);
|
||||
|
||||
// Assert — The HTTP-01 solver should default to Gateway API mode.
|
||||
|
||||
ConfigurationParameter modeParam = enriched.Parameters.First(p => p.Key == "httpSolverMode");
|
||||
modeParam.DefaultValue.Should().Be("gatewayHTTPRoute");
|
||||
|
||||
ConfigurationParameter gatewayNameParam = enriched.Parameters.First(p => p.Key == "httpSolverGatewayName");
|
||||
gatewayNameParam.DefaultValue.Should().Be("internal");
|
||||
|
||||
ConfigurationParameter gatewayNsParam = enriched.Parameters.First(p => p.Key == "httpSolverGatewayNamespace");
|
||||
gatewayNsParam.DefaultValue.Should().Be("internal-ingress");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_WithTraefikIngress_DefaultsToIngressMode()
|
||||
{
|
||||
// Arrange — A cluster using Traefik for ingress. The HTTP-01 solver
|
||||
// should default to ingress mode with "traefik" as the class.
|
||||
|
||||
List<ClusterComponent> components = new()
|
||||
{
|
||||
BuildComponent("ingress", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["provider"] = "traefik",
|
||||
["ingressClassRegistered"] = "True"
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("letsencrypt", components);
|
||||
|
||||
// Assert — Should remain in ingress mode with traefik class.
|
||||
|
||||
ConfigurationParameter modeParam = enriched.Parameters.First(p => p.Key == "httpSolverMode");
|
||||
modeParam.DefaultValue.Should().Be("ingress");
|
||||
|
||||
ConfigurationParameter classParam = enriched.Parameters.First(p => p.Key == "httpSolverIngressClass");
|
||||
classParam.DefaultValue.Should().Be("traefik");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_WithNoIngressComponent_UsesStaticDefaults()
|
||||
{
|
||||
// Arrange — No ingress component detected on the cluster. The schema
|
||||
// should return unchanged static defaults.
|
||||
|
||||
List<ClusterComponent> components = new();
|
||||
|
||||
// Act
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("letsencrypt", components);
|
||||
|
||||
// Assert — Static defaults should be preserved.
|
||||
|
||||
ConfigurationParameter modeParam = enriched.Parameters.First(p => p.Key == "httpSolverMode");
|
||||
modeParam.DefaultValue.Should().Be("ingress");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_WithIstioAndDiscoveredGateways_SetsGatewayAsSelect()
|
||||
{
|
||||
// Arrange — A cluster with Istio and discovered gateways stored in
|
||||
// the ingress component's config. The httpSolverGatewayName should
|
||||
// become a Select parameter with the available gateways as options.
|
||||
|
||||
List<ClusterComponent> components = new()
|
||||
{
|
||||
BuildComponent("ingress", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["provider"] = "istio",
|
||||
["hasInternalGateway"] = "True",
|
||||
["hasExternalGateway"] = "True"
|
||||
}),
|
||||
BuildComponent("letsencrypt", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["availableGateways"] = "[{\"name\":\"internal\",\"namespace\":\"internal-ingress\"},{\"name\":\"external\",\"namespace\":\"external-ingress\"}]"
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("letsencrypt", components);
|
||||
|
||||
// Assert — Gateway name should be a Select with discovered gateways as options.
|
||||
|
||||
ConfigurationParameter gatewayParam = enriched.Parameters.First(p => p.Key == "httpSolverGatewayName");
|
||||
gatewayParam.Type.Should().Be(ParameterType.Select);
|
||||
gatewayParam.AllowedValues.Should().Contain("internal");
|
||||
gatewayParam.AllowedValues.Should().Contain("external");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_NonLetsEncryptComponent_ReturnsUnchanged()
|
||||
{
|
||||
// Arrange — Enriching a component other than letsencrypt should
|
||||
// return the static schema unchanged.
|
||||
|
||||
List<ClusterComponent> components = new()
|
||||
{
|
||||
BuildComponent("ingress", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["provider"] = "istio"
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("cert-manager", components);
|
||||
|
||||
// Assert — cert-manager schema should be unchanged.
|
||||
|
||||
ConfigurationSchema original = ComponentSchemas.GetSchema("cert-manager");
|
||||
enriched.Parameters.Should().HaveCount(original.Parameters.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_WithIstioAndExistingLetsEncryptConfig_PreservesDiscoveredValues()
|
||||
{
|
||||
// Arrange — A cluster where Let's Encrypt was already configured with
|
||||
// specific values. The enrichment should set defaults from the ingress
|
||||
// provider but the existing discovered config (email, solver type) is
|
||||
// separate and handled by the UI pre-fill, not schema defaults.
|
||||
|
||||
List<ClusterComponent> components = new()
|
||||
{
|
||||
BuildComponent("ingress", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["provider"] = "istio",
|
||||
["hasInternalGateway"] = "True",
|
||||
["hasExternalGateway"] = "False"
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("letsencrypt", components);
|
||||
|
||||
// Assert — With only internal gateway, external should not appear in options.
|
||||
// The gateway namespace should default to internal-ingress.
|
||||
|
||||
ConfigurationParameter gatewayNsParam = enriched.Parameters.First(p => p.Key == "httpSolverGatewayNamespace");
|
||||
gatewayNsParam.DefaultValue.Should().Be("internal-ingress");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_IngressWithExternalGateway_DefaultsEnableExternalToTrue()
|
||||
{
|
||||
// Arrange — The cluster scan detected an external gateway. When the
|
||||
// user opens the ingress configuration, the enable_external toggle
|
||||
// should default to true so it reflects reality.
|
||||
|
||||
List<ClusterComponent> components = new()
|
||||
{
|
||||
BuildComponent("ingress", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["provider"] = "istio",
|
||||
["hasExternalGateway"] = "True"
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("ingress", components);
|
||||
|
||||
// Assert — enable_external should default to "true".
|
||||
|
||||
ConfigurationParameter extParam = enriched.Parameters.First(p => p.Key == "enable_external");
|
||||
extParam.DefaultValue.Should().Be("true");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnrichForCluster_IngressWithoutExternalGateway_DefaultsEnableExternalToFalse()
|
||||
{
|
||||
// Arrange — No external gateway detected. The default should stay false.
|
||||
|
||||
List<ClusterComponent> components = new()
|
||||
{
|
||||
BuildComponent("ingress", ComponentStatus.Installed, new Dictionary<string, string>
|
||||
{
|
||||
["provider"] = "istio",
|
||||
["hasExternalGateway"] = "False"
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
ConfigurationSchema enriched = ComponentSchemas.EnrichForCluster("ingress", components);
|
||||
|
||||
// Assert — enable_external should remain "false".
|
||||
|
||||
ConfigurationParameter extParam = enriched.Parameters.First(p => p.Key == "enable_external");
|
||||
extParam.DefaultValue.Should().Be("false");
|
||||
}
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
private static ClusterComponent BuildComponent(
|
||||
string name, ComponentStatus status, Dictionary<string, string> config)
|
||||
{
|
||||
ComponentCheckResult checkResult = new(
|
||||
name, status, new List<string>(), new List<string>(),
|
||||
new DiscoveredConfiguration(null, null, null, config));
|
||||
|
||||
return ClusterComponent.FromCheckResult(checkResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParameterType_IncludesPostgresCluster()
|
||||
{
|
||||
// The UI needs a PostgresCluster parameter type to render a CNPG cluster
|
||||
// picker, similar to how StorageBucket renders a bucket picker.
|
||||
|
||||
ParameterType type = ParameterType.PostgresCluster;
|
||||
type.Should().BeDefined();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user