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,348 @@
using EntKube.Clusters.Features.AdoptCluster;
using EntKube.Clusters.Features.AdoptCluster.Components.Gitea;
using EntKube.Clusters.Infrastructure.Cleura;
using FluentAssertions;
using k8s.Models;
using Moq;
namespace EntKube.Clusters.Tests.Features;
public class GiteaComponentTests
{
[Fact]
public void GiteaInstaller_HasCorrectComponentName()
{
// The installer's ComponentName must match the check's ComponentName
// so the coordinator can pair them correctly.
GiteaInstaller installer = new(
new Microsoft.Extensions.Logging.Abstractions.NullLogger<GiteaInstaller>(),
new CleuraS3Client(Mock.Of<IHttpClientFactory>()),
Mock.Of<IHttpClientFactory>());
installer.ComponentName.Should().Be("gitea");
}
[Fact]
public void GiteaCheck_HasCorrectComponentName()
{
// The check's ComponentName identifies which component is being evaluated
// during cluster adoption scans.
GiteaCheck check = new(new Microsoft.Extensions.Logging.Abstractions.NullLogger<GiteaCheck>());
check.ComponentName.Should().Be("gitea");
}
[Fact]
public void GiteaCheck_HasDisplayName()
{
// The display name is shown in the UI so users can identify the component.
GiteaCheck check = new(new Microsoft.Extensions.Logging.Abstractions.NullLogger<GiteaCheck>());
check.DisplayName.Should().NotBeNullOrWhiteSpace();
check.DisplayName.Should().Contain("Gitea");
}
[Fact]
public void GiteaSchema_IsRegistered()
{
// The configuration schema must be registered so the UI can render
// typed configuration forms for the Gitea component.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.ComponentName.Should().Be("gitea");
schema.Parameters.Should().NotBeEmpty();
}
[Fact]
public void GiteaSchema_ContainsReplicasParameter()
{
// The replicas parameter controls how many Gitea pods are deployed.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p => p.Key == "replicas");
}
[Fact]
public void GiteaSchema_ContainsActionsParameter()
{
// Gitea Actions (CI/CD) is a key feature that can be toggled.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p => p.Key == "actionsEnabled");
}
[Fact]
public void GiteaSchema_ContainsStorageParameter()
{
// Repository persistence size is configurable for capacity planning.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p => p.Key == "persistenceSize");
}
[Fact]
public void GiteaSchema_ContainsSshParameter()
{
// SSH access can be enabled/disabled depending on security requirements.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p => p.Key == "sshEnabled");
}
[Fact]
public void GiteaInstaller_GetSchema_ReturnsGiteaSchema()
{
// The installer's default GetSchema() implementation should delegate
// to ComponentSchemas and return the gitea schema.
IComponentInstaller installer = new GiteaInstaller(
new Microsoft.Extensions.Logging.Abstractions.NullLogger<GiteaInstaller>(),
new CleuraS3Client(Mock.Of<IHttpClientFactory>()),
Mock.Of<IHttpClientFactory>());
ConfigurationSchema schema = installer.GetSchema();
schema.ComponentName.Should().Be("gitea");
schema.Parameters.Should().NotBeEmpty();
}
[Fact]
public void BuildActRunnerStatefulSet_DefaultReplicas_ReturnsValidStatefulSet()
{
// Arrange & Act
V1StatefulSet sts = GiteaInstaller.BuildActRunnerStatefulSet("gitea", 2, "10Gi");
// Assert
sts.Metadata.Name.Should().Be("act-runner");
sts.Metadata.NamespaceProperty.Should().Be("gitea");
sts.Spec.Replicas.Should().Be(2);
sts.Spec.ServiceName.Should().Be("act-runner");
}
[Fact]
public void BuildActRunnerStatefulSet_UsesCorrectImage()
{
// Arrange & Act
V1StatefulSet sts = GiteaInstaller.BuildActRunnerStatefulSet("gitea", 1, "5Gi");
// Assert
V1Container container = sts.Spec.Template.Spec.Containers[0];
container.Image.Should().Be("gitea/act_runner:latest-dind-rootless");
container.Name.Should().Be("runner");
}
[Fact]
public void BuildActRunnerStatefulSet_SetsEnvironmentVariables()
{
// Arrange & Act
V1StatefulSet sts = GiteaInstaller.BuildActRunnerStatefulSet("gitea", 1, "5Gi");
// Assert — runner needs DOCKER_HOST and GITEA_INSTANCE_URL at minimum
V1Container container = sts.Spec.Template.Spec.Containers[0];
container.Env.Should().Contain(e => e.Name == "DOCKER_HOST");
container.Env.Should().Contain(e => e.Name == "GITEA_INSTANCE_URL");
container.Env.Should().Contain(e => e.Name == "GITEA_RUNNER_REGISTRATION_TOKEN");
}
[Fact]
public void BuildActRunnerStatefulSet_ConfiguresPvcWithRequestedSize()
{
// Arrange & Act
V1StatefulSet sts = GiteaInstaller.BuildActRunnerStatefulSet("gitea", 3, "20Gi");
// Assert
V1PersistentVolumeClaim pvc = sts.Spec.VolumeClaimTemplates[0];
pvc.Metadata.Name.Should().Be("runner-data");
pvc.Spec.Resources.Requests["storage"].ToString().Should().Be("20Gi");
}
[Fact]
public void BuildActRunnerStatefulSet_WithHarborUrl_SetsRegistryMirrorEnv()
{
// Arrange & Act — provide a Harbor registry URL for the runners.
V1StatefulSet sts = GiteaInstaller.BuildActRunnerStatefulSet("gitea", 1, "5Gi", "harbor.example.com");
// Assert — the Docker daemon should be configured to mirror through Harbor.
V1Container container = sts.Spec.Template.Spec.Containers[0];
container.Env.Should().Contain(e =>
e.Name == "DOCKERD_ROOTLESS_FLAGS" &&
e.Value == "--registry-mirror=https://harbor.example.com");
}
[Fact]
public void BuildActRunnerStatefulSet_WithoutHarborUrl_NoMirrorEnv()
{
// Arrange & Act — no Harbor URL, runners pull images directly.
V1StatefulSet sts = GiteaInstaller.BuildActRunnerStatefulSet("gitea", 1, "5Gi");
// Assert — no Docker daemon mirror flag should be present.
V1Container container = sts.Spec.Template.Spec.Containers[0];
container.Env.Should().NotContain(e => e.Name == "DOCKERD_ROOTLESS_FLAGS");
}
[Fact]
public void GiteaSchema_ContainsCnpgClusterNameParameter()
{
// Gitea should reference the shared CNPG cluster for its database.
// The cnpgClusterName parameter tells the installer which CNPG cluster
// to create the gitea database on, instead of requiring manual dbHost.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p =>
p.Key == "cnpgClusterName" && p.Type == ParameterType.PostgresCluster);
}
[Fact]
public void GiteaSchema_ContainsCnpgNamespaceParameter()
{
// The namespace where the CNPG cluster lives — needed to resolve
// the service endpoint ({clusterName}-rw.{namespace}.svc).
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p => p.Key == "cnpgNamespace");
}
[Fact]
public void GiteaSchema_IngressProviderIsSelect()
{
// Ingress provider should be a dropdown selector, not a free-text
// field, so users pick from the supported providers.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
ConfigurationParameter ingressProvider = schema.Parameters.First(p => p.Key == "ingressProvider");
ingressProvider.Type.Should().Be(ParameterType.Select);
ingressProvider.AllowedValues.Should().Contain("traefik");
ingressProvider.AllowedValues.Should().Contain("istio");
ingressProvider.AllowedValues.Should().Contain("gatewayapi");
}
[Fact]
public void GiteaSchema_RedisClusterIsRedisClusterType()
{
// Redis should be a cluster selector dropdown, not a manual host
// textbox. The UI populates it from provisioned Redis clusters.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p =>
p.Key == "redisClusterName" && p.Type == ParameterType.RedisCluster);
}
[Fact]
public void GiteaSchema_DatabaseFieldsDoNotIncludeManualHostUserPassword()
{
// With the CNPG cluster selector, manual dbHost/dbUser/dbPassword
// fields are no longer needed — credentials are auto-generated
// and stored in the secrets vault.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().NotContain(p => p.Key == "dbHost");
schema.Parameters.Should().NotContain(p => p.Key == "dbUser");
schema.Parameters.Should().NotContain(p => p.Key == "dbPassword");
}
[Fact]
public void GiteaSchema_ContainsOidcParameters()
{
// OIDC integration allows Gitea to authenticate users via an external
// identity provider like Keycloak.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p => p.Key == "oidcEnabled" && p.Type == ParameterType.Boolean);
schema.Parameters.Should().Contain(p => p.Key == "oidcDiscoveryUrl");
schema.Parameters.Should().Contain(p => p.Key == "oidcClientId");
schema.Parameters.Should().Contain(p => p.Key == "oidcClientSecret");
}
[Fact]
public void GiteaSchema_OidcFieldsDependOnOidcEnabled()
{
// OIDC-specific fields should only be visible when oidcEnabled is true.
// This keeps the UI clean for installations without OIDC.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
ConfigurationParameter discoveryUrl = schema.Parameters.First(p => p.Key == "oidcDiscoveryUrl");
discoveryUrl.DependsOnKey.Should().Be("oidcEnabled");
discoveryUrl.DependsOnValues.Should().Contain("true");
}
[Fact]
public void GiteaSchema_ObjectStorageCascading()
{
// Object storage fields should cascade based on the objectStorageType
// selection — selecting "bucket" shows the bucket selector, "s3" shows
// manual S3 fields, "azure" shows Azure fields, "none" hides everything.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
ConfigurationParameter storageType = schema.Parameters.First(p => p.Key == "objectStorageType");
storageType.Type.Should().Be(ParameterType.Select);
storageType.AllowedValues.Should().Contain("none");
storageType.AllowedValues.Should().Contain("bucket");
storageType.AllowedValues.Should().Contain("s3");
storageType.AllowedValues.Should().Contain("azure");
ConfigurationParameter bucket = schema.Parameters.First(p => p.Key == "storageBucketId");
bucket.DependsOnKey.Should().Be("objectStorageType");
bucket.DependsOnValues.Should().Contain("bucket");
ConfigurationParameter s3Endpoint = schema.Parameters.First(p => p.Key == "s3Endpoint");
s3Endpoint.DependsOnKey.Should().Be("objectStorageType");
s3Endpoint.DependsOnValues.Should().Contain("s3");
ConfigurationParameter azureName = schema.Parameters.First(p => p.Key == "azureAccountName");
azureName.DependsOnKey.Should().Be("objectStorageType");
azureName.DependsOnValues.Should().Contain("azure");
}
[Fact]
public void GiteaSchema_ContainsVersionParameter()
{
// Users should be able to select which version of Gitea to install
// or upgrade to.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p => p.Key == "version");
}
[Fact]
public void GiteaSchema_ContainsProjectsParameter()
{
// Projects (kanban boards) should be configurable per installation.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
schema.Parameters.Should().Contain(p =>
p.Key == "projectsEnabled" && p.Type == ParameterType.Boolean);
}
[Fact]
public void GiteaSchema_SshPortDependsOnSshEnabled()
{
// SSH port should only be visible when SSH is enabled.
ConfigurationSchema schema = ComponentSchemas.GetSchema("gitea");
ConfigurationParameter sshPort = schema.Parameters.First(p => p.Key == "sshPort");
sshPort.DependsOnKey.Should().Be("sshEnabled");
sshPort.DependsOnValues.Should().Contain("true");
}
}