Merge all changes

This commit is contained in:
StellaOps Bot
2026-01-08 08:54:27 +02:00
parent 589de352c2
commit 110591d6bf
381 changed files with 2237 additions and 1939 deletions

View File

@@ -65,35 +65,35 @@ public class AuthAbstractionsConstantsTests
[nameof(StellaOpsClaimTypes.SessionId)] = "sid"
};
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Subject)], StellaOpsClaimTypes.Subject);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Tenant)], StellaOpsClaimTypes.Tenant);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Project)], StellaOpsClaimTypes.Project);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.ClientId)], StellaOpsClaimTypes.ClientId);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.ServiceAccount)], StellaOpsClaimTypes.ServiceAccount);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.TokenId)], StellaOpsClaimTypes.TokenId);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.AuthenticationMethod)], StellaOpsClaimTypes.AuthenticationMethod);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Scope)], StellaOpsClaimTypes.Scope);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.ScopeItem)], StellaOpsClaimTypes.ScopeItem);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Audience)], StellaOpsClaimTypes.Audience);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.IdentityProvider)], StellaOpsClaimTypes.IdentityProvider);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.OperatorReason)], StellaOpsClaimTypes.OperatorReason);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.OperatorTicket)], StellaOpsClaimTypes.OperatorTicket);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.QuotaReason)], StellaOpsClaimTypes.QuotaReason);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.QuotaTicket)], StellaOpsClaimTypes.QuotaTicket);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.BackfillReason)], StellaOpsClaimTypes.BackfillReason);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.BackfillTicket)], StellaOpsClaimTypes.BackfillTicket);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyDigest)], StellaOpsClaimTypes.PolicyDigest);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyTicket)], StellaOpsClaimTypes.PolicyTicket);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyReason)], StellaOpsClaimTypes.PolicyReason);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PackRunId)], StellaOpsClaimTypes.PackRunId);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PackGateId)], StellaOpsClaimTypes.PackGateId);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PackPlanHash)], StellaOpsClaimTypes.PackPlanHash);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyOperation)], StellaOpsClaimTypes.PolicyOperation);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.IncidentReason)], StellaOpsClaimTypes.IncidentReason);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.VulnerabilityEnvironment)], StellaOpsClaimTypes.VulnerabilityEnvironment);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.VulnerabilityOwner)], StellaOpsClaimTypes.VulnerabilityOwner);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.VulnerabilityBusinessTier)], StellaOpsClaimTypes.VulnerabilityBusinessTier);
Assert.Equal(expected[nameof(StellaOpsClaimTypes.SessionId)], StellaOpsClaimTypes.SessionId);
Assert.Equal(StellaOpsClaimTypes.Subject, expected[nameof(StellaOpsClaimTypes.Subject)]);
Assert.Equal(StellaOpsClaimTypes.Tenant, expected[nameof(StellaOpsClaimTypes.Tenant)]);
Assert.Equal(StellaOpsClaimTypes.Project, expected[nameof(StellaOpsClaimTypes.Project)]);
Assert.Equal(StellaOpsClaimTypes.ClientId, expected[nameof(StellaOpsClaimTypes.ClientId)]);
Assert.Equal(StellaOpsClaimTypes.ServiceAccount, expected[nameof(StellaOpsClaimTypes.ServiceAccount)]);
Assert.Equal(StellaOpsClaimTypes.TokenId, expected[nameof(StellaOpsClaimTypes.TokenId)]);
Assert.Equal(StellaOpsClaimTypes.AuthenticationMethod, expected[nameof(StellaOpsClaimTypes.AuthenticationMethod)]);
Assert.Equal(StellaOpsClaimTypes.Scope, expected[nameof(StellaOpsClaimTypes.Scope)]);
Assert.Equal(StellaOpsClaimTypes.ScopeItem, expected[nameof(StellaOpsClaimTypes.ScopeItem)]);
Assert.Equal(StellaOpsClaimTypes.Audience, expected[nameof(StellaOpsClaimTypes.Audience)]);
Assert.Equal(StellaOpsClaimTypes.IdentityProvider, expected[nameof(StellaOpsClaimTypes.IdentityProvider)]);
Assert.Equal(StellaOpsClaimTypes.OperatorReason, expected[nameof(StellaOpsClaimTypes.OperatorReason)]);
Assert.Equal(StellaOpsClaimTypes.OperatorTicket, expected[nameof(StellaOpsClaimTypes.OperatorTicket)]);
Assert.Equal(StellaOpsClaimTypes.QuotaReason, expected[nameof(StellaOpsClaimTypes.QuotaReason)]);
Assert.Equal(StellaOpsClaimTypes.QuotaTicket, expected[nameof(StellaOpsClaimTypes.QuotaTicket)]);
Assert.Equal(StellaOpsClaimTypes.BackfillReason, expected[nameof(StellaOpsClaimTypes.BackfillReason)]);
Assert.Equal(StellaOpsClaimTypes.BackfillTicket, expected[nameof(StellaOpsClaimTypes.BackfillTicket)]);
Assert.Equal(StellaOpsClaimTypes.PolicyDigest, expected[nameof(StellaOpsClaimTypes.PolicyDigest)]);
Assert.Equal(StellaOpsClaimTypes.PolicyTicket, expected[nameof(StellaOpsClaimTypes.PolicyTicket)]);
Assert.Equal(StellaOpsClaimTypes.PolicyReason, expected[nameof(StellaOpsClaimTypes.PolicyReason)]);
Assert.Equal(StellaOpsClaimTypes.PackRunId, expected[nameof(StellaOpsClaimTypes.PackRunId)]);
Assert.Equal(StellaOpsClaimTypes.PackGateId, expected[nameof(StellaOpsClaimTypes.PackGateId)]);
Assert.Equal(StellaOpsClaimTypes.PackPlanHash, expected[nameof(StellaOpsClaimTypes.PackPlanHash)]);
Assert.Equal(StellaOpsClaimTypes.PolicyOperation, expected[nameof(StellaOpsClaimTypes.PolicyOperation)]);
Assert.Equal(StellaOpsClaimTypes.IncidentReason, expected[nameof(StellaOpsClaimTypes.IncidentReason)]);
Assert.Equal(StellaOpsClaimTypes.VulnerabilityEnvironment, expected[nameof(StellaOpsClaimTypes.VulnerabilityEnvironment)]);
Assert.Equal(StellaOpsClaimTypes.VulnerabilityOwner, expected[nameof(StellaOpsClaimTypes.VulnerabilityOwner)]);
Assert.Equal(StellaOpsClaimTypes.VulnerabilityBusinessTier, expected[nameof(StellaOpsClaimTypes.VulnerabilityBusinessTier)]);
Assert.Equal(StellaOpsClaimTypes.SessionId, expected[nameof(StellaOpsClaimTypes.SessionId)]);
}
[Trait("Category", TestCategories.Unit)]

View File

@@ -72,7 +72,7 @@ public class ServiceCollectionExtensionsTests
using var provider = services.BuildServiceProvider();
var cache = provider.GetRequiredService<StellaOpsDiscoveryCache>();
var configuration = await cache.GetAsync(CancellationToken.None);
var configuration = await cache.GetAsync(TestContext.Current.CancellationToken);
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
Assert.Equal(2, attemptCount);

View File

@@ -47,12 +47,12 @@ public class StellaOpsDiscoveryCacheTests
var monitor = new TestOptionsMonitor<StellaOpsAuthClientOptions>(options);
var cache = new StellaOpsDiscoveryCache(httpClient, monitor, timeProvider, NullLogger<StellaOpsDiscoveryCache>.Instance);
var configuration = await cache.GetAsync(CancellationToken.None);
var configuration = await cache.GetAsync(TestContext.Current.CancellationToken);
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
timeProvider.Advance(TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(5));
configuration = await cache.GetAsync(CancellationToken.None);
configuration = await cache.GetAsync(TestContext.Current.CancellationToken);
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
Assert.Equal(2, callCount);
@@ -66,7 +66,7 @@ public class StellaOpsDiscoveryCacheTests
HttpRequestException? exception = null;
try
{
await cache.GetAsync(CancellationToken.None);
await cache.GetAsync(TestContext.Current.CancellationToken);
}
catch (HttpRequestException ex)
{

View File

@@ -52,12 +52,12 @@ public class StellaOpsJwksCacheTests
var discoveryCache = new StellaOpsDiscoveryCache(discoveryClient, monitor, timeProvider, NullLogger<StellaOpsDiscoveryCache>.Instance);
var jwksCache = new StellaOpsJwksCache(jwksClient, discoveryCache, monitor, timeProvider, NullLogger<StellaOpsJwksCache>.Instance);
var keys = await jwksCache.GetAsync(CancellationToken.None);
var keys = await jwksCache.GetAsync(TestContext.Current.CancellationToken);
Assert.NotNull(keys);
timeProvider.Advance(TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(5));
keys = await jwksCache.GetAsync(CancellationToken.None);
keys = await jwksCache.GetAsync(TestContext.Current.CancellationToken);
Assert.NotNull(keys);
Assert.Equal(2, jwksCallCount);
@@ -68,7 +68,7 @@ public class StellaOpsJwksCacheTests
Assert.True(offlineExpiry < timeProvider.GetUtcNow());
await Assert.ThrowsAsync<HttpRequestException>(() => jwksCache.GetAsync(CancellationToken.None));
await Assert.ThrowsAsync<HttpRequestException>(() => jwksCache.GetAsync(TestContext.Current.CancellationToken));
}
private static HttpResponseMessage CreateJsonResponse(string json)

View File

@@ -38,7 +38,7 @@ public class StellaOpsAuthorityConfigurationManagerTests
var initialMetadataRequests = handler.MetadataRequests;
var initialJwksRequests = handler.JwksRequests;
var second = await manager.GetConfigurationAsync(CancellationToken.None);
var second = await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
// Cache must return same instance
Assert.Same(first, second);
@@ -70,11 +70,11 @@ public class StellaOpsAuthorityConfigurationManagerTests
timeProvider,
NullLogger<StellaOpsAuthorityConfigurationManager>.Instance);
var first = await manager.GetConfigurationAsync(CancellationToken.None);
var first = await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
timeProvider.Advance(TimeSpan.FromMinutes(2));
var second = await manager.GetConfigurationAsync(CancellationToken.None);
var second = await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
Assert.Same(first, second);
Assert.Equal(2, handler.MetadataRequests);
@@ -100,12 +100,12 @@ public class StellaOpsAuthorityConfigurationManagerTests
timeProvider,
NullLogger<StellaOpsAuthorityConfigurationManager>.Instance);
await manager.GetConfigurationAsync(CancellationToken.None);
await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
var updated = CreateOptions("https://authority2.test");
optionsMonitor.Set(updated);
await manager.GetConfigurationAsync(CancellationToken.None);
await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
Assert.Equal(2, handler.MetadataRequests);
}

View File

@@ -21,8 +21,8 @@ public sealed class InMemoryLdapClaimsCacheTests
["displayName"] = "Alice Example"
});
await cache.SetAsync("uid=alice,ou=people,dc=example,dc=internal", claims, CancellationToken.None);
var fetched = await cache.GetAsync("uid=alice,ou=people,dc=example,dc=internal", CancellationToken.None);
await cache.SetAsync("uid=alice,ou=people,dc=example,dc=internal", claims, TestContext.Current.CancellationToken);
var fetched = await cache.GetAsync("uid=alice,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
Assert.NotNull(fetched);
Assert.Contains("operators", fetched!.Roles);
@@ -35,10 +35,10 @@ public sealed class InMemoryLdapClaimsCacheTests
var timeProvider = new TestTimeProvider(new DateTimeOffset(2025, 11, 9, 6, 0, 0, TimeSpan.Zero));
var cache = CreateCache(enabled: true, ttlSeconds: 60, timeProvider: timeProvider);
await cache.SetAsync("uid=expired,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), CancellationToken.None);
await cache.SetAsync("uid=expired,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), TestContext.Current.CancellationToken);
timeProvider.Advance(TimeSpan.FromMinutes(5));
var fetched = await cache.GetAsync("uid=expired,ou=people,dc=example,dc=internal", CancellationToken.None);
var fetched = await cache.GetAsync("uid=expired,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
Assert.Null(fetched);
}
@@ -48,11 +48,11 @@ public sealed class InMemoryLdapClaimsCacheTests
{
var cache = CreateCache(enabled: true, ttlSeconds: 600, maxEntries: 1);
await cache.SetAsync("uid=first,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), CancellationToken.None);
await cache.SetAsync("uid=second,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), CancellationToken.None);
await cache.SetAsync("uid=first,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), TestContext.Current.CancellationToken);
await cache.SetAsync("uid=second,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), TestContext.Current.CancellationToken);
var first = await cache.GetAsync("uid=first,ou=people,dc=example,dc=internal", CancellationToken.None);
var second = await cache.GetAsync("uid=second,ou=people,dc=example,dc=internal", CancellationToken.None);
var first = await cache.GetAsync("uid=first,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
var second = await cache.GetAsync("uid=second,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
Assert.Null(first);
Assert.NotNull(second);

View File

@@ -41,7 +41,7 @@ public class LdapClaimsEnricherTests
var identity = new ClaimsIdentity();
var context = CreateContext("uid=j.doe,ou=people,dc=example,dc=internal");
await enricher.EnrichAsync(identity, context, CancellationToken.None);
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "operators");
}
@@ -74,7 +74,7 @@ public class LdapClaimsEnricherTests
var identity = new ClaimsIdentity();
var context = CreateContext("uid=ops,ou=people,dc=example,dc=internal");
await enricher.EnrichAsync(identity, context, CancellationToken.None);
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "incident");
}
@@ -107,7 +107,7 @@ public class LdapClaimsEnricherTests
var identity = new ClaimsIdentity();
var context = CreateContext("uid=alice,ou=people,dc=example,dc=internal");
await enricher.EnrichAsync(identity, context, CancellationToken.None);
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
Assert.Contains(identity.Claims, claim => claim.Type == "displayName" && claim.Value == "Alice Example");
Assert.Contains(identity.Claims, claim => claim.Type == "email" && claim.Value == "alice@example.test");
@@ -134,11 +134,11 @@ public class LdapClaimsEnricherTests
var identity = new ClaimsIdentity();
var context = CreateContext("uid=cached,ou=people,dc=example,dc=internal");
await enricher.EnrichAsync(identity, context, CancellationToken.None);
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "operators");
Assert.Contains(identity.Claims, claim => claim.Type == "displayName" && claim.Value == "Cached User");
Assert.Equal(0, connection.Operations.Count);
Assert.Empty(connection.Operations);
Assert.Equal(0, cache.SetCount);
}

View File

@@ -25,7 +25,7 @@ public class LdapCapabilityProbeTests
checkClientProvisioning: true,
checkBootstrap: true,
options.CapabilityProbe.Timeout,
CancellationToken.None);
TestContext.Current.CancellationToken);
Assert.True(snapshot.ClientProvisioningWritable);
Assert.True(snapshot.BootstrapWritable);
@@ -47,7 +47,7 @@ public class LdapCapabilityProbeTests
checkClientProvisioning: true,
checkBootstrap: true,
options.CapabilityProbe.Timeout,
CancellationToken.None);
TestContext.Current.CancellationToken);
Assert.False(snapshot.ClientProvisioningWritable);
Assert.False(snapshot.BootstrapWritable);

View File

@@ -49,7 +49,7 @@ public sealed class LdapClientProvisioningStoreTests
allowedScopes: new[] { "signer.sign" },
allowedAudiences: new[] { "signer" });
var result = await store.CreateOrUpdateAsync(registration, CancellationToken.None);
var result = await store.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.True(clientStore.Documents.ContainsKey("svc-bootstrap"));
@@ -88,7 +88,7 @@ public sealed class LdapClientProvisioningStoreTests
SecretHash = "hash"
};
var result = await store.DeleteAsync("svc-bootstrap", CancellationToken.None);
var result = await store.DeleteAsync("svc-bootstrap", TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.DoesNotContain("svc-bootstrap", clientStore.Documents.Keys);
@@ -124,7 +124,7 @@ public sealed class LdapClientProvisioningStoreTests
allowedGrantTypes: new[] { "client_credentials" },
allowedScopes: new[] { "signer.sign" });
var result = await store.CreateOrUpdateAsync(registration, CancellationToken.None);
var result = await store.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
Assert.False(result.Succeeded);
Assert.Equal("disabled", result.ErrorCode);

View File

@@ -44,7 +44,7 @@ public class LdapCredentialStoreTests
monitor,
new FakeLdapConnectionFactory(connection));
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.Equal(2, bindCalls.Count);
@@ -86,7 +86,7 @@ public class LdapCredentialStoreTests
monitor,
new FakeLdapConnectionFactory(connection));
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.NotNull(result.User);
@@ -119,7 +119,7 @@ public class LdapCredentialStoreTests
new FakeLdapConnectionFactory(connection),
delayAsync: (_, _) => Task.CompletedTask);
var result = await store.VerifyPasswordAsync("jdoe", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("jdoe", "Password1!", TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.Equal(2, attempts);
@@ -142,7 +142,7 @@ public class LdapCredentialStoreTests
new FakeLdapConnectionFactory(connection),
delayAsync: (_, _) => Task.CompletedTask);
var result = await store.VerifyPasswordAsync("jdoe", "bad", CancellationToken.None);
var result = await store.VerifyPasswordAsync("jdoe", "bad", TestContext.Current.CancellationToken);
Assert.False(result.Succeeded);
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, result.FailureCode);
@@ -176,7 +176,7 @@ public class LdapCredentialStoreTests
monitor,
new FakeLdapConnectionFactory(connection));
var result = await store.FindBySubjectAsync("uid=j.doe,ou=people,dc=example,dc=internal", CancellationToken.None);
var result = await store.FindBySubjectAsync("uid=j.doe,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("uid=j.doe,ou=people,dc=example,dc=internal", result!.SubjectId);
@@ -201,7 +201,7 @@ public class LdapCredentialStoreTests
email: "bootstrap@example.internal",
requirePasswordReset: true);
var result = await store.UpsertUserAsync(registration, CancellationToken.None);
var result = await store.UpsertUserAsync(registration, TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.Contains(connection.Operations, op => op.StartsWith("add:uid=bootstrap.user", StringComparison.OrdinalIgnoreCase));
@@ -234,7 +234,7 @@ public class LdapCredentialStoreTests
email: "bootstrap@example.internal",
requirePasswordReset: false);
var result = await store.UpsertUserAsync(registration, CancellationToken.None);
var result = await store.UpsertUserAsync(registration, TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.Contains(connection.Operations, op => op.StartsWith("modify:uid=bootstrap.user", StringComparison.OrdinalIgnoreCase));

View File

@@ -61,7 +61,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, connection);
// Act
var result = await store.VerifyPasswordAsync("noname", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("noname", "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeTrue("Missing displayName should not prevent authentication");
@@ -88,7 +88,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, connection);
// Act
var result = await store.VerifyPasswordAsync("nomail", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("nomail", "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeTrue("Missing mail should not prevent authentication");
@@ -113,7 +113,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, connection);
// Act
var result = await store.VerifyPasswordAsync("nogroups", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("nogroups", "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeTrue("Empty memberOf should not prevent authentication");
@@ -135,7 +135,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, connection);
// Act
var result = await store.VerifyPasswordAsync("nonexistent", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("nonexistent", "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeFalse("Nonexistent user should fail authentication");
@@ -177,7 +177,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
var result = await store.VerifyPasswordAsync("user", "WrongPassword!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("user", "WrongPassword!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeFalse("Wrong password should fail authentication");
@@ -200,7 +200,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, connection);
// Act
var result = await store.VerifyPasswordAsync("malformed", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("malformed", "Password1!", TestContext.Current.CancellationToken);
// Assert - should handle gracefully (either succeed with warning or fail cleanly)
// The exact behavior depends on implementation
@@ -225,7 +225,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", CancellationToken.None);
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", TestContext.Current.CancellationToken);
// Assert
await act.Should().ThrowAsync<TimeoutException>();
@@ -247,7 +247,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", CancellationToken.None);
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", TestContext.Current.CancellationToken);
// Assert
await act.Should().ThrowAsync<InvalidOperationException>();
@@ -303,7 +303,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, connection);
// Act
var result = await store.VerifyPasswordAsync("münchen-user", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("münchen-user", "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeTrue("Unicode username should be handled");
@@ -329,7 +329,7 @@ public sealed class LdapConnectorResilienceTests
var store = CreateStore(options, connection);
// Act
var result = await store.VerifyPasswordAsync("user+test", "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync("user+test", "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeTrue("Special characters in DN should be handled");

View File

@@ -69,7 +69,7 @@ public sealed class LdapConnectorSecurityTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
var result = await store.VerifyPasswordAsync(maliciousUsername, "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync(maliciousUsername, "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeFalse("Injection attempt should fail");
@@ -103,7 +103,7 @@ public sealed class LdapConnectorSecurityTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
var result = await store.VerifyPasswordAsync(emptyUsername, "Password1!", CancellationToken.None);
var result = await store.VerifyPasswordAsync(emptyUsername, "Password1!", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeFalse("Empty username should be rejected");
@@ -120,7 +120,7 @@ public sealed class LdapConnectorSecurityTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
var result = await store.VerifyPasswordAsync("user", null!, CancellationToken.None);
var result = await store.VerifyPasswordAsync("user", null!, TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeFalse("Null password should be rejected");
@@ -137,7 +137,7 @@ public sealed class LdapConnectorSecurityTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
var result = await store.VerifyPasswordAsync("user", "", CancellationToken.None);
var result = await store.VerifyPasswordAsync("user", "", TestContext.Current.CancellationToken);
// Assert
result.Succeeded.Should().BeFalse("Empty password should be rejected");
@@ -169,7 +169,7 @@ public sealed class LdapConnectorSecurityTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", CancellationToken.None);
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", TestContext.Current.CancellationToken);
// Assert
await act.Should().ThrowAsync<InvalidOperationException>();
@@ -201,7 +201,7 @@ public sealed class LdapConnectorSecurityTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
await store.VerifyPasswordAsync("targetuser", "Password1!", CancellationToken.None);
await store.VerifyPasswordAsync("targetuser", "Password1!", TestContext.Current.CancellationToken);
// Assert
bindDns.Should().HaveCountGreaterThanOrEqualTo(2, "Should bind as service then as user");
@@ -289,7 +289,7 @@ public sealed class LdapConnectorSecurityTests
// Act
try
{
await store.VerifyPasswordAsync("user", "SuperSecret123!", CancellationToken.None);
await store.VerifyPasswordAsync("user", "SuperSecret123!", TestContext.Current.CancellationToken);
}
catch
{
@@ -308,7 +308,7 @@ public sealed class LdapConnectorSecurityTests
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
// Act
var result = await store.VerifyPasswordAsync("user", "MyPassword123", CancellationToken.None);
var result = await store.VerifyPasswordAsync("user", "MyPassword123", TestContext.Current.CancellationToken);
// Assert
var resultString = result.ToString();

View File

@@ -112,13 +112,13 @@ public sealed class LdapConnectorSnapshotTests
{
// Arrange
var fixtureFiles = Directory.Exists(FixturesPath)
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).ToList()
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).Where(n => n is not null).Cast<string>().ToList()
: new List<string>();
var expectedFiles = Directory.Exists(ExpectedPath)
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
.Select(f => Path.GetFileNameWithoutExtension(f).Replace(".canonical", ""))
.ToList()
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
.Where(n => n is not null).Cast<string>().ToList()
: new List<string>();
// Assert

View File

@@ -52,7 +52,7 @@ public sealed class OidcCredentialStoreTests
username: "user@example.com",
signingCredentials: new SigningCredentials(symmetricKey, SecurityAlgorithms.HmacSha256));
var result = await store.VerifyPasswordAsync("user@example.com", token, CancellationToken.None);
var result = await store.VerifyPasswordAsync("user@example.com", token, TestContext.Current.CancellationToken);
Assert.False(result.Succeeded);
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, result.FailureCode);
@@ -102,10 +102,10 @@ public sealed class OidcCredentialStoreTests
username: "user2@example.com",
signingCredentials: new SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha256));
var result = await storeA.VerifyPasswordAsync("user2@example.com", token, CancellationToken.None);
var result = await storeA.VerifyPasswordAsync("user2@example.com", token, TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
var cached = await storeB.FindBySubjectAsync("user-2", CancellationToken.None);
var cached = await storeB.FindBySubjectAsync("user-2", TestContext.Current.CancellationToken);
Assert.Null(cached);
}
@@ -220,8 +220,8 @@ public sealed class OidcCredentialStoreTests
public OidcPluginOptions CurrentValue => options.Values.First();
public OidcPluginOptions Get(string name)
=> options.TryGetValue(name, out var value) ? value : options.Values.First();
public OidcPluginOptions Get(string? name)
=> name is not null && options.TryGetValue(name, out var value) ? value : options.Values.First();
public IDisposable OnChange(Action<OidcPluginOptions, string> listener)
=> new NoopDisposable();

View File

@@ -23,7 +23,7 @@ public sealed class OidcIdentityProviderPluginTests
{
var (plugin, _) = CreatePlugin(HttpStatusCode.OK);
var result = await plugin.CheckHealthAsync(CancellationToken.None);
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
Assert.Equal(AuthorityPluginHealthStatus.Healthy, result.Status);
}
@@ -33,7 +33,7 @@ public sealed class OidcIdentityProviderPluginTests
{
var (plugin, _) = CreatePlugin(HttpStatusCode.ServiceUnavailable);
var result = await plugin.CheckHealthAsync(CancellationToken.None);
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
Assert.Equal(AuthorityPluginHealthStatus.Degraded, result.Status);
}
@@ -132,7 +132,7 @@ public sealed class OidcIdentityProviderPluginTests
public OidcPluginOptions CurrentValue => options;
public OidcPluginOptions Get(string name)
public OidcPluginOptions Get(string? name)
=> string.Equals(name, pluginName, StringComparison.Ordinal) ? options : options;
public IDisposable OnChange(Action<OidcPluginOptions, string> listener)

View File

@@ -129,13 +129,13 @@ public sealed class OidcConnectorSnapshotTests
{
// Arrange
var fixtureFiles = Directory.Exists(FixturesPath)
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).ToList()
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).Where(n => n is not null).Cast<string>().ToList()
: new List<string>();
var expectedFiles = Directory.Exists(ExpectedPath)
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
.ToList()
.Where(n => n is not null).Cast<string>().ToList()
: new List<string>();
// Assert

View File

@@ -23,7 +23,7 @@ public sealed class SamlIdentityProviderPluginTests
{
var plugin = CreatePlugin(HttpStatusCode.OK);
var result = await plugin.CheckHealthAsync(CancellationToken.None);
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
Assert.Equal(AuthorityPluginHealthStatus.Healthy, result.Status);
}
@@ -33,7 +33,7 @@ public sealed class SamlIdentityProviderPluginTests
{
var plugin = CreatePlugin(HttpStatusCode.ServiceUnavailable);
var result = await plugin.CheckHealthAsync(CancellationToken.None);
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
Assert.Equal(AuthorityPluginHealthStatus.Degraded, result.Status);
}
@@ -130,7 +130,7 @@ public sealed class SamlIdentityProviderPluginTests
public SamlPluginOptions CurrentValue => options;
public SamlPluginOptions Get(string name)
public SamlPluginOptions Get(string? name)
=> string.Equals(name, pluginName, StringComparison.Ordinal) ? options : options;
public IDisposable OnChange(Action<SamlPluginOptions, string> listener)

View File

@@ -122,13 +122,13 @@ public sealed class SamlConnectorSnapshotTests
{
// Arrange
var fixtureFiles = Directory.Exists(FixturesPath)
? Directory.EnumerateFiles(FixturesPath, "*.xml").Select(Path.GetFileNameWithoutExtension).ToList()
? Directory.EnumerateFiles(FixturesPath, "*.xml").Select(Path.GetFileNameWithoutExtension).Where(n => n is not null).Cast<string>().ToList()
: new List<string>();
var expectedFiles = Directory.Exists(ExpectedPath)
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
.ToList()
.Where(n => n is not null).Cast<string>().ToList()
: new List<string>();
// Assert

View File

@@ -39,7 +39,7 @@ public class StandardCredentialAuditLoggerTests
failureCode: null,
reason: null,
properties: Array.Empty<AuthEventProperty>(),
CancellationToken.None);
TestContext.Current.CancellationToken);
var record = Assert.Single(sink.Records);
Assert.Equal("authority.plugin.standard.password_verification", record.EventType);
@@ -92,7 +92,7 @@ public class StandardCredentialAuditLoggerTests
failureCode: AuthorityCredentialFailureCode.InvalidCredentials,
reason: "Invalid credentials.",
properties,
CancellationToken.None);
TestContext.Current.CancellationToken);
var record = Assert.Single(sink.Records);
Assert.Equal(AuthEventOutcome.Failure, record.Outcome);
@@ -147,7 +147,7 @@ public class StandardCredentialAuditLoggerTests
failureCode: AuthorityCredentialFailureCode.LockedOut,
reason: "Account locked.",
properties,
CancellationToken.None);
TestContext.Current.CancellationToken);
var record = Assert.Single(sink.Records);
Assert.Equal(AuthEventOutcome.LockedOut, record.Outcome);
@@ -189,7 +189,7 @@ public class StandardCredentialAuditLoggerTests
failureCode: AuthorityCredentialFailureCode.RequiresMfa,
reason: "MFA required.",
properties: null,
CancellationToken.None);
TestContext.Current.CancellationToken);
var record = Assert.Single(sink.Records);
var property = Assert.Single(record.Properties);

View File

@@ -43,7 +43,7 @@ public class StandardClaimsEnricherTests
var identity = new ClaimsIdentity();
var enricher = new StandardClaimsEnricher();
await enricher.EnrichAsync(identity, context, CancellationToken.None);
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "admin");
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "ops");

View File

@@ -32,7 +32,7 @@ public class StandardClientProvisioningStoreTests
allowedGrantTypes: new[] { "client_credentials" },
allowedScopes: new[] { "scopeA" });
var result = await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
var result = await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.True(store.Documents.TryGetValue("bootstrap-client", out var document));
@@ -40,7 +40,7 @@ public class StandardClientProvisioningStoreTests
Assert.Equal(AuthoritySecretHasher.ComputeHash("SuperSecret1!"), document!.SecretHash);
Assert.Equal("standard", document.Plugin);
var descriptor = await provisioning.FindByClientIdAsync("bootstrap-client", CancellationToken.None);
var descriptor = await provisioning.FindByClientIdAsync("bootstrap-client", TestContext.Current.CancellationToken);
Assert.NotNull(descriptor);
Assert.Equal("bootstrap-client", descriptor!.ClientId);
Assert.True(descriptor.Confidential);
@@ -65,13 +65,13 @@ public class StandardClientProvisioningStoreTests
allowedScopes: new[] { "scopeA" },
tenant: " Tenant-Alpha " );
await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
Assert.True(store.Documents.TryGetValue("tenant-client", out var document));
Assert.NotNull(document);
Assert.Equal("tenant-alpha", document!.Properties[AuthorityClientMetadataKeys.Tenant]);
var descriptor = await provisioning.FindByClientIdAsync("tenant-client", CancellationToken.None);
var descriptor = await provisioning.FindByClientIdAsync("tenant-client", TestContext.Current.CancellationToken);
Assert.NotNull(descriptor);
Assert.Equal("tenant-alpha", descriptor!.Tenant);
}
@@ -92,14 +92,14 @@ public class StandardClientProvisioningStoreTests
allowedScopes: new[] { "signer.sign" },
allowedAudiences: new[] { "attestor", "signer" });
var result = await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
var result = await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.True(store.Documents.TryGetValue("signer", out var document));
Assert.NotNull(document);
Assert.Equal("attestor signer", document!.Properties[AuthorityClientMetadataKeys.Audiences]);
var descriptor = await provisioning.FindByClientIdAsync("signer", CancellationToken.None);
var descriptor = await provisioning.FindByClientIdAsync("signer", TestContext.Current.CancellationToken);
Assert.NotNull(descriptor);
Assert.Equal(new[] { "attestor", "signer" }, descriptor!.AllowedAudiences.OrderBy(value => value, StringComparer.Ordinal));
}
@@ -132,7 +132,7 @@ public class StandardClientProvisioningStoreTests
allowedAudiences: new[] { "signer" },
certificateBindings: new[] { bindingRegistration });
await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
Assert.True(store.Documents.TryGetValue("mtls-client", out var document));
Assert.NotNull(document);
@@ -164,9 +164,9 @@ public class StandardClientProvisioningStoreTests
allowedGrantTypes: new[] { "client_credentials" },
allowedScopes: new[] { "scopeA" });
await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
var result = await provisioning.DeleteAsync("delete-me", CancellationToken.None);
var result = await provisioning.DeleteAsync("delete-me", TestContext.Current.CancellationToken);
Assert.True(result.Succeeded);
Assert.False(store.Documents.ContainsKey("delete-me"));

View File

@@ -58,7 +58,7 @@ public class StandardIdentityProviderPluginTests
new StandardClaimsEnricher(),
NullLogger<StandardIdentityProviderPlugin>.Instance);
var health = await plugin.CheckHealthAsync(CancellationToken.None);
var health = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
Assert.Equal(AuthorityPluginHealthStatus.Healthy, health.Status);
}

View File

@@ -67,7 +67,7 @@ public class StandardPluginBootstrapperTests
using var provider = services.BuildServiceProvider();
var bootstrapper = provider.GetRequiredService<StandardPluginBootstrapper>();
var exception = await Record.ExceptionAsync(() => bootstrapper.StartAsync(CancellationToken.None));
var exception = await Record.ExceptionAsync(() => bootstrapper.StartAsync(TestContext.Current.CancellationToken));
Assert.Null(exception);
}

View File

@@ -228,6 +228,7 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
var updated = await store.UpsertUserAsync(update, CancellationToken.None);
Assert.True(updated.Succeeded);
Assert.NotNull(updated.Value);
Assert.Contains("editor", updated.Value.Roles);
Assert.Contains("admin", updated.Value.Roles);
Assert.Equal("us", updated.Value.Attributes["region"]);
@@ -250,6 +251,7 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
var created = await store.UpsertUserAsync(registration, CancellationToken.None);
Assert.True(created.Succeeded);
Assert.NotNull(created.Value);
var found = await store.FindBySubjectAsync(created.Value.SubjectId, CancellationToken.None);
Assert.NotNull(found);

View File

@@ -115,7 +115,7 @@ public sealed class AdvisoryAiRemoteInferenceEndpointTests : IClassFixture<Autho
var expectedHash = ComputeSha256(payload.Prompt);
Assert.Equal(expectedHash, body["prompt_hash"]);
var doc = Assert.Single(lastLoginAttemptStore!.Records.Where(record => record.EventType == "authority.advisory_ai.remote_inference"));
var doc = Assert.Single(lastLoginAttemptStore!.Records, record => record.EventType == "authority.advisory_ai.remote_inference");
Assert.Equal("authority.advisory_ai.remote_inference", doc.EventType);
var properties = doc.Properties.ToDictionary(p => p.Name, p => p.Value);
Assert.Equal(expectedHash, properties["advisory_ai.prompt.hash"]);

View File

@@ -77,7 +77,7 @@ public class AuthorityAuditSinkTests
Assert.Equal(record.OccurredAt, document.OccurredAt);
Assert.Equal(new[] { "openid", "profile" }, document.Scopes);
var pluginProperty = Assert.Single(document.Properties.Where(property => property.Name == "plugin.failed_attempts"));
var pluginProperty = Assert.Single(document.Properties, property => property.Name == "plugin.failed_attempts");
Assert.Equal("0", pluginProperty.Value);
Assert.Equal("none", pluginProperty.Classification);

View File

@@ -2926,9 +2926,9 @@ public class ClientCredentialsHandlersTests
Assert.False(validateContext.IsRejected);
Assert.False(validateContext.Transaction.Properties.ContainsKey(AuthorityOpenIddictConstants.SenderConstraintProperty));
var bypassEvent = Assert.Single(auditSink.Events.Where(record => record.EventType == "authority.dpop.proof.bypass"));
var bypassEvent = Assert.Single(auditSink.Events, record => record.EventType == "authority.dpop.proof.bypass");
Assert.Equal(AuthEventOutcome.Success, bypassEvent.Outcome);
var reasonProperty = Assert.Single(bypassEvent.Properties.Where(property => property.Name == "dpop.reason_code"));
var reasonProperty = Assert.Single(bypassEvent.Properties, property => property.Name == "dpop.reason_code");
Assert.Equal("bypass", reasonProperty.Value.Value);
}
@@ -3387,7 +3387,7 @@ public class ClientCredentialsHandlersTests
var grantEvent = authSink.Events.LastOrDefault(evt => evt.EventType == "authority.client_credentials.grant");
Assert.NotNull(grantEvent);
var serviceProperty = Assert.Single(grantEvent!.Properties.Where(prop => prop.Name == "delegation.service_account"));
var serviceProperty = Assert.Single(grantEvent!.Properties, prop => prop.Name == "delegation.service_account");
Assert.Equal(serviceAccount.AccountId, serviceProperty.Value.Value);
var actorPropertyValues = grantEvent.Properties

View File

@@ -22,6 +22,9 @@ namespace StellaOps.Authority.ConfigDiff.Tests;
[Trait("BlastRadius", TestCategories.BlastRadius.Auth)]
public class AuthorityConfigDiffTests : ConfigDiffTestBase
{
private static readonly DateTimeOffset SnapshotTimestamp =
new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
/// <summary>
/// Initializes a new instance of the <see cref="AuthorityConfigDiffTests"/> class.
/// </summary>
@@ -61,7 +64,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
async config => await GetSessionBehaviorAsync(config),
async config => await GetRefreshBehaviorAsync(config),
async config => await GetAuthenticationBehaviorAsync(config)
]);
],
ct: TestContext.Current.CancellationToken);
// Assert
result.IsSuccess.Should().BeTrue(
@@ -93,7 +97,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
changedConfig,
getBehavior: async config => await CaptureSessionBehaviorAsync(config),
computeDelta: ComputeBehaviorSnapshotDelta,
expectedDelta: expectedDelta);
expectedDelta: expectedDelta,
ct: TestContext.Current.CancellationToken);
// Assert
result.IsSuccess.Should().BeTrue(
@@ -119,7 +124,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
[
async config => await GetSessionBehaviorAsync(config),
async config => await GetPasswordPolicyBehaviorAsync(config)
]);
],
ct: TestContext.Current.CancellationToken);
// Assert
result.IsSuccess.Should().BeTrue(
@@ -151,7 +157,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
changedConfig,
getBehavior: async config => await CapturePasswordPolicyBehaviorAsync(config),
computeDelta: ComputeBehaviorSnapshotDelta,
expectedDelta: expectedDelta);
expectedDelta: expectedDelta,
ct: TestContext.Current.CancellationToken);
// Assert
result.IsSuccess.Should().BeTrue();
@@ -176,7 +183,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
[
async config => await GetTokenBehaviorAsync(config),
async config => await GetSessionBehaviorAsync(config)
]);
],
ct: TestContext.Current.CancellationToken);
// Assert
result.IsSuccess.Should().BeTrue(
@@ -216,11 +224,11 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
ConfigurationId: $"sessions-{config.MaxConcurrentSessions}",
Behaviors:
[
new CapturedBehavior("SessionLimit", config.MaxConcurrentSessions.ToString(), DateTimeOffset.UtcNow),
new CapturedBehavior("SessionLimit", config.MaxConcurrentSessions.ToString(), SnapshotTimestamp),
new CapturedBehavior("ConcurrencyPolicy",
config.MaxConcurrentSessions > 5 ? "permissive" : "restrictive", DateTimeOffset.UtcNow)
config.MaxConcurrentSessions > 5 ? "permissive" : "restrictive", SnapshotTimestamp)
],
CapturedAt: DateTimeOffset.UtcNow);
CapturedAt: SnapshotTimestamp);
return Task.FromResult(snapshot);
}
@@ -232,11 +240,11 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
Behaviors:
[
new CapturedBehavior("PasswordComplexity",
config.MinPasswordLength >= 12 ? "enhanced" : "standard", DateTimeOffset.UtcNow),
config.MinPasswordLength >= 12 ? "enhanced" : "standard", SnapshotTimestamp),
new CapturedBehavior("ValidationRejectionRate",
config.MinPasswordLength >= 12 ? "increase" : "standard", DateTimeOffset.UtcNow)
config.MinPasswordLength >= 12 ? "increase" : "standard", SnapshotTimestamp)
],
CapturedAt: DateTimeOffset.UtcNow);
CapturedAt: SnapshotTimestamp);
return Task.FromResult(snapshot);
}

View File

@@ -33,7 +33,7 @@ public sealed class AuthorityPostgresFixture : PostgresIntegrationFixture, IColl
public PostgresOptions CreateOptions()
{
var options = Fixture.CreateOptions();
options.SchemaName = SchemaName;
options.SchemaName = AuthorityDataSource.DefaultSchemaName;
options.MaxPoolSize = 10;
options.MinPoolSize = 0;
return options;

View File

@@ -1,5 +1,4 @@
using System.Collections.Immutable;
using System.Threading;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -7,6 +6,7 @@ using Npgsql;
using StellaOps.Authority.Core.Verdicts;
using StellaOps.Authority.Persistence.Postgres;
using StellaOps.TestKit;
using System.Text.Json;
using Xunit;
namespace StellaOps.Authority.Persistence.Tests;
@@ -37,7 +37,7 @@ public sealed class VerdictManifestStoreTests : IAsyncLifetime
[Fact]
public async Task StoreAndGetById_RoundTripsManifest()
{
var evaluatedAt = DateTimeOffset.Parse("2025-01-15T10:00:00Z");
var evaluatedAt = new DateTimeOffset(2025, 1, 15, 10, 0, 0, TimeSpan.Zero);
var manifest = CreateManifest("tenant-1", "manifest-001", evaluatedAt, VexStatus.NotAffected);
await _store.StoreAsync(manifest);
@@ -58,12 +58,15 @@ public sealed class VerdictManifestStoreTests : IAsyncLifetime
[Fact]
public async Task StoreAsync_WritesStringEnumJson()
{
var evaluatedAt = DateTimeOffset.Parse("2025-01-15T11:00:00Z");
var evaluatedAt = new DateTimeOffset(2025, 1, 15, 11, 0, 0, TimeSpan.Zero);
var manifest = CreateManifest("tenant-2", "manifest-002", evaluatedAt, VexStatus.UnderInvestigation);
await _store.StoreAsync(manifest);
await using var conn = await _dataSource.OpenConnectionAsync(manifest.Tenant, "reader", CancellationToken.None);
await using var conn = await _dataSource.OpenConnectionAsync(
manifest.Tenant,
"reader",
TestContext.Current.CancellationToken);
await using var cmd = new NpgsqlCommand("""
SELECT result_json::text
FROM verdict_manifests
@@ -77,7 +80,8 @@ public sealed class VerdictManifestStoreTests : IAsyncLifetime
var json = (string?)await cmd.ExecuteScalarAsync();
json.Should().NotBeNull();
json.Should().Contain("\"status\":\"under_investigation\"");
using var document = JsonDocument.Parse(json!);
document.RootElement.GetProperty("status").GetString().Should().Be("under_investigation");
}
private static VerdictManifest CreateManifest(string tenant, string manifestId, DateTimeOffset evaluatedAt, VexStatus status)