Refactor code structure and optimize performance across multiple modules
This commit is contained in:
@@ -3,11 +3,13 @@ using System.Net;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Abstractions.Tests;
|
||||
|
||||
public class NetworkMaskMatcherTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_SingleAddress_YieldsHostMask()
|
||||
{
|
||||
var mask = NetworkMask.Parse("192.168.1.42");
|
||||
@@ -17,7 +19,8 @@ public class NetworkMaskMatcherTests
|
||||
Assert.False(mask.Contains(IPAddress.Parse("192.168.1.43")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_Cidr_NormalisesHostBits()
|
||||
{
|
||||
var mask = NetworkMask.Parse("10.0.15.9/20");
|
||||
@@ -27,7 +30,8 @@ public class NetworkMaskMatcherTests
|
||||
Assert.False(mask.Contains(IPAddress.Parse("10.0.32.1")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Contains_ReturnsFalse_ForMismatchedAddressFamily()
|
||||
{
|
||||
var mask = NetworkMask.Parse("192.168.0.0/16");
|
||||
@@ -35,7 +39,8 @@ public class NetworkMaskMatcherTests
|
||||
Assert.False(mask.Contains(IPAddress.IPv6Loopback));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Matcher_AllowsAll_WhenStarProvided()
|
||||
{
|
||||
var matcher = new NetworkMaskMatcher(new[] { "*" });
|
||||
@@ -45,7 +50,8 @@ public class NetworkMaskMatcherTests
|
||||
Assert.True(matcher.IsAllowed(IPAddress.IPv6Loopback));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Matcher_ReturnsFalse_WhenNoMasksConfigured()
|
||||
{
|
||||
var matcher = new NetworkMaskMatcher(Array.Empty<string>());
|
||||
@@ -55,7 +61,8 @@ public class NetworkMaskMatcherTests
|
||||
Assert.False(matcher.IsAllowed(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Matcher_SupportsIpv4AndIpv6Masks()
|
||||
{
|
||||
var matcher = new NetworkMaskMatcher(new[] { "192.168.0.0/24", "::1/128" });
|
||||
@@ -66,7 +73,8 @@ public class NetworkMaskMatcherTests
|
||||
Assert.False(matcher.IsAllowed(IPAddress.IPv6Any));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Matcher_Throws_ForInvalidEntries()
|
||||
{
|
||||
var exception = Assert.Throws<FormatException>(() => new NetworkMaskMatcher(new[] { "invalid-mask" }));
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -4,11 +4,13 @@ using System.Security.Claims;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Abstractions.Tests;
|
||||
|
||||
public class StellaOpsPrincipalBuilderTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void NormalizedScopes_AreSortedDeduplicatedLowerCased()
|
||||
{
|
||||
var builder = new StellaOpsPrincipalBuilder()
|
||||
@@ -24,7 +26,8 @@ public class StellaOpsPrincipalBuilderTests
|
||||
builder.Audiences);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_ConstructsClaimsPrincipalWithNormalisedValues()
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
@@ -4,11 +4,13 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Abstractions.Tests;
|
||||
|
||||
public class StellaOpsProblemResultFactoryTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AuthenticationRequired_ReturnsCanonicalProblem()
|
||||
{
|
||||
var result = StellaOpsProblemResultFactory.AuthenticationRequired(instance: "/jobs");
|
||||
@@ -22,7 +24,8 @@ public class StellaOpsProblemResultFactoryTests
|
||||
Assert.Equal(details.Detail, details.Extensions["error_description"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void InvalidToken_UsesProvidedDetail()
|
||||
{
|
||||
var result = StellaOpsProblemResultFactory.InvalidToken("expired refresh token");
|
||||
@@ -33,7 +36,8 @@ public class StellaOpsProblemResultFactoryTests
|
||||
Assert.Equal("invalid_token", details.Extensions["error"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void InsufficientScope_AddsScopeExtensions()
|
||||
{
|
||||
var result = StellaOpsProblemResultFactory.InsufficientScope(
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Abstractions.Tests;
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
public class StellaOpsScopesTests
|
||||
{
|
||||
[Theory]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Theory]
|
||||
[InlineData(StellaOpsScopes.AdvisoryRead)]
|
||||
[InlineData(StellaOpsScopes.AdvisoryIngest)]
|
||||
[InlineData(StellaOpsScopes.AdvisoryAiView)]
|
||||
@@ -73,7 +75,8 @@ public class StellaOpsScopesTests
|
||||
Assert.Contains(scope, StellaOpsScopes.All);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Theory]
|
||||
[InlineData("Advisory:Read", StellaOpsScopes.AdvisoryRead)]
|
||||
[InlineData(" VEX:Ingest ", StellaOpsScopes.VexIngest)]
|
||||
[InlineData("AOC:VERIFY", StellaOpsScopes.AocVerify)]
|
||||
|
||||
@@ -564,6 +564,11 @@ public static class StellaOpsScopes
|
||||
/// </summary>
|
||||
public const string ExceptionsWrite = "exceptions:write";
|
||||
|
||||
/// <summary>
|
||||
/// Scope granting permission to request exceptions (initiate approval workflow).
|
||||
/// </summary>
|
||||
public const string ExceptionsRequest = "exceptions:request";
|
||||
|
||||
/// <summary>
|
||||
/// Scope granting administrative control over Graph resources.
|
||||
/// </summary>
|
||||
@@ -684,6 +689,7 @@ public static class StellaOpsScopes
|
||||
ZastavaAdmin,
|
||||
ExceptionsRead,
|
||||
ExceptionsWrite,
|
||||
ExceptionsRequest,
|
||||
GraphAdmin
|
||||
};
|
||||
|
||||
|
||||
@@ -16,11 +16,14 @@ using StellaOps.Auth.Client;
|
||||
using StellaOps.AirGap.Policy;
|
||||
using Xunit;
|
||||
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Client.Tests;
|
||||
|
||||
public class ServiceCollectionExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task AddStellaOpsAuthClient_ConfiguresRetryPolicy()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
@@ -75,7 +78,8 @@ public class ServiceCollectionExtensionsTests
|
||||
Assert.Contains(recordedHandlers, handler => handler.GetType().Name.Contains("PolicyHttpMessageHandler", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void EnsureEgressAllowed_InvokesPolicyWhenAuthorityProvided()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
@@ -131,7 +135,8 @@ public class ServiceCollectionExtensionsTests
|
||||
=> responder(request, cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task AddStellaOpsApiAuthentication_AttachesPatAndTenantHeader()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
@@ -177,7 +182,8 @@ public class ServiceCollectionExtensionsTests
|
||||
Assert.Equal(0, tokenClient.RequestCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task AddStellaOpsApiAuthentication_UsesClientCredentialsWithCaching()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Auth.Client\StellaOps.Auth.Client.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.0.0" />
|
||||
|
||||
@@ -2,11 +2,13 @@ using System;
|
||||
using StellaOps.Auth.Client;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Client.Tests;
|
||||
|
||||
public class StellaOpsAuthClientOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_NormalizesScopes()
|
||||
{
|
||||
var options = new StellaOpsAuthClientOptions
|
||||
@@ -26,7 +28,8 @@ public class StellaOpsAuthClientOptionsTests
|
||||
Assert.Equal<TimeSpan>(options.RetryDelays, options.NormalizedRetryDelays);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_When_AuthorityMissing()
|
||||
{
|
||||
var options = new StellaOpsAuthClientOptions();
|
||||
@@ -36,7 +39,8 @@ public class StellaOpsAuthClientOptionsTests
|
||||
Assert.Contains("Authority", exception.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_NormalizesRetryDelays()
|
||||
{
|
||||
var options = new StellaOpsAuthClientOptions
|
||||
@@ -54,7 +58,8 @@ public class StellaOpsAuthClientOptionsTests
|
||||
Assert.Equal<TimeSpan>(options.NormalizedRetryDelays, options.RetryDelays);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_DisabledRetries_ProducesEmptyDelays()
|
||||
{
|
||||
var options = new StellaOpsAuthClientOptions
|
||||
@@ -68,7 +73,8 @@ public class StellaOpsAuthClientOptionsTests
|
||||
Assert.Empty(options.NormalizedRetryDelays);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_When_OfflineToleranceNegative()
|
||||
{
|
||||
var options = new StellaOpsAuthClientOptions
|
||||
|
||||
@@ -10,11 +10,13 @@ using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Auth.Client;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Client.Tests;
|
||||
|
||||
public class StellaOpsDiscoveryCacheTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetAsync_UsesOfflineFallbackWithinTolerance()
|
||||
{
|
||||
var timeProvider = new FakeTimeProvider(DateTimeOffset.Parse("2025-01-01T00:00:00Z"));
|
||||
|
||||
@@ -18,6 +18,7 @@ using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Auth.Client;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Client.Tests;
|
||||
|
||||
/// <summary>
|
||||
@@ -31,7 +32,8 @@ public class StellaOpsTokenClientTests
|
||||
{
|
||||
#region Task 1: Token Issuance Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestPasswordToken_ReturnsResultAndCaches()
|
||||
{
|
||||
var timeProvider = new FakeTimeProvider(DateTimeOffset.Parse("2025-02-01T00:00:00Z"));
|
||||
@@ -76,7 +78,8 @@ public class StellaOpsTokenClientTests
|
||||
Assert.Empty(jwks.Keys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestClientCredentialsToken_ReturnsTokenWithCorrectExpiry()
|
||||
{
|
||||
// Arrange
|
||||
@@ -121,7 +124,8 @@ public class StellaOpsTokenClientTests
|
||||
Assert.Equal(expectedExpiry, result.ExpiresAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestClientCredentialsToken_WithCustomScope_UsesCustomScope()
|
||||
{
|
||||
// Arrange
|
||||
@@ -160,7 +164,8 @@ public class StellaOpsTokenClientTests
|
||||
Assert.Contains("policy.evaluate", result.Scopes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestClientCredentialsToken_WithoutClientId_ThrowsInvalidOperation()
|
||||
{
|
||||
// Arrange
|
||||
@@ -186,7 +191,8 @@ public class StellaOpsTokenClientTests
|
||||
client.RequestClientCredentialsTokenAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestPasswordToken_WithAdditionalParameters_IncludesParameters()
|
||||
{
|
||||
// Arrange
|
||||
@@ -237,7 +243,8 @@ public class StellaOpsTokenClientTests
|
||||
|
||||
#region Task 2: Token Validation/Rejection Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestPasswordToken_WhenServerReturnsError_ThrowsInvalidOperation()
|
||||
{
|
||||
// Arrange
|
||||
@@ -278,7 +285,8 @@ public class StellaOpsTokenClientTests
|
||||
Assert.Contains("401", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestPasswordToken_WhenResponseMissingAccessToken_ThrowsInvalidOperation()
|
||||
{
|
||||
// Arrange
|
||||
@@ -313,7 +321,8 @@ public class StellaOpsTokenClientTests
|
||||
Assert.Contains("access_token", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CachedToken_WhenExpired_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
@@ -338,7 +347,8 @@ public class StellaOpsTokenClientTests
|
||||
// The cache may have already evicted it or it won't be returned
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestPasswordToken_DefaultsToBearer_WhenTokenTypeNotProvided()
|
||||
{
|
||||
// Arrange
|
||||
@@ -375,7 +385,8 @@ public class StellaOpsTokenClientTests
|
||||
Assert.Equal("Bearer", result.TokenType); // Defaults to Bearer
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RequestPasswordToken_DefaultsTo3600ExpiresIn_WhenNotProvided()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
@@ -6,11 +6,13 @@ using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Auth.Client;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.Client.Tests;
|
||||
|
||||
public class TokenCacheTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task InMemoryTokenCache_ExpiresEntries()
|
||||
{
|
||||
var timeProvider = new FakeTimeProvider(DateTimeOffset.Parse("2025-01-01T00:00:00Z"));
|
||||
@@ -28,7 +30,8 @@ public class TokenCacheTests
|
||||
Assert.Null(retrieved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task FileTokenCache_PersistsEntries()
|
||||
{
|
||||
var directory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
|
||||
|
||||
@@ -8,11 +8,14 @@ using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using Xunit;
|
||||
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class ServiceCollectionExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AddStellaOpsResourceServerAuthentication_ConfiguresJwtBearer()
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Auth.ServerIntegration\StellaOps.Auth.ServerIntegration.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -3,11 +3,13 @@ using System.Net;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class StellaOpsResourceServerOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_NormalisesCollections()
|
||||
{
|
||||
var options = new StellaOpsResourceServerOptions
|
||||
@@ -43,7 +45,8 @@ public class StellaOpsResourceServerOptionsTests
|
||||
Assert.True(options.BypassMatcher.IsAllowed(IPAddress.IPv6Loopback));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_When_AuthorityMissing()
|
||||
{
|
||||
var options = new StellaOpsResourceServerOptions();
|
||||
|
||||
@@ -4,11 +4,13 @@ using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class StellaOpsResourceServerPoliciesTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AddObservabilityResourcePolicies_RegistersExpectedPolicies()
|
||||
{
|
||||
var options = new AuthorizationOptions();
|
||||
@@ -28,7 +30,8 @@ public class StellaOpsResourceServerPoliciesTests
|
||||
AssertPolicy(options, StellaOpsResourceServerPolicies.ExportAdmin, StellaOpsScopes.ExportAdmin);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AddPacksResourcePolicies_RegistersExpectedPolicies()
|
||||
{
|
||||
var options = new AuthorizationOptions();
|
||||
|
||||
@@ -16,11 +16,13 @@ using StellaOps.Cryptography.Audit;
|
||||
using OpenIddict.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class StellaOpsScopeAuthorizationHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenScopePresent()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -52,7 +54,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.False(string.IsNullOrWhiteSpace(record.CorrelationId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenTenantMismatch()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -83,7 +86,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("true", GetPropertyValue(record, "resource.tenant.mismatch"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenBypassNetworkMatches()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -107,7 +111,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("true", GetPropertyValue(record, "resource.authorization.bypass"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenScopeMissingAndNoBypass()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -130,7 +135,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("false", GetPropertyValue(record, "principal.authenticated"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenDefaultScopeMissing()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -159,7 +165,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal(StellaOpsScopes.PolicyRun, GetPropertyValue(record, "resource.scopes.missing"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenDefaultScopePresent()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -187,7 +194,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("true", GetPropertyValue(record, "principal.authenticated"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenIncidentAuthTimeMissing()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -220,7 +228,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("Sev1 drill", GetPropertyValue(record, "incident.reason"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenIncidentAuthTimeStale()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -256,7 +265,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("Sev1 drill", GetPropertyValue(record, "incident.reason"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenIncidentFreshAuthValid()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -291,7 +301,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("Sev1 drill", GetPropertyValue(record, "incident.reason"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenBackfillMetadataMissing()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -321,7 +332,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("false", GetPropertyValue(record, "backfill.metadata_satisfied"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenBackfillMetadataPresent()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -354,7 +366,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal("INC-741", GetPropertyValue(record, "backfill.ticket"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenPackApprovalMetadataMissing()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -385,7 +398,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal(StellaOpsScopes.PacksApprove, Assert.Single(record.Scopes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenPackApprovalFreshAuthStale()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
@@ -421,7 +435,8 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
Assert.Equal(StellaOpsScopes.PacksApprove, Assert.Single(record.Scopes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenPackApprovalMetadataPresent()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Tests;
|
||||
|
||||
public class LdapPluginOptionsTests : IDisposable
|
||||
@@ -19,7 +20,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Directory.CreateDirectory(tempRoot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Normalize_ResolvesRelativeClientCertificateAndBundlePaths()
|
||||
{
|
||||
var configPath = Path.Combine(tempRoot, "ldap.yaml");
|
||||
@@ -53,7 +55,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Equal(expectedBundle, options.Connection.TrustStore.BundlePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenHostMissing()
|
||||
{
|
||||
var options = new LdapPluginOptions
|
||||
@@ -70,7 +73,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Contains("connection.host", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenBundleModeWithoutPath()
|
||||
{
|
||||
var options = new LdapPluginOptions
|
||||
@@ -95,7 +99,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Contains("connection.trustStore.bundlePath", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenClientCertificateIncomplete()
|
||||
{
|
||||
var options = new LdapPluginOptions
|
||||
@@ -119,7 +124,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Contains("clientCertificate.pfxPath", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenTlsDisabledWithoutEnvToggle()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -132,7 +138,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Contains("allowInsecureWithEnvToggle", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenTlsDisabledWithoutEnvironmentVariable()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -145,7 +152,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Contains(LdapSecurityOptions.AllowInsecureEnvironmentVariable, ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_AllowsTlsDisabledWhenEnvToggleSet()
|
||||
{
|
||||
const string envVar = "STELLAOPS_LDAP_ALLOW_INSECURE";
|
||||
@@ -167,7 +175,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenRequireTlsWithoutTlsConfiguration()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -182,7 +191,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Contains("requires TLS", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_AllowsRequireTlsWithStartTls()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -195,7 +205,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
options.Validate("corp-ldap");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenRequireClientCertificateWithoutConfiguration()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -207,7 +218,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Contains("requireClientCertificate", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Normalize_ParsesLdapsSchemeAndSetsPort()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -220,7 +232,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Equal(1636, options.Connection.Port);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Normalize_DeduplicatesCipherSuites()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -234,7 +247,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
item => Assert.Equal("TLS_AES_128_GCM_SHA256", item));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Registrar_BindsOptionsAndAppliesNormalization()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
@@ -283,7 +297,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Equal("TLS_AES_256_GCM_SHA384", Assert.Single(options.Security.AllowedCipherSuites));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Normalize_TrimsClaimsConfiguration()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -317,7 +332,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Equal(0, options.Claims.Cache.MaxEntries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_AllowsClaimsCacheWithoutExplicitCollection()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -330,7 +346,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Equal("ldap_claims_cache_corp-ldap", options.Claims.Cache.ResolveCollectionName("corp-ldap"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Normalize_ClientProvisioningOptions()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
@@ -346,7 +363,8 @@ public class LdapPluginOptionsTests : IDisposable
|
||||
Assert.Equal("audit_log", options.ClientProvisioning.AuditMirror.CollectionName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenClientProvisioningMissingContainer()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugin.Ldap\StellaOps.Authority.Plugin.Ldap.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\\..\\__Libraries\\StellaOps.Authority.Storage.Postgres\\StellaOps.Authority.Storage.Postgres.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
|
||||
@@ -10,11 +10,13 @@ using StellaOps.Authority.Storage.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
public class StandardClientProvisioningStoreTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateOrUpdateAsync_HashesSecretAndPersistsDocument()
|
||||
{
|
||||
var store = new TrackingClientStore();
|
||||
@@ -45,7 +47,8 @@ public class StandardClientProvisioningStoreTests
|
||||
Assert.Contains("scopea", descriptor.AllowedScopes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateOrUpdateAsync_NormalisesTenant()
|
||||
{
|
||||
var store = new TrackingClientStore();
|
||||
@@ -71,7 +74,8 @@ public class StandardClientProvisioningStoreTests
|
||||
Assert.NotNull(descriptor);
|
||||
Assert.Equal("tenant-alpha", descriptor!.Tenant);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateOrUpdateAsync_StoresAudiences()
|
||||
{
|
||||
var store = new TrackingClientStore();
|
||||
@@ -99,7 +103,8 @@ public class StandardClientProvisioningStoreTests
|
||||
Assert.Equal(new[] { "attestor", "signer" }, descriptor!.AllowedAudiences.OrderBy(value => value, StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateOrUpdateAsync_MapsCertificateBindings()
|
||||
{
|
||||
var store = new TrackingClientStore();
|
||||
|
||||
@@ -3,11 +3,13 @@ using System.IO;
|
||||
using StellaOps.Authority.Plugin.Standard;
|
||||
using StellaOps.Cryptography;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
public class StandardPluginOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_AllowsBootstrapWhenCredentialsProvided()
|
||||
{
|
||||
var options = new StandardPluginOptions
|
||||
@@ -23,7 +25,8 @@ public class StandardPluginOptionsTests
|
||||
options.Validate("standard");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenBootstrapUserIncomplete()
|
||||
{
|
||||
var options = new StandardPluginOptions
|
||||
@@ -39,7 +42,8 @@ public class StandardPluginOptionsTests
|
||||
Assert.Contains("bootstrapUser", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenLockoutWindowMinutesInvalid()
|
||||
{
|
||||
var options = new StandardPluginOptions
|
||||
@@ -56,7 +60,8 @@ public class StandardPluginOptionsTests
|
||||
Assert.Contains("lockout.windowMinutes", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Normalize_ResolvesRelativeTokenSigningDirectory()
|
||||
{
|
||||
var configDir = Path.Combine(Path.GetTempPath(), "stellaops-standard-plugin", Guid.NewGuid().ToString("N"));
|
||||
@@ -84,7 +89,8 @@ public class StandardPluginOptionsTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Normalize_PreservesAbsoluteTokenSigningDirectory()
|
||||
{
|
||||
var absolute = Path.Combine(Path.GetTempPath(), "stellaops-standard-plugin", Guid.NewGuid().ToString("N"), "keys");
|
||||
@@ -98,7 +104,8 @@ public class StandardPluginOptionsTests
|
||||
Assert.Equal(Path.GetFullPath(absolute), options.TokenSigning.KeyDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenPasswordHashingMemoryInvalid()
|
||||
{
|
||||
var options = new StandardPluginOptions
|
||||
@@ -113,7 +120,8 @@ public class StandardPluginOptionsTests
|
||||
Assert.Contains("memory", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenPasswordHashingIterationsInvalid()
|
||||
{
|
||||
var options = new StandardPluginOptions
|
||||
@@ -128,7 +136,8 @@ public class StandardPluginOptionsTests
|
||||
Assert.Contains("iteration", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenPasswordHashingParallelismInvalid()
|
||||
{
|
||||
var options = new StandardPluginOptions
|
||||
|
||||
@@ -17,11 +17,14 @@ using StellaOps.Authority.Storage.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
public class StandardPluginRegistrarTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Register_ConfiguresIdentityProviderAndSeedsBootstrapUser()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -83,7 +86,8 @@ public class StandardPluginRegistrarTests
|
||||
Assert.True(verification.User?.RequiresPasswordReset);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Register_LogsWarning_WhenPasswordPolicyWeaker()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -128,7 +132,8 @@ public class StandardPluginRegistrarTests
|
||||
entry.Message.Contains("weaker password policy", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Register_ForcesPasswordCapability_WhenManifestMissing()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -160,7 +165,8 @@ public class StandardPluginRegistrarTests
|
||||
Assert.True(plugin.Capabilities.SupportsClientProvisioning);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Register_Throws_WhenBootstrapConfigurationIncomplete()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -194,7 +200,8 @@ public class StandardPluginRegistrarTests
|
||||
Assert.Throws<InvalidOperationException>(() => scope.ServiceProvider.GetRequiredService<IIdentityProviderPlugin>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Register_NormalizesTokenSigningKeyDirectory()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
|
||||
@@ -12,6 +12,7 @@ using StellaOps.Authority.Plugin.Standard.Storage;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
@@ -60,7 +61,8 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
NullLogger<StandardUserCredentialStore>.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_ReturnsSuccess_ForValidCredentials()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
@@ -87,7 +89,8 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
Assert.Null(auditEntry.FailureCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_EnforcesLockout_AfterRepeatedFailures()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
@@ -135,7 +138,8 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
Assert.Contains(lastAudit.Properties, property => property.Name == "plugin.retry_after_seconds");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_RehashesLegacyHashesToArgon2()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
@@ -179,7 +183,8 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
Assert.StartsWith("$argon2id$", updated!.PasswordHash, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_RecordsAudit_ForUnknownUser()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugin.Standard\StellaOps.Authority.Plugin.Standard.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
<!-- Storage now uses PostgreSQL via Plugin.Standard project reference -->
|
||||
</Project>
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
using System;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugins.Abstractions.Tests;
|
||||
|
||||
public class AuthorityClientRegistrationTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Constructor_Throws_WhenClientIdMissing()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new AuthorityClientRegistration(string.Empty, false, null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Constructor_RequiresSecret_ForConfidentialClients()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new AuthorityClientRegistration("cli", true, null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void WithClientSecret_ReturnsCopy()
|
||||
{
|
||||
var registration = new AuthorityClientRegistration("cli", false, null, null, tenant: "Tenant-Alpha");
|
||||
|
||||
@@ -2,11 +2,13 @@ using System;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugins.Abstractions.Tests;
|
||||
|
||||
public class AuthorityCredentialVerificationResultTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Success_SetsUserAndClearsFailure()
|
||||
{
|
||||
var user = new AuthorityUserDescriptor("subject-1", "user", "User", false);
|
||||
@@ -25,13 +27,15 @@ public class AuthorityCredentialVerificationResultTests
|
||||
Assert.Collection(result.AuditProperties, property => Assert.Equal("test", property.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Success_Throws_WhenUserNull()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => AuthorityCredentialVerificationResult.Success(null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Failure_SetsFailureCode()
|
||||
{
|
||||
var auditProperties = new[]
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugins.Abstractions.Tests;
|
||||
|
||||
public class AuthorityIdentityProviderCapabilitiesTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void FromCapabilities_SetsFlags_WhenTokensPresent()
|
||||
{
|
||||
var capabilities = AuthorityIdentityProviderCapabilities.FromCapabilities(new[]
|
||||
@@ -22,7 +24,8 @@ public class AuthorityIdentityProviderCapabilitiesTests
|
||||
Assert.True(capabilities.SupportsBootstrap);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void FromCapabilities_DefaultsToFalse_WhenEmpty()
|
||||
{
|
||||
var capabilities = AuthorityIdentityProviderCapabilities.FromCapabilities(Array.Empty<string>());
|
||||
@@ -33,7 +36,8 @@ public class AuthorityIdentityProviderCapabilitiesTests
|
||||
Assert.False(capabilities.SupportsBootstrap);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void FromCapabilities_IgnoresNullSet()
|
||||
{
|
||||
var capabilities = AuthorityIdentityProviderCapabilities.FromCapabilities(null!);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugins.Abstractions.Tests;
|
||||
|
||||
public class AuthorityPluginHealthResultTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Healthy_ReturnsHealthyStatus()
|
||||
{
|
||||
var result = AuthorityPluginHealthResult.Healthy("ready");
|
||||
@@ -14,7 +16,8 @@ public class AuthorityPluginHealthResultTests
|
||||
Assert.NotNull(result.Details);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Degraded_ReturnsDegradedStatus()
|
||||
{
|
||||
var result = AuthorityPluginHealthResult.Degraded("slow");
|
||||
@@ -22,7 +25,8 @@ public class AuthorityPluginHealthResultTests
|
||||
Assert.Equal(AuthorityPluginHealthStatus.Degraded, result.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Unavailable_ReturnsUnavailableStatus()
|
||||
{
|
||||
var result = AuthorityPluginHealthResult.Unavailable("down");
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugins.Abstractions.Tests;
|
||||
|
||||
public class AuthorityPluginOperationResultTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Success_ReturnsSucceededResult()
|
||||
{
|
||||
var result = AuthorityPluginOperationResult.Success("ok");
|
||||
@@ -15,7 +17,8 @@ public class AuthorityPluginOperationResultTests
|
||||
Assert.Equal("ok", result.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Failure_PopulatesErrorCode()
|
||||
{
|
||||
var result = AuthorityPluginOperationResult.Failure("ERR_CODE", "failure");
|
||||
@@ -25,13 +28,15 @@ public class AuthorityPluginOperationResultTests
|
||||
Assert.Equal("failure", result.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Failure_Throws_WhenErrorCodeMissing()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => AuthorityPluginOperationResult.Failure(string.Empty));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void GenericSuccess_ReturnsValue()
|
||||
{
|
||||
var result = AuthorityPluginOperationResult<string>.Success("value", "created");
|
||||
@@ -41,7 +46,8 @@ public class AuthorityPluginOperationResultTests
|
||||
Assert.Equal("created", result.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void GenericFailure_PopulatesErrorCode()
|
||||
{
|
||||
var result = AuthorityPluginOperationResult<int>.Failure("CONFLICT", "duplicate");
|
||||
@@ -52,7 +58,8 @@ public class AuthorityPluginOperationResultTests
|
||||
Assert.Equal("duplicate", result.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void GenericFailure_Throws_WhenErrorCodeMissing()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => AuthorityPluginOperationResult<string>.Failure(" "));
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
using System;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugins.Abstractions.Tests;
|
||||
|
||||
public class AuthorityUserDescriptorTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Constructor_Throws_WhenSubjectMissing()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new AuthorityUserDescriptor(string.Empty, "user", null, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Constructor_Throws_WhenUsernameMissing()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new AuthorityUserDescriptor("subject", " ", null, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Constructor_MaterialisesCollections()
|
||||
{
|
||||
var descriptor = new AuthorityUserDescriptor("subject", "user", null, false);
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
using System;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugins.Abstractions.Tests;
|
||||
|
||||
public class AuthorityUserRegistrationTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Constructor_Throws_WhenUsernameMissing()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new AuthorityUserRegistration(string.Empty, null, null, null, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void WithPassword_ReturnsCopyWithPassword()
|
||||
{
|
||||
var registration = new AuthorityUserRegistration("alice", null, "Alice", null, true);
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user