Increase secrets unit coverage (#7545)

This commit is contained in:
Sipke Schoorstra 2026-05-30 08:12:12 +02:00 committed by GitHub
parent 5245599131
commit 67ded31cba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 141 additions and 0 deletions

View file

@ -0,0 +1,32 @@
using Elsa.Secrets.Models;
using Elsa.Secrets.Services;
using Xunit;
namespace Elsa.Secrets.UnitTests;
public class SecretModelMapperTests
{
[Fact]
public void ToModel_ReportsExpired_WhenActiveSecretHasOnlyExpiredVersions()
{
var secret = new Secret
{
Name = "smtp:password",
DisplayName = "SMTP password",
Versions =
{
new SecretVersion
{
Version = 1,
ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(-1)
}
}
};
var model = secret.ToModel();
Assert.Equal(SecretStatus.Expired, model.Status);
Assert.Null(model.CurrentVersion);
Assert.Null(model.ExpiresAt);
}
}

View file

@ -40,6 +40,51 @@ public class SecretStoreTests
Assert.Equal("configured-secret", value);
}
[Fact]
public async Task ConfigurationStore_FallsBackToRootConfigurationKey()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?> { ["SmtpPassword"] = "root-configured-secret" })
.Build();
var fixture = new SecretTestFixture(configuration);
var secret = await fixture.Manager.CreateAsync(new CreateSecretRequest
{
Name = "smtp:password",
StoreName = SecretStoreNames.Configuration,
ConfigurationKey = " SmtpPassword "
});
var value = await fixture.Resolver.ResolveAsync("smtp:password");
Assert.Equal("root-configured-secret", value);
Assert.Null(secret.Versions.Single().Payload.Value);
Assert.Equal("SmtpPassword", secret.Versions.Single().Payload.Metadata["configurationKey"]);
}
[Fact]
public async Task ConfigurationStore_RotateAsync_UsesReplacementConfigurationKey()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Elsa:Secrets:OldPassword"] = "old-configured-secret",
["Elsa:Secrets:NewPassword"] = "new-configured-secret"
})
.Build();
var fixture = new SecretTestFixture(configuration);
await fixture.Manager.CreateAsync(new CreateSecretRequest
{
Name = "smtp:password",
StoreName = SecretStoreNames.Configuration,
ConfigurationKey = "OldPassword"
});
await fixture.Manager.RotateAsync("smtp:password", new RotateSecretRequest { ConfigurationKey = "NewPassword" });
var value = await fixture.Resolver.ResolveAsync("smtp:password");
Assert.Equal("new-configured-secret", value);
}
[Fact]
public async Task ConfigurationStore_TestAsync_ReturnsFalseWhenConfiguredValueIsMissing()
{
@ -57,6 +102,18 @@ public class SecretStoreTests
Assert.Equal("Secret value is unavailable.", result.Error);
}
[Fact]
public void Registries_Throw_WhenTypeOrStoreIsMissing()
{
var fixture = new SecretTestFixture();
var missingType = Assert.Throws<InvalidOperationException>(() => fixture.TypeRegistry.Get("missing-type"));
var missingStore = Assert.Throws<InvalidOperationException>(() => fixture.StoreRegistry.Get("missing-store"));
Assert.Contains("missing-type", missingType.Message);
Assert.Contains("missing-store", missingStore.Message);
}
[Fact]
public async Task FileRepository_PersistsSecretAggregate()
{

View file

@ -0,0 +1,52 @@
using Elsa.Secrets.Options;
using Elsa.Secrets.Services;
using Xunit;
namespace Elsa.Secrets.UnitTests;
public class SecretValueProtectorTests
{
[Fact]
public void Protect_AndUnprotect_RoundTripsValue()
{
var protector = CreateProtector("0123456789abcdef0123456789abcdef"u8.ToArray());
var protectedValue = protector.Protect("plain-text-secret");
var value = protector.Unprotect(protectedValue);
Assert.NotEqual("plain-text-secret", protectedValue);
Assert.Equal("plain-text-secret", value);
}
[Theory]
[InlineData(null)]
[InlineData(0)]
[InlineData(15)]
[InlineData(17)]
public void Protect_RejectsMissingOrInvalidEncryptionKey(int? keyLength)
{
var protector = CreateProtector(keyLength == null ? null : new byte[keyLength.Value]);
var exception = Assert.Throws<InvalidOperationException>(() => protector.Protect("secret"));
Assert.Contains("encryption key", exception.Message, StringComparison.OrdinalIgnoreCase);
}
[Theory]
[InlineData("")]
[InlineData("v2.payload")]
[InlineData("v1.payload")]
public void Unprotect_RejectsUnsupportedPayloadFormat(string protectedValue)
{
var protector = CreateProtector("0123456789abcdef0123456789abcdef"u8.ToArray());
var exception = Assert.Throws<InvalidOperationException>(() => protector.Unprotect(protectedValue));
Assert.Contains("not supported", exception.Message, StringComparison.OrdinalIgnoreCase);
}
private static DefaultSecretValueProtector CreateProtector(byte[]? key)
{
return new DefaultSecretValueProtector(Microsoft.Extensions.Options.Options.Create(new SecretsOptions { EncryptionKey = key }));
}
}