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,116 @@
using EntKube.Identity.Domain;
using FluentAssertions;
namespace EntKube.Identity.Tests.Domain;
public class CustomerTests
{
[Fact]
public void Create_WithValidInputs_CreatesActiveCustomer()
{
// Arrange — A tenant admin onboards a new customer (team/organization)
// within their tenant. In the terraform reference, these are the "tenants"
// like capioDA, capioonline, volvat — each representing a team that gets
// its own namespaces, quotas, and app deployments per environment.
Guid tenantId = Guid.NewGuid();
// Act
Customer customer = Customer.Create(tenantId, "Capio Data Analytics", "capioda");
// Assert — The customer should be active and linked to its tenant.
customer.Id.Should().NotBe(Guid.Empty);
customer.TenantId.Should().Be(tenantId);
customer.Name.Should().Be("Capio Data Analytics");
customer.Slug.Should().Be("capioda");
customer.Status.Should().Be(CustomerStatus.Active);
customer.CreatedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
}
[Fact]
public void Create_NormalizesSlugToLowerCase()
{
// Arrange & Act — Slugs should be lowercase for consistent lookups.
Customer customer = Customer.Create(Guid.NewGuid(), "Volvat", "VOLVAT");
// Assert
customer.Slug.Should().Be("volvat");
}
[Fact]
public void Create_WithEmptyName_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => Customer.Create(Guid.NewGuid(), "", "slug");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("name");
}
[Fact]
public void Create_WithEmptySlug_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => Customer.Create(Guid.NewGuid(), "Some Customer", "");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("slug");
}
[Fact]
public void Create_WithEmptyTenantId_ThrowsArgumentException()
{
// Arrange & Act — Every customer must belong to a tenant.
Action act = () => Customer.Create(Guid.Empty, "Some Customer", "slug");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("tenantId");
}
[Fact]
public void Suspend_SetsStatusToSuspended()
{
// Arrange — An active customer that the admin wants to temporarily disable
// (e.g. during a billing dispute or compliance review).
Customer customer = Customer.Create(Guid.NewGuid(), "Volvat", "volvat");
// Act
customer.Suspend();
// Assert
customer.Status.Should().Be(CustomerStatus.Suspended);
}
[Fact]
public void Activate_SetsStatusToActive()
{
// Arrange — A previously suspended customer being re-enabled.
Customer customer = Customer.Create(Guid.NewGuid(), "Volvat", "volvat");
customer.Suspend();
// Act
customer.Activate();
// Assert
customer.Status.Should().Be(CustomerStatus.Active);
}
}

View File

@@ -0,0 +1,115 @@
using EntKube.Identity.Domain;
using FluentAssertions;
namespace EntKube.Identity.Tests.Domain;
public class EnvironmentTests
{
[Fact]
public void Create_WithValidInputs_CreatesActiveEnvironment()
{
// Arrange — A tenant admin creates a new environment (e.g. "dev")
// for their organization. The environment starts in an Active state
// and is ready to have clusters assigned to it.
Guid tenantId = Guid.NewGuid();
// Act
TenantEnvironment env = TenantEnvironment.Create(tenantId, "Development", "dev");
// Assert — The environment should be active and linked to its tenant.
env.Id.Should().NotBe(Guid.Empty);
env.TenantId.Should().Be(tenantId);
env.Name.Should().Be("Development");
env.Slug.Should().Be("dev");
env.Status.Should().Be(EnvironmentStatus.Active);
env.CreatedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
}
[Fact]
public void Create_NormalizesSlugToLowerCase()
{
// Arrange & Act — Slugs should always be lowercase for consistent lookups.
TenantEnvironment env = TenantEnvironment.Create(Guid.NewGuid(), "Production", "PROD");
// Assert
env.Slug.Should().Be("prod");
}
[Fact]
public void Create_WithEmptyName_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => TenantEnvironment.Create(Guid.NewGuid(), "", "dev");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("name");
}
[Fact]
public void Create_WithEmptySlug_ThrowsArgumentException()
{
// Arrange & Act
Action act = () => TenantEnvironment.Create(Guid.NewGuid(), "Development", "");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("slug");
}
[Fact]
public void Create_WithEmptyTenantId_ThrowsArgumentException()
{
// Arrange & Act — Every environment must belong to a tenant.
Action act = () => TenantEnvironment.Create(Guid.Empty, "Development", "dev");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("tenantId");
}
[Fact]
public void Deactivate_SetsStatusToInactive()
{
// Arrange — An active environment that the admin wants to disable
// (e.g. decommissioning a staging environment).
TenantEnvironment env = TenantEnvironment.Create(Guid.NewGuid(), "Staging", "staging");
// Act
env.Deactivate();
// Assert
env.Status.Should().Be(EnvironmentStatus.Inactive);
}
[Fact]
public void Activate_SetsStatusToActive()
{
// Arrange — A previously deactivated environment being re-enabled.
TenantEnvironment env = TenantEnvironment.Create(Guid.NewGuid(), "Staging", "staging");
env.Deactivate();
// Act
env.Activate();
// Assert
env.Status.Should().Be(EnvironmentStatus.Active);
}
}

View File

@@ -0,0 +1,50 @@
using EntKube.Identity.Domain;
using EntKube.Identity.Features.GetTenants;
using EntKube.Identity.Infrastructure;
using FluentAssertions;
namespace EntKube.Identity.Tests.Features;
public class GetTenantsHandlerTests
{
private readonly InMemoryTenantRepository repository;
public GetTenantsHandlerTests()
{
repository = new InMemoryTenantRepository();
}
[Fact]
public async Task GetAllAsync_WithTenants_ReturnsList()
{
// Arrange — Two tenants exist in the repository.
Tenant tenant1 = Tenant.Create("Acme Corp", "acme", Guid.NewGuid());
Tenant tenant2 = Tenant.Create("Globex", "globex", Guid.NewGuid());
await repository.AddAsync(tenant1);
await repository.AddAsync(tenant2);
// Act — Retrieve all tenants via the repository (endpoint uses it directly).
IReadOnlyList<Tenant> tenants = await repository.GetAllAsync();
// Assert
tenants.Should().HaveCount(2);
tenants.Should().Contain(t => t.Name == "Acme Corp");
tenants.Should().Contain(t => t.Name == "Globex");
}
[Fact]
public async Task GetAllAsync_Empty_ReturnsEmptyList()
{
// Act
IReadOnlyList<Tenant> tenants = await repository.GetAllAsync();
// Assert
tenants.Should().BeEmpty();
}
}