Files
Entkube/tests/EntKube.Secrets.Tests/Domain/SecretScopeTests.cs
2026-05-13 14:01:32 +02:00

233 lines
7.3 KiB
C#

using EntKube.Secrets.Domain;
using FluentAssertions;
namespace EntKube.Secrets.Tests.Domain;
public class SecretScopeTests
{
// ─── Factory Method Tests ────────────────────────────────────────────
[Fact]
public void ForCluster_CreatesInfrastructureScope()
{
// Arrange — Infrastructure secrets are scoped to Tenant + Environment + Cluster.
// These are things like kubeconfig credentials, DNS tokens, cloud provider secrets.
Guid tenantId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
Guid clusterId = Guid.NewGuid();
// Act
SecretScope scope = SecretScope.ForCluster(tenantId, environmentId, clusterId);
// Assert
scope.TenantId.Should().Be(tenantId);
scope.EnvironmentId.Should().Be(environmentId);
scope.ClusterId.Should().Be(clusterId);
scope.CustomerId.Should().BeNull();
scope.AppName.Should().BeNull();
}
[Fact]
public void ForApp_CreatesApplicationScope()
{
// Arrange — Application secrets are scoped to Tenant + Customer + App + Environment.
// These are things like database connection strings, API keys for third-party services.
Guid tenantId = Guid.NewGuid();
Guid customerId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
// Act
SecretScope scope = SecretScope.ForApp(tenantId, customerId, "my-api", environmentId);
// Assert
scope.TenantId.Should().Be(tenantId);
scope.EnvironmentId.Should().Be(environmentId);
scope.ClusterId.Should().BeNull();
scope.CustomerId.Should().Be(customerId);
scope.AppName.Should().Be("my-api");
}
// ─── Record Construction Tests ───────────────────────────────────────
[Fact]
public void Create_WithAllFields_StoresAllValues()
{
// Arrange — A fully-scoped secret with all fields populated.
Guid tenantId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
Guid clusterId = Guid.NewGuid();
Guid customerId = Guid.NewGuid();
// Act
SecretScope scope = new(tenantId, environmentId, clusterId, customerId, "my-api");
// Assert
scope.TenantId.Should().Be(tenantId);
scope.EnvironmentId.Should().Be(environmentId);
scope.ClusterId.Should().Be(clusterId);
scope.CustomerId.Should().Be(customerId);
scope.AppName.Should().Be("my-api");
}
[Fact]
public void Create_WithTenantOnly_AllowsNullOptionalFields()
{
// Arrange & Act — A tenant-wide secret (e.g. a shared API key)
// that isn't scoped to a specific environment, cluster, customer, or app.
Guid tenantId = Guid.NewGuid();
SecretScope scope = new(tenantId, null, null, null, null);
// Assert
scope.TenantId.Should().Be(tenantId);
scope.EnvironmentId.Should().BeNull();
scope.ClusterId.Should().BeNull();
scope.CustomerId.Should().BeNull();
scope.AppName.Should().BeNull();
}
[Fact]
public void Create_WithTenantAndEnvironment_ScopesToEnvironment()
{
// Arrange & Act — A secret scoped to a tenant + environment
// (e.g. a dev database password shared across all customers in dev).
Guid tenantId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
SecretScope scope = new(tenantId, environmentId, null, null, null);
// Assert
scope.TenantId.Should().Be(tenantId);
scope.EnvironmentId.Should().Be(environmentId);
scope.ClusterId.Should().BeNull();
scope.CustomerId.Should().BeNull();
scope.AppName.Should().BeNull();
}
// ─── BuildPath Tests ─────────────────────────────────────────────────
[Fact]
public void BuildPath_TenantOnly_ReturnsPathWithTenantPrefix()
{
// Arrange — A tenant-scoped secret should have a path like
// "tenants/{tenantId}/secrets/my-key".
Guid tenantId = Guid.NewGuid();
SecretScope scope = new(tenantId, null, null, null, null);
// Act
string path = scope.BuildPath("my-key");
// Assert
path.Should().Be($"tenants/{tenantId}/secrets/my-key");
}
[Fact]
public void BuildPath_TenantAndEnvironment_ReturnsEnvironmentScopedPath()
{
// Arrange
Guid tenantId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
SecretScope scope = new(tenantId, environmentId, null, null, null);
// Act
string path = scope.BuildPath("db-password");
// Assert
path.Should().Be($"tenants/{tenantId}/environments/{environmentId}/secrets/db-password");
}
[Fact]
public void BuildPath_ClusterScope_ReturnsClusterScopedPath()
{
// Arrange — Infrastructure secret scoped to a specific cluster.
Guid tenantId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
Guid clusterId = Guid.NewGuid();
SecretScope scope = SecretScope.ForCluster(tenantId, environmentId, clusterId);
// Act
string path = scope.BuildPath("dns-credential");
// Assert — Path includes the cluster segment.
path.Should().Be(
$"tenants/{tenantId}/environments/{environmentId}/clusters/{clusterId}/secrets/dns-credential");
}
[Fact]
public void BuildPath_AppScope_ReturnsFullAppScopedPath()
{
// Arrange — Application secret: tenant → environment → customer → app.
Guid tenantId = Guid.NewGuid();
Guid customerId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
SecretScope scope = SecretScope.ForApp(tenantId, customerId, "extract01", environmentId);
// Act
string path = scope.BuildPath("api-token");
// Assert
path.Should().Be(
$"tenants/{tenantId}/environments/{environmentId}/customers/{customerId}/apps/extract01/secrets/api-token");
}
[Fact]
public void BuildPath_CustomerWithoutApp_IncludesCustomerScope()
{
// Arrange — Customer-scoped without a specific app.
Guid tenantId = Guid.NewGuid();
Guid environmentId = Guid.NewGuid();
Guid customerId = Guid.NewGuid();
SecretScope scope = new(tenantId, environmentId, null, customerId, null);
// Act
string path = scope.BuildPath("shared-token");
// Assert
path.Should().Be(
$"tenants/{tenantId}/environments/{environmentId}/customers/{customerId}/secrets/shared-token");
}
// ─── Equality ────────────────────────────────────────────────────────
[Fact]
public void Equality_SameValues_AreEqual()
{
// Arrange — Records should have value-based equality.
Guid tenantId = Guid.NewGuid();
Guid envId = Guid.NewGuid();
SecretScope scope1 = new(tenantId, envId, null, null, null);
SecretScope scope2 = new(tenantId, envId, null, null, null);
// Assert
scope1.Should().Be(scope2);
}
}