too much for one commit

This commit is contained in:
Nils Blomgren
2026-05-13 14:01:32 +02:00
parent a96dd33039
commit 328d494530
394 changed files with 98104 additions and 78 deletions

View File

@@ -0,0 +1,229 @@
using EntKube.Provisioning.Domain;
using FluentAssertions;
namespace EntKube.Provisioning.Tests.Domain;
public class AppTests
{
// ─── Create ──────────────────────────────────────────────────────────
[Fact]
public void Create_WithValidInputs_CreatesActiveApp()
{
// Arrange — A customer admin creates a new app (e.g. "frontend-portal")
// that will eventually be deployed across environments. The app starts
// in an Active state and is ready to have environments added.
Guid tenantId = Guid.NewGuid();
Guid customerId = Guid.NewGuid();
// Act
App app = App.Create(tenantId, customerId, "Frontend Portal", "frontend-portal", AppType.Deployment);
// Assert — The app should be active and linked to its customer and tenant.
app.Id.Should().NotBe(Guid.Empty);
app.TenantId.Should().Be(tenantId);
app.CustomerId.Should().Be(customerId);
app.Name.Should().Be("Frontend Portal");
app.Slug.Should().Be("frontend-portal");
app.Type.Should().Be(AppType.Deployment);
app.Status.Should().Be(AppStatus.Active);
app.CreatedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
app.Environments.Should().BeEmpty();
}
[Fact]
public void Create_NormalizesSlugToLowerCase()
{
// Arrange & Act — Slugs should always be lowercase for consistency.
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "My App", "MY-APP", AppType.HelmChart);
// Assert
app.Slug.Should().Be("my-app");
}
[Fact]
public void Create_WithEmptyTenantId_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => App.Create(Guid.Empty, Guid.NewGuid(), "App", "app", AppType.Deployment);
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("tenantId");
}
[Fact]
public void Create_WithEmptyCustomerId_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => App.Create(Guid.NewGuid(), Guid.Empty, "App", "app", AppType.Deployment);
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("customerId");
}
[Fact]
public void Create_WithEmptyName_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => App.Create(Guid.NewGuid(), Guid.NewGuid(), "", "app", AppType.Deployment);
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("name");
}
[Fact]
public void Create_WithEmptySlug_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => App.Create(Guid.NewGuid(), Guid.NewGuid(), "App", "", AppType.Deployment);
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("slug");
}
// ─── Suspend / Activate ──────────────────────────────────────────────
[Fact]
public void Suspend_SetsStatusToSuspended()
{
// Arrange — An active app that the admin wants to pause deployments for.
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "App", "app", AppType.Deployment);
// Act
app.Suspend();
// Assert
app.Status.Should().Be(AppStatus.Suspended);
}
[Fact]
public void Activate_SetsStatusToActive()
{
// Arrange — A previously suspended app being re-enabled.
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "App", "app", AppType.Deployment);
app.Suspend();
// Act
app.Activate();
// Assert
app.Status.Should().Be(AppStatus.Active);
}
// ─── AddEnvironment ──────────────────────────────────────────────────
[Fact]
public void AddEnvironment_WithValidInputs_CreatesAppEnvironment()
{
// Arrange — After creating an app, the admin adds it to a specific
// environment (e.g. "dev"). This links the app to a cluster and
// namespace where it will be deployed.
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API Service", "api-service", AppType.Deployment);
Guid environmentId = Guid.NewGuid();
Guid clusterId = Guid.NewGuid();
// Act
AppEnvironment env = app.AddEnvironment(environmentId, clusterId, "api-dev");
// Assert — The environment should be created in Pending state.
env.Id.Should().NotBe(Guid.Empty);
env.EnvironmentId.Should().Be(environmentId);
env.ClusterId.Should().Be(clusterId);
env.Namespace.Should().Be("api-dev");
env.SyncStatus.Should().Be(AppSyncStatus.Pending);
app.Environments.Should().HaveCount(1);
}
[Fact]
public void AddEnvironment_DuplicateEnvironmentId_ThrowsInvalidOperation()
{
// Arrange — An app can only be deployed to each environment once.
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "App", "app", AppType.Deployment);
Guid environmentId = Guid.NewGuid();
app.AddEnvironment(environmentId, Guid.NewGuid(), "app-dev");
// Act
Action act = () => app.AddEnvironment(environmentId, Guid.NewGuid(), "app-dev-2");
// Assert
act.Should().Throw<InvalidOperationException>()
.WithMessage("*already*");
}
[Fact]
public void AddEnvironment_WithEmptyNamespace_ThrowsArgumentException()
{
// Arrange & Act
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "App", "app", AppType.Deployment);
Action act = () => app.AddEnvironment(Guid.NewGuid(), Guid.NewGuid(), "");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("ns");
}
// ─── RemoveEnvironment ───────────────────────────────────────────────
[Fact]
public void RemoveEnvironment_ExistingEnvironment_RemovesIt()
{
// Arrange — An app deployed to dev that the admin wants to remove.
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "App", "app", AppType.Deployment);
Guid environmentId = Guid.NewGuid();
app.AddEnvironment(environmentId, Guid.NewGuid(), "app-dev");
// Act
app.RemoveEnvironment(environmentId);
// Assert
app.Environments.Should().BeEmpty();
}
[Fact]
public void RemoveEnvironment_NonExistent_ThrowsInvalidOperation()
{
// Arrange & Act
App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "App", "app", AppType.Deployment);
Action act = () => app.RemoveEnvironment(Guid.NewGuid());
// Assert
act.Should().Throw<InvalidOperationException>()
.WithMessage("*not found*");
}
}