230 lines
7.0 KiB
C#
230 lines
7.0 KiB
C#
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*");
|
|
}
|
|
}
|