Refactor code structure and optimize performance across multiple modules
This commit is contained in:
@@ -59,7 +59,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
await _npgsqlDataSource.DisposeAsync();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ParallelCreates_DifferentIds_All_Succeed()
|
||||
{
|
||||
// Arrange
|
||||
@@ -77,7 +78,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
allKeys.Should().HaveCount(parallelCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ConcurrentReads_SameKey_All_Succeed()
|
||||
{
|
||||
// Arrange
|
||||
@@ -97,7 +99,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
"all concurrent reads should return same key");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ParallelReadsDuringWrite_ReturnsConsistentState()
|
||||
{
|
||||
// Arrange
|
||||
@@ -124,7 +127,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ConcurrentUpdateLastUsed_SameKey_NoConflict()
|
||||
{
|
||||
// Arrange
|
||||
@@ -146,7 +150,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
result!.LastUsedAt.Should().NotBeNull("at least one update should have succeeded");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ParallelListOperations_NoDeadlock()
|
||||
{
|
||||
// Arrange - Create some keys first
|
||||
@@ -166,7 +171,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
completedInTime.Should().BeTrue("parallel list operations should not deadlock");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task MixedOperations_NoDeadlock()
|
||||
{
|
||||
// Arrange
|
||||
@@ -200,7 +206,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
completedInTime.Should().BeTrue("mixed operations should not deadlock");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RapidSuccessiveWrites_AllSucceed()
|
||||
{
|
||||
// Arrange
|
||||
@@ -217,7 +224,8 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
allKeys.Should().HaveCount(iterations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ConcurrentDeleteAndRead_ReturnsConsistentState()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
@@ -59,7 +59,8 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
await _npgsqlDataSource.DisposeAsync();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAsync_SameId_Twice_Should_Not_Duplicate()
|
||||
{
|
||||
// Arrange
|
||||
@@ -88,7 +89,8 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAsync_DifferentIds_SamePrefix_Should_Not_Duplicate()
|
||||
{
|
||||
// Arrange
|
||||
@@ -116,7 +118,8 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UpdateLastUsedAsync_Twice_Should_Be_Idempotent()
|
||||
{
|
||||
// Arrange
|
||||
@@ -138,7 +141,8 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
after2!.Id.Should().Be(key.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RevokeAsync_Twice_Should_Be_Idempotent()
|
||||
{
|
||||
// Arrange
|
||||
@@ -160,7 +164,8 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
after2!.Status.Should().Be(ApiKeyStatus.Revoked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task DeleteAsync_Twice_Should_Be_Idempotent()
|
||||
{
|
||||
// Arrange
|
||||
@@ -182,7 +187,8 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
afterSecond.Should().BeNull("second delete should also succeed");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAsync_Multiple_Keys_For_Same_User_Allowed()
|
||||
{
|
||||
// Arrange - Create 5 keys for same user
|
||||
|
||||
@@ -5,6 +5,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -32,7 +33,8 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAndGetByPrefix_RoundTripsApiKey()
|
||||
{
|
||||
var keyPrefix = "sk_live_" + Guid.NewGuid().ToString("N")[..8];
|
||||
@@ -59,7 +61,8 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
fetched.Scopes.Should().BeEquivalentTo(["scan:read", "scan:write"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetById_ReturnsApiKey()
|
||||
{
|
||||
var apiKey = CreateApiKey(Guid.NewGuid(), "Test Key");
|
||||
@@ -72,7 +75,8 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
fetched!.Name.Should().Be("Test Key");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByUserId_ReturnsUserApiKeys()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
@@ -87,7 +91,8 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
keys.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task List_ReturnsAllKeysForTenant()
|
||||
{
|
||||
var key1 = CreateApiKey(Guid.NewGuid(), "Key A");
|
||||
@@ -101,7 +106,8 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
keys.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Revoke_UpdatesStatusAndRevokedFields()
|
||||
{
|
||||
var apiKey = CreateApiKey(Guid.NewGuid(), "ToRevoke");
|
||||
@@ -116,7 +122,8 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
fetched.RevokedBy.Should().Be("security@test.com");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Delete_RemovesApiKey()
|
||||
{
|
||||
var apiKey = CreateApiKey(Guid.NewGuid(), "DeleteKey");
|
||||
|
||||
@@ -5,6 +5,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -27,7 +28,8 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
public Task InitializeAsync() => _fixture.TruncateAllTablesAsync();
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Create_ReturnsGeneratedId()
|
||||
{
|
||||
// Arrange
|
||||
@@ -50,7 +52,8 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
id.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task List_ReturnsAuditEntriesOrderedByCreatedAtDesc()
|
||||
{
|
||||
// Arrange
|
||||
@@ -68,7 +71,8 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
audits[0].Action.Should().Be("action2"); // Most recent first
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByUserId_ReturnsUserAudits()
|
||||
{
|
||||
// Arrange
|
||||
@@ -90,7 +94,8 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
audits[0].UserId.Should().Be(userId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByResource_ReturnsResourceAudits()
|
||||
{
|
||||
// Arrange
|
||||
@@ -112,7 +117,8 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
audits[0].ResourceId.Should().Be(resourceId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByCorrelationId_ReturnsCorrelatedAudits()
|
||||
{
|
||||
// Arrange
|
||||
@@ -142,7 +148,8 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
audits.Should().AllSatisfy(a => a.CorrelationId.Should().Be(correlationId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByAction_ReturnsMatchingAudits()
|
||||
{
|
||||
// Arrange
|
||||
@@ -158,7 +165,8 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
audits.Should().AllSatisfy(a => a.Action.Should().Be("user.login"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Create_StoresJsonbValues()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
@@ -2,6 +2,8 @@ using FluentAssertions;
|
||||
using Npgsql;
|
||||
using Xunit;
|
||||
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
/// <summary>
|
||||
@@ -17,7 +19,8 @@ public sealed class AuthorityMigrationTests
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task MigrationsApplied_SchemaHasTables()
|
||||
{
|
||||
// Arrange
|
||||
@@ -47,7 +50,8 @@ public sealed class AuthorityMigrationTests
|
||||
// Add more specific table assertions based on Authority migrations
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task MigrationsApplied_SchemaVersionRecorded()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
@@ -5,6 +5,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -26,7 +27,8 @@ public sealed class OfflineKitAuditRepositoryTests : IAsyncLifetime
|
||||
public Task InitializeAsync() => _fixture.TruncateAllTablesAsync();
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Insert_ThenList_ReturnsRecord()
|
||||
{
|
||||
var tenantId = Guid.NewGuid().ToString("N");
|
||||
@@ -52,7 +54,8 @@ public sealed class OfflineKitAuditRepositoryTests : IAsyncLifetime
|
||||
listed[0].Details.Should().Contain("kitFilename");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task List_WithFilters_ReturnsMatchingRows()
|
||||
{
|
||||
var tenantId = Guid.NewGuid().ToString("N");
|
||||
@@ -88,7 +91,8 @@ public sealed class OfflineKitAuditRepositoryTests : IAsyncLifetime
|
||||
validated[0].EventType.Should().Be("IMPORT_VALIDATED");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task List_IsTenantIsolated()
|
||||
{
|
||||
var tenantA = Guid.NewGuid().ToString("N");
|
||||
|
||||
@@ -5,6 +5,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -32,7 +33,8 @@ public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAndGet_RoundTripsPermission()
|
||||
{
|
||||
var permission = new PermissionEntity
|
||||
@@ -54,7 +56,8 @@ public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
fetched.Action.Should().Be("read");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByName_ReturnsCorrectPermission()
|
||||
{
|
||||
var permission = BuildPermission("tokens:revoke", "tokens", "revoke", "Revoke tokens");
|
||||
@@ -66,7 +69,8 @@ public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
fetched!.Action.Should().Be("revoke");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByResource_ReturnsResourcePermissions()
|
||||
{
|
||||
var p1 = BuildPermission("users:read", "users", "read", "Read");
|
||||
@@ -79,7 +83,8 @@ public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
perms.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task List_ReturnsAllPermissionsForTenant()
|
||||
{
|
||||
var p1 = BuildPermission("orch:read", "orch", "read", "Read orch");
|
||||
@@ -92,7 +97,8 @@ public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
perms.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Delete_RemovesPermission()
|
||||
{
|
||||
var permission = BuildPermission("tokens:revoke", "tokens", "revoke", "Revoke tokens");
|
||||
|
||||
@@ -6,6 +6,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -33,7 +34,8 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAndGetByHash_RoundTripsRefreshToken()
|
||||
{
|
||||
var refresh = BuildToken(Guid.NewGuid());
|
||||
@@ -47,7 +49,8 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
fetched!.Id.Should().Be(refresh.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetById_ReturnsToken()
|
||||
{
|
||||
var refresh = BuildToken(Guid.NewGuid());
|
||||
@@ -61,7 +64,8 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
fetched!.UserId.Should().Be(refresh.UserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByUserId_ReturnsUserTokens()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
@@ -77,7 +81,8 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
tokens.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Revoke_SetsRevokedFields()
|
||||
{
|
||||
var refresh = BuildToken(Guid.NewGuid());
|
||||
@@ -92,7 +97,8 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
fetched.RevokedBy.Should().Be("tester");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RevokeByUserId_RevokesAllUserTokens()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
@@ -111,7 +117,8 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
revoked2!.RevokedAt.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Revoke_WithReplacedBy_SetsReplacedByField()
|
||||
{
|
||||
var refresh = BuildToken(Guid.NewGuid());
|
||||
@@ -126,7 +133,8 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
fetched!.ReplacedBy.Should().Be(newTokenId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByUserId_IsDeterministic_WhenIssuedAtTies()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
@@ -13,6 +13,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
/// <summary>
|
||||
@@ -56,7 +57,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
|
||||
#region User-Role Assignment Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithRole_GetsRolePermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -81,7 +83,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
userPermissions.Should().Contain(p => p.Resource == "scanner" && p.Action == "view");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithoutRole_HasNoPermissions_DenyByDefault()
|
||||
{
|
||||
// Arrange
|
||||
@@ -101,7 +104,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
userPermissions.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithExpiredRole_HasNoPermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -124,7 +128,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
userPermissions.Should().BeEmpty("expired role should not grant permissions");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithFutureExpiryRole_HasPermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -148,7 +153,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
userPermissions.Should().Contain(p => p.Resource == "policy" && p.Action == "read");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithNoExpiryRole_HasPermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -174,7 +180,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
|
||||
#region Multiple Roles Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithMultipleRoles_AccumulatesPermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -209,7 +216,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
userPermissions.Should().Contain(p => p.Action == "delete");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithOverlappingRolePermissions_GetsDistinctPermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -239,7 +247,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
userPermissions.Select(p => p.Id).Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task UserWithOneExpiredRole_StillHasOtherRolePermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -277,7 +286,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
|
||||
#region Role Removal Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RemovingRole_RemovesPermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -303,7 +313,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
afterRemoval.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RemovingPermissionFromRole_AffectsAllUsersWithRole()
|
||||
{
|
||||
// Arrange
|
||||
@@ -337,7 +348,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
|
||||
#region Role Permission Enforcement Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetRolePermissions_ReturnsOnlyAssignedPermissions()
|
||||
{
|
||||
// Arrange
|
||||
@@ -357,7 +369,8 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
rolePermissions.Should().NotContain(p => p.Resource == "notallowed");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task SystemRole_CanHaveSpecialPermissions()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
@@ -5,6 +5,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -32,7 +33,8 @@ public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAndGet_RoundTripsRole()
|
||||
{
|
||||
var role = BuildRole("Admin");
|
||||
@@ -44,7 +46,8 @@ public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
fetched!.Name.Should().Be("Admin");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByName_ReturnsCorrectRole()
|
||||
{
|
||||
var role = BuildRole("Reader");
|
||||
@@ -56,7 +59,8 @@ public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
fetched!.Description.Should().Be("Reader role");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task List_ReturnsAllRolesForTenant()
|
||||
{
|
||||
await _repository.CreateAsync(_tenantId, BuildRole("Reader"));
|
||||
@@ -67,7 +71,8 @@ public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
roles.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Update_ModifiesRole()
|
||||
{
|
||||
var role = BuildRole("Updater");
|
||||
@@ -92,7 +97,8 @@ public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
fetched!.Description.Should().Be("Updated description");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Delete_RemovesRole()
|
||||
{
|
||||
var role = BuildRole("Deleter");
|
||||
|
||||
@@ -5,6 +5,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -32,7 +33,8 @@ public sealed class SessionRepositoryTests : IAsyncLifetime
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAndGet_RoundTripsSession()
|
||||
{
|
||||
var session = BuildSession();
|
||||
@@ -45,7 +47,8 @@ public sealed class SessionRepositoryTests : IAsyncLifetime
|
||||
fetched!.Id.Should().Be(session.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByTokenHash_ReturnsSession()
|
||||
{
|
||||
var session = BuildSession();
|
||||
@@ -58,7 +61,8 @@ public sealed class SessionRepositoryTests : IAsyncLifetime
|
||||
fetched!.UserId.Should().Be(session.UserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task EndByUserId_EndsAllUserSessions()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
@@ -6,6 +6,7 @@ using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using Xunit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Storage.Postgres.Tests;
|
||||
|
||||
[Collection(AuthorityPostgresCollection.Name)]
|
||||
@@ -32,7 +33,8 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
}
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task CreateAndGetByHash_RoundTripsToken()
|
||||
{
|
||||
// Arrange
|
||||
@@ -61,7 +63,8 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
fetched.Scopes.Should().BeEquivalentTo(["read", "write"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetById_ReturnsToken()
|
||||
{
|
||||
// Arrange
|
||||
@@ -77,7 +80,8 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
fetched!.Id.Should().Be(token.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByUserId_ReturnsUserTokens()
|
||||
{
|
||||
// Arrange
|
||||
@@ -95,7 +99,8 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
tokens.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Revoke_SetsRevokedFields()
|
||||
{
|
||||
// Arrange
|
||||
@@ -112,7 +117,8 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
fetched.RevokedBy.Should().Be("admin@test.com");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task RevokeByUserId_RevokesAllUserTokens()
|
||||
{
|
||||
// Arrange
|
||||
@@ -133,7 +139,8 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
revoked2!.RevokedAt.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetByUserId_IsDeterministic_WhenIssuedAtTies()
|
||||
{
|
||||
// Arrange: same IssuedAt, fixed IDs to validate ordering
|
||||
|
||||
Reference in New Issue
Block a user