Refactor code structure and optimize performance across multiple modules

This commit is contained in:
StellaOps Bot
2025-12-26 20:03:22 +02:00
parent c786faae84
commit f10d83c444
1385 changed files with 69732 additions and 10280 deletions

View File

@@ -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" }));

View File

@@ -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;

View File

@@ -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(

View File

@@ -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)]

View File

@@ -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
};

View File

@@ -20,7 +20,8 @@ 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 +76,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 +133,8 @@ public class ServiceCollectionExtensionsTests
=> responder(request, cancellationToken);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AddStellaOpsApiAuthentication_AttachesPatAndTenantHeader()
{
var services = new ServiceCollection();
@@ -177,7 +180,8 @@ public class ServiceCollectionExtensionsTests
Assert.Equal(0, tokenClient.RequestCount);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AddStellaOpsApiAuthentication_UsesClientCredentialsWithCaching()
{
var services = new ServiceCollection();
@@ -210,6 +214,7 @@ public class ServiceCollectionExtensionsTests
});
using var provider = services.BuildServiceProvider();
using StellaOps.TestKit;
var client = provider.GetRequiredService<IHttpClientFactory>().CreateClient("notify");
await client.GetAsync("https://notify.example/api");

View File

@@ -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

View File

@@ -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"));

View File

@@ -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

View File

@@ -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"));

View File

@@ -12,7 +12,8 @@ namespace StellaOps.Auth.ServerIntegration.Tests;
public class ServiceCollectionExtensionsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AddStellaOpsResourceServerAuthentication_ConfiguresJwtBearer()
{
var configuration = new ConfigurationBuilder()
@@ -31,6 +32,7 @@ public class ServiceCollectionExtensionsTests
using var provider = services.BuildServiceProvider();
using StellaOps.TestKit;
var resourceOptions = provider.GetRequiredService<IOptionsMonitor<StellaOpsResourceServerOptions>>().CurrentValue;
var jwtOptions = provider.GetRequiredService<IOptionsMonitor<JwtBearerOptions>>().Get(StellaOpsAuthenticationDefaults.AuthenticationScheme);

View File

@@ -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();

View File

@@ -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();

View File

@@ -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 =>

View File

@@ -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();

View File

@@ -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();

View File

@@ -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

View File

@@ -21,7 +21,8 @@ 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 +84,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 +130,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 +163,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 +198,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();
@@ -231,6 +236,7 @@ public class StandardPluginRegistrarTests
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
using var provider = services.BuildServiceProvider();
using StellaOps.TestKit;
var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<StandardPluginOptions>>();
var options = optionsMonitor.Get("standard");

View File

@@ -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();

View File

@@ -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");

View File

@@ -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[]

View File

@@ -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!);

View File

@@ -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");

View File

@@ -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(" "));

View File

@@ -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);

View File

@@ -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);