using EntKube.Provisioning.Features.MinioTenants; using FluentAssertions; namespace EntKube.Provisioning.Tests.Features; /// /// Tests for parsing MinIO tenant credentials from the various secret formats /// used by different MinIO Operator versions. The operator has changed its /// credential storage format over time: /// /// - Legacy (v4): separate "accesskey" and "secretkey" keys in the K8s secret /// - v5+: a single "config.env" key containing export statements /// - Console: "CONSOLE_ACCESS_KEY" and "CONSOLE_SECRET_KEY" keys /// /// The credential parser must handle all formats so mc commands can authenticate /// against tenants provisioned by any operator version. /// public class MinioTenantCredentialParsingTests { // ─── config.env format (MinIO Operator v5+) ──────────────────────────── [Fact] public void ParseConfigEnv_WithQuotedValues_ExtractsCredentials() { // The most common format — double-quoted values with export prefix. string configEnv = """ export MINIO_ROOT_USER="minio-admin" export MINIO_ROOT_PASSWORD="super-secret-password" """; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().NotBeNull(); result!.Value.accessKey.Should().Be("minio-admin"); result!.Value.secretKey.Should().Be("super-secret-password"); } [Fact] public void ParseConfigEnv_WithSingleQuotedValues_ExtractsCredentials() { // Some installations use single quotes. string configEnv = """ export MINIO_ROOT_USER='admin' export MINIO_ROOT_PASSWORD='p@ssw0rd' """; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().NotBeNull(); result!.Value.accessKey.Should().Be("admin"); result!.Value.secretKey.Should().Be("p@ssw0rd"); } [Fact] public void ParseConfigEnv_WithUnquotedValues_ExtractsCredentials() { // Unquoted values — less common but valid. string configEnv = """ export MINIO_ROOT_USER=minio export MINIO_ROOT_PASSWORD=minio123 """; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().NotBeNull(); result!.Value.accessKey.Should().Be("minio"); result!.Value.secretKey.Should().Be("minio123"); } [Fact] public void ParseConfigEnv_WithAdditionalEnvVars_IgnoresExtras() { // The config.env may contain other env vars beyond credentials. // We should extract only the root user/password. string configEnv = """ export MINIO_ROOT_USER="minio" export MINIO_ROOT_PASSWORD="secret" export MINIO_BROWSER="on" export MINIO_STORAGE_CLASS_STANDARD="EC:2" """; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().NotBeNull(); result!.Value.accessKey.Should().Be("minio"); result!.Value.secretKey.Should().Be("secret"); } [Fact] public void ParseConfigEnv_MissingUser_ReturnsNull() { // If the user key is missing, we can't authenticate. string configEnv = """ export MINIO_ROOT_PASSWORD="secret" """; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().BeNull(); } [Fact] public void ParseConfigEnv_MissingPassword_ReturnsNull() { // If the password key is missing, we can't authenticate. string configEnv = """ export MINIO_ROOT_USER="admin" """; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().BeNull(); } [Fact] public void ParseConfigEnv_EmptyString_ReturnsNull() { // An empty config.env should not crash — just return null. (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(""); result.Should().BeNull(); } [Fact] public void ParseConfigEnv_WithWhitespaceAndEmptyLines_ExtractsCredentials() { // Real config files often have trailing newlines and inconsistent whitespace. string configEnv = "\n export MINIO_ROOT_USER=\"root-user\" \n\n export MINIO_ROOT_PASSWORD=\"root-pass\" \n\n"; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().NotBeNull(); result!.Value.accessKey.Should().Be("root-user"); result!.Value.secretKey.Should().Be("root-pass"); } [Fact] public void ParseConfigEnv_SingleLine_ExtractsCredentials() { // Some environments produce config.env as a single line with all exports // separated by spaces. This happens with MinIO Operator v6 in some configurations. string configEnv = """export MINIO_ROOT_USER="minio-admin" export MINIO_ROOT_PASSWORD="sXmLAnMLX3yGoDrKOxUF1C992FWfDBQjVh6HJJU7" export MINIO_BROWSER="on" export MINIO_PROMETHEUS_AUTH_TYPE="public" """; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().NotBeNull(); result!.Value.accessKey.Should().Be("minio-admin"); result!.Value.secretKey.Should().Be("sXmLAnMLX3yGoDrKOxUF1C992FWfDBQjVh6HJJU7"); } [Fact] public void ParseConfigEnv_RealWorldV6Format_ExtractsCredentials() { // Exact format from a real MinIO Operator v6 installation. string configEnv = "export MINIO_ROOT_USER=\"minio-admin\"\nexport MINIO_ROOT_PASSWORD=\"sXmLAnMLX3yGoDrKOxUF1C992FWfDBQjVh6HJJU7\"\nexport MINIO_BROWSER=\"on\"\nexport MINIO_PROMETHEUS_AUTH_TYPE=\"public\"\n"; // Act (string accessKey, string secretKey)? result = KubernetesMinioTenantClient.ParseConfigEnvCredentials(configEnv); // Assert result.Should().NotBeNull(); result!.Value.accessKey.Should().Be("minio-admin"); result!.Value.secretKey.Should().Be("sXmLAnMLX3yGoDrKOxUF1C992FWfDBQjVh6HJJU7"); } }