181 lines
6.7 KiB
C#
181 lines
6.7 KiB
C#
using EntKube.Provisioning.Domain;
|
|
using FluentAssertions;
|
|
|
|
namespace EntKube.Provisioning.Tests.Domain;
|
|
|
|
public class AppEnvironmentTests
|
|
{
|
|
// ─── ConfigureDeployment ─────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void ConfigureDeployment_WithValidSpec_SetsDeploymentSpec()
|
|
{
|
|
// Arrange — After adding an environment, the admin configures
|
|
// the deployment details: which container image, how many replicas,
|
|
// port mappings, and routing. This is the equivalent of writing
|
|
// deployment.yaml + service.yaml + httproute.yaml by hand.
|
|
|
|
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment);
|
|
AppEnvironment env = app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "api-dev");
|
|
|
|
DeploymentSpec spec = new(
|
|
Image: "registry.example.com/api",
|
|
Tag: "v1.2.3",
|
|
Replicas: 2,
|
|
ContainerPort: 8080,
|
|
ServicePort: 80,
|
|
HostName: "api.dev.example.com",
|
|
PathPrefix: "/",
|
|
EnvironmentVariables: new Dictionary<string, string> { ["ASPNETCORE_ENVIRONMENT"] = "Development" },
|
|
Resources: new ResourceSpec("100m", "500m", "128Mi", "512Mi"));
|
|
|
|
// Act
|
|
|
|
env.ConfigureDeployment(spec);
|
|
|
|
// Assert
|
|
|
|
env.DeploymentSpec.Should().NotBeNull();
|
|
env.DeploymentSpec!.Image.Should().Be("registry.example.com/api");
|
|
env.DeploymentSpec.Tag.Should().Be("v1.2.3");
|
|
env.DeploymentSpec.Replicas.Should().Be(2);
|
|
env.DeploymentSpec.ContainerPort.Should().Be(8080);
|
|
env.DeploymentSpec.ServicePort.Should().Be(80);
|
|
env.DeploymentSpec.HostName.Should().Be("api.dev.example.com");
|
|
env.DeploymentSpec.Resources.Should().NotBeNull();
|
|
env.SyncStatus.Should().Be(AppSyncStatus.Pending, "changing config resets sync to pending");
|
|
}
|
|
|
|
// ─── ConfigureHelmRelease ────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void ConfigureHelmRelease_WithValidSpec_SetsHelmSpec()
|
|
{
|
|
// Arrange — For a Helm-type app, the admin points to a Helm chart
|
|
// repository, selects the chart and version, and provides a values.yaml.
|
|
// This is the equivalent of `helm install` with custom values.
|
|
|
|
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "Redis", "redis", AppType.HelmChart);
|
|
AppEnvironment env = app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "redis-dev");
|
|
|
|
HelmReleaseSpec spec = new(
|
|
RepoUrl: "https://charts.bitnami.com/bitnami",
|
|
ChartName: "redis",
|
|
ChartVersion: "18.6.1",
|
|
ValuesYaml: "replica:\n replicaCount: 3");
|
|
|
|
// Act
|
|
|
|
env.ConfigureHelmRelease(spec);
|
|
|
|
// Assert
|
|
|
|
env.HelmReleaseSpec.Should().NotBeNull();
|
|
env.HelmReleaseSpec!.RepoUrl.Should().Be("https://charts.bitnami.com/bitnami");
|
|
env.HelmReleaseSpec.ChartName.Should().Be("redis");
|
|
env.HelmReleaseSpec.ChartVersion.Should().Be("18.6.1");
|
|
env.HelmReleaseSpec.ValuesYaml.Should().Contain("replicaCount: 3");
|
|
env.SyncStatus.Should().Be(AppSyncStatus.Pending);
|
|
}
|
|
|
|
// ─── Secrets ─────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void AddSecret_WithValidInputs_AddsSecretReference()
|
|
{
|
|
// Arrange — Secrets for an app environment are stored in the vault.
|
|
// Here we add a reference that maps a Kubernetes secret key name
|
|
// to the vault path where the actual secret value lives.
|
|
|
|
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment);
|
|
AppEnvironment env = app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "api-dev");
|
|
|
|
// Act
|
|
|
|
env.AddSecret("DATABASE_URL", "database-connection-string");
|
|
|
|
// Assert
|
|
|
|
env.Secrets.Should().HaveCount(1);
|
|
env.Secrets[0].Name.Should().Be("DATABASE_URL");
|
|
env.Secrets[0].VaultKey.Should().Be("database-connection-string");
|
|
env.SyncStatus.Should().Be(AppSyncStatus.Pending, "adding a secret resets sync");
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSecret_DuplicateName_ThrowsInvalidOperation()
|
|
{
|
|
// Arrange — Each secret name must be unique within an environment.
|
|
|
|
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment);
|
|
AppEnvironment env = app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "api-dev");
|
|
env.AddSecret("DATABASE_URL", "db-conn-string");
|
|
|
|
// Act
|
|
|
|
Action act = () => env.AddSecret("DATABASE_URL", "another-path");
|
|
|
|
// Assert
|
|
|
|
act.Should().Throw<InvalidOperationException>()
|
|
.WithMessage("*already exists*");
|
|
}
|
|
|
|
[Fact]
|
|
public void RemoveSecret_ExistingSecret_RemovesIt()
|
|
{
|
|
// Arrange
|
|
|
|
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment);
|
|
AppEnvironment env = app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "api-dev");
|
|
env.AddSecret("DATABASE_URL", "db-conn-string");
|
|
|
|
// Act
|
|
|
|
env.RemoveSecret("DATABASE_URL");
|
|
|
|
// Assert
|
|
|
|
env.Secrets.Should().BeEmpty();
|
|
env.SyncStatus.Should().Be(AppSyncStatus.Pending);
|
|
}
|
|
|
|
// ─── Sync Status ─────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void MarkSynced_UpdatesSyncStatus()
|
|
{
|
|
// Arrange — The reconciler successfully deployed the app.
|
|
|
|
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment);
|
|
AppEnvironment env = app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "api-dev");
|
|
|
|
// Act
|
|
|
|
env.MarkSynced();
|
|
|
|
// Assert
|
|
|
|
env.SyncStatus.Should().Be(AppSyncStatus.Synced);
|
|
env.LastSyncAt.Should().NotBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkError_SetsSyncStatusAndMessage()
|
|
{
|
|
// Arrange — Something went wrong during deployment.
|
|
|
|
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment);
|
|
AppEnvironment env = app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "api-dev");
|
|
|
|
// Act
|
|
|
|
env.MarkError("ImagePullBackOff: registry.example.com/api:v1.2.3");
|
|
|
|
// Assert
|
|
|
|
env.SyncStatus.Should().Be(AppSyncStatus.Error);
|
|
env.SyncStatusMessage.Should().Contain("ImagePullBackOff");
|
|
}
|
|
}
|