349 lines
13 KiB
C#
349 lines
13 KiB
C#
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");
|
|
}
|
|
}
|