Tests fixes, audit progress, UI completions
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# Auth Abstractions Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Auth.Abstractions.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: unit coverage for scopes, claims, principal builder, network masks, and problem responses.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs, stable ordering).
|
||||
- Use explicit assertions for scope lists and network mask behavior.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit.
|
||||
- Add edge-case coverage for parsing and canonicalization.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Auth Abstractions Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0079-M | DONE | Maintainability audit for StellaOps.Auth.Abstractions.Tests. |
|
||||
| AUDIT-0079-T | DONE | Test coverage audit for StellaOps.Auth.Abstractions.Tests. |
|
||||
| AUDIT-0079-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Auth Abstractions AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: shared auth scopes, claim types, problem responses, and network mask utilities.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep auth identifiers deterministic and stable (no implicit ordering changes).
|
||||
- Preserve offline posture (no network calls).
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit.
|
||||
- Cover scope normalization, network masks, principal builder behavior, and problem responses.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Auth Abstractions Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0078-M | DONE | Maintainability audit for StellaOps.Auth.Abstractions. |
|
||||
| AUDIT-0078-T | DONE | Test coverage audit for StellaOps.Auth.Abstractions. |
|
||||
| AUDIT-0078-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Auth Client Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Auth.Client.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: unit coverage for auth client options, caches, and auth handlers.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs, stable ordering).
|
||||
- Avoid live network calls and file system leakage; clean temp artifacts.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit.
|
||||
- Cover token caches, discovery/JWKS fallback, and bearer handler modes.
|
||||
@@ -77,7 +77,7 @@ public class ServiceCollectionExtensionsTests
|
||||
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
|
||||
Assert.Equal(2, attemptCount);
|
||||
Assert.NotEmpty(recordedHandlers);
|
||||
Assert.Contains(recordedHandlers, handler => handler.GetType().Name.Contains("PolicyHttpMessageHandler", StringComparison.Ordinal));
|
||||
Assert.Contains(recordedHandlers, handler => handler.GetType().Name.Contains("ResilienceHandler", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Auth Client Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0081-M | DONE | Maintainability audit for StellaOps.Auth.Client.Tests. |
|
||||
| AUDIT-0081-T | DONE | Test coverage audit for StellaOps.Auth.Client.Tests. |
|
||||
| AUDIT-0081-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,23 @@
|
||||
# Auth Client AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Auth.Client/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: token acquisition, discovery/JWKS caching, and auth handler integration.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep token flows deterministic and time-aware (TimeProvider, skew handling).
|
||||
- Respect offline/air-gap posture and egress policy checks.
|
||||
- Avoid leaking sensitive credentials in logs.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit.
|
||||
- Cover discovery/JWKS caches, token client error paths, and auth handler behavior.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Auth Client Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0080-M | DONE | Maintainability audit for StellaOps.Auth.Client. |
|
||||
| AUDIT-0080-T | DONE | Test coverage audit for StellaOps.Auth.Client. |
|
||||
| AUDIT-0080-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Auth Server Integration Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Auth.ServerIntegration.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: resource server options, policy registration, bypass evaluation, and scope authorization behavior.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs, stable ordering).
|
||||
- Avoid live network calls; use fakes for metadata/JWKS retrieval.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit.
|
||||
- Cover options validation, bypass deny paths, scope normalization, and audit event emission.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Auth Server Integration Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0084-M | DONE | Maintainability audit for StellaOps.Auth.ServerIntegration.Tests. |
|
||||
| AUDIT-0084-T | DONE | Test coverage audit for StellaOps.Auth.ServerIntegration.Tests. |
|
||||
| AUDIT-0084-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,23 @@
|
||||
# Auth Server Integration AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Auth.ServerIntegration/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: ASP.NET Core resource server auth configuration, scope policies, and authorization audit events.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep auth decisions deterministic and time-aware (TimeProvider).
|
||||
- Preserve offline/air-gap posture with resilient metadata/JWKS caching.
|
||||
- Avoid logging sensitive claims; use classified strings.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit.
|
||||
- Cover options normalization, bypass evaluation, metadata/JWKS caching, scope decisions, and audit event emission.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Auth Server Integration Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0083-M | DONE | Maintainability audit for StellaOps.Auth.ServerIntegration. |
|
||||
| AUDIT-0083-T | DONE | Test coverage audit for StellaOps.Auth.ServerIntegration. |
|
||||
| AUDIT-0083-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority LDAP Plugin Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Ldap.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: LDAP plugin test coverage, fixtures, and determinism.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs); avoid real LDAP network access.
|
||||
- Prefer exercising production code paths over duplicated test-only logic.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit fakes.
|
||||
- Tag integration/snapshot tests appropriately and keep fixtures stable.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority LDAP Plugin Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0091-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Ldap.Tests. |
|
||||
| AUDIT-0091-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Ldap.Tests. |
|
||||
| AUDIT-0091-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority LDAP Plugin AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Ldap/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: LDAP identity provider plugin, connection factory, claims enrichment, and client provisioning.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Preserve TLS and credential handling guarantees; avoid weakening defaults.
|
||||
- Keep timeouts and bind flows configurable; avoid hidden sync-over-async paths.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit fakes; avoid real LDAP network access.
|
||||
- Cover health checks, capability probing, and error/timeout paths deterministically.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority LDAP Plugin Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0090-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Ldap. |
|
||||
| AUDIT-0090-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Ldap. |
|
||||
| AUDIT-0090-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority OIDC Plugin Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Oidc.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: OIDC plugin test coverage, fixtures, and determinism.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs); avoid live OIDC metadata calls.
|
||||
- Prefer exercising production code paths over test-only simulations.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + local fixtures.
|
||||
- Tag snapshot and security tests appropriately and keep fixtures stable.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority OIDC Plugin Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0093-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Oidc.Tests. |
|
||||
| AUDIT-0093-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Oidc.Tests. |
|
||||
| AUDIT-0093-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority OIDC Plugin AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Oidc/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: OIDC identity provider plugin, token validation, metadata retrieval, and claims mapping.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Preserve offline/air-gap posture; avoid implicit external calls without explicit configuration.
|
||||
- Use IHttpClientFactory and configurable timeouts; avoid new HttpClient per call.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + deterministic token fixtures.
|
||||
- Avoid live OIDC network calls; mock metadata retrieval and token validation.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority OIDC Plugin Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0092-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Oidc. |
|
||||
| AUDIT-0092-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Oidc. |
|
||||
| AUDIT-0092-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority SAML Plugin Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Saml.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: SAML plugin test coverage, fixtures, and determinism.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs); avoid live IdP metadata calls.
|
||||
- Prefer exercising production code paths over test-only simulations.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + fixture XML.
|
||||
- Tag snapshot and security tests appropriately and keep fixtures stable.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority SAML Plugin Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0095-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Saml.Tests. |
|
||||
| AUDIT-0095-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Saml.Tests. |
|
||||
| AUDIT-0095-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,23 @@
|
||||
# Authority SAML Plugin AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Saml/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: SAML identity provider plugin, assertion validation, metadata/certificate handling, and claims mapping.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Preserve offline/air-gap posture; avoid implicit external calls without explicit configuration.
|
||||
- Use IHttpClientFactory and configurable timeouts; avoid new HttpClient per call.
|
||||
- Harden XML parsing (no DTD/XXE) and keep assertion validation deterministic.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions with deterministic fixture XML.
|
||||
- Avoid live IdP network calls; mock metadata/cert retrieval.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority SAML Plugin Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0094-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Saml. |
|
||||
| AUDIT-0094-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Saml. |
|
||||
| AUDIT-0094-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,21 @@
|
||||
# Authority Standard Plugin Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Standard.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: Standard plugin test coverage, credential flows, and determinism.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/dev/31_AUTHORITY_PLUGIN_DEVELOPER_GUIDE.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs); avoid external network calls.
|
||||
- Prefer exercising production code paths over test-only simulations.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit helpers.
|
||||
- Cover credential flows, lockouts, bootstrap behavior, and client provisioning.
|
||||
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.InMemoryDriver;
|
||||
using StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Standard;
|
||||
using StellaOps.Authority.Plugin.Standard.Bootstrap;
|
||||
@@ -16,16 +17,16 @@ using StellaOps.Authority.Plugin.Standard.Storage;
|
||||
using StellaOps.Authority.Persistence.Documents;
|
||||
using StellaOps.Authority.Persistence.InMemory.Stores;
|
||||
using StellaOps.Authority.Persistence.Sessions;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
public class StandardPluginRegistrarTests
|
||||
{
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public async Task Register_ConfiguresIdentityProviderAndSeedsBootstrapUser()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -58,10 +59,11 @@ public class StandardPluginRegistrarTests
|
||||
"standard.yaml");
|
||||
|
||||
var pluginContext = new AuthorityPluginContext(manifest, configuration);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database, configuration);
|
||||
|
||||
var registrar = new StandardPluginRegistrar();
|
||||
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
|
||||
services.AddSingleton<ICryptoProvider>(new DefaultCryptoProvider());
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
var hostedServices = provider.GetServices<IHostedService>();
|
||||
@@ -88,7 +90,7 @@ public class StandardPluginRegistrarTests
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public void Register_LogsWarning_WhenPasswordPolicyWeaker()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -116,12 +118,13 @@ public class StandardPluginRegistrarTests
|
||||
"standard.yaml");
|
||||
|
||||
var pluginContext = new AuthorityPluginContext(manifest, configuration);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database, configuration);
|
||||
var loggerProvider = new CapturingLoggerProvider();
|
||||
services.AddLogging(builder => builder.AddProvider(loggerProvider));
|
||||
|
||||
var registrar = new StandardPluginRegistrar();
|
||||
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
|
||||
services.AddSingleton<ICryptoProvider>(new DefaultCryptoProvider());
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
using var scope = provider.CreateScope();
|
||||
@@ -134,7 +137,7 @@ public class StandardPluginRegistrarTests
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public void Register_ForcesPasswordCapability_WhenManifestMissing()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -152,10 +155,11 @@ public class StandardPluginRegistrarTests
|
||||
"standard.yaml");
|
||||
|
||||
var pluginContext = new AuthorityPluginContext(manifest, configuration);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database, configuration);
|
||||
|
||||
var registrar = new StandardPluginRegistrar();
|
||||
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
|
||||
services.AddSingleton<ICryptoProvider>(new DefaultCryptoProvider());
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
using var scope = provider.CreateScope();
|
||||
@@ -167,7 +171,7 @@ public class StandardPluginRegistrarTests
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public void Register_Throws_WhenBootstrapConfigurationIncomplete()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -191,10 +195,11 @@ public class StandardPluginRegistrarTests
|
||||
"standard.yaml");
|
||||
|
||||
var pluginContext = new AuthorityPluginContext(manifest, configuration);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database, configuration);
|
||||
|
||||
var registrar = new StandardPluginRegistrar();
|
||||
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
|
||||
services.AddSingleton<ICryptoProvider>(new DefaultCryptoProvider());
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
using var scope = provider.CreateScope();
|
||||
@@ -202,7 +207,7 @@ public class StandardPluginRegistrarTests
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public void Register_NormalizesTokenSigningKeyDirectory()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
@@ -232,11 +237,12 @@ public class StandardPluginRegistrarTests
|
||||
configPath);
|
||||
|
||||
var pluginContext = new AuthorityPluginContext(manifest, configuration);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database);
|
||||
var services = StandardPluginRegistrarTestHelpers.CreateServiceCollection(database, configuration);
|
||||
services.AddSingleton(TimeProvider.System);
|
||||
|
||||
var registrar = new StandardPluginRegistrar();
|
||||
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
|
||||
services.AddSingleton<ICryptoProvider>(new DefaultCryptoProvider());
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<StandardPluginOptions>>();
|
||||
@@ -398,6 +404,7 @@ internal static class StandardPluginRegistrarTestHelpers
|
||||
{
|
||||
public static ServiceCollection CreateServiceCollection(
|
||||
IDatabase database,
|
||||
IConfiguration? configuration = null,
|
||||
IAuthEventSink? authEventSink = null,
|
||||
IAuthorityCredentialAuditContextAccessor? auditContextAccessor = null)
|
||||
{
|
||||
@@ -405,10 +412,12 @@ internal static class StandardPluginRegistrarTestHelpers
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddSingleton(configuration ?? new ConfigurationBuilder().Build());
|
||||
services.AddSingleton(database);
|
||||
services.AddSingleton<IAuthorityClientStore>(new InMemoryClientStore());
|
||||
services.AddSingleton<IAuthorityRevocationStore>(new StubRevocationStore());
|
||||
services.AddSingleton<IAuthorityLoginAttemptStore>(new InMemoryLoginAttemptStore());
|
||||
services.AddSingleton<IUserRepository>(new InMemoryUserRepository());
|
||||
services.AddSingleton(TimeProvider.System);
|
||||
services.AddSingleton<IAuthorityCredentialAuditContextAccessor>(
|
||||
auditContextAccessor ?? new TestAuthorityCredentialAuditContextAccessor());
|
||||
|
||||
@@ -2,12 +2,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using StellaOps.Authority.InMemoryDriver;
|
||||
using StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Standard.Security;
|
||||
using StellaOps.Authority.Plugin.Standard.Storage;
|
||||
@@ -15,20 +14,18 @@ using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
{
|
||||
private readonly IDatabase database;
|
||||
private readonly InMemoryUserRepository userRepository;
|
||||
private readonly StandardPluginOptions options;
|
||||
private readonly StandardUserCredentialStore store;
|
||||
private readonly TestAuditLogger auditLogger;
|
||||
private readonly Mock<IUserRepository> userRepositoryMock;
|
||||
|
||||
public StandardUserCredentialStoreTests()
|
||||
{
|
||||
var client = new InMemoryClient();
|
||||
database = client.GetDatabase("authority-tests");
|
||||
options = new StandardPluginOptions
|
||||
{
|
||||
PasswordPolicy = new PasswordPolicyOptions
|
||||
@@ -55,11 +52,11 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
};
|
||||
var cryptoProvider = new DefaultCryptoProvider();
|
||||
auditLogger = new TestAuditLogger();
|
||||
userRepositoryMock = new Mock<IUserRepository>();
|
||||
userRepository = new InMemoryUserRepository();
|
||||
store = new StandardUserCredentialStore(
|
||||
"standard",
|
||||
"test-tenant",
|
||||
userRepositoryMock.Object,
|
||||
userRepository,
|
||||
options,
|
||||
new CryptoPasswordHasher(options, cryptoProvider),
|
||||
auditLogger,
|
||||
@@ -67,7 +64,7 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_ReturnsSuccess_ForValidCredentials()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
@@ -95,7 +92,7 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_EnforcesLockout_AfterRepeatedFailures()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
@@ -144,7 +141,7 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_RehashesLegacyHashesToArgon2()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
@@ -156,19 +153,24 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
Iterations = 160_000
|
||||
});
|
||||
|
||||
var document = new StandardUserDocument
|
||||
await userRepository.CreateAsync(new UserEntity
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = "test-tenant",
|
||||
Username = "legacy",
|
||||
NormalizedUsername = "legacy",
|
||||
Email = "legacy@local",
|
||||
DisplayName = "Legacy",
|
||||
PasswordHash = legacyHash,
|
||||
Roles = new List<string>(),
|
||||
Attributes = new Dictionary<string, string?>(),
|
||||
CreatedAt = DateTimeOffset.UtcNow.AddDays(-1),
|
||||
UpdatedAt = DateTimeOffset.UtcNow.AddDays(-1)
|
||||
};
|
||||
|
||||
await database.GetCollection<StandardUserDocument>("authority_users_standard")
|
||||
.InsertOneAsync(document);
|
||||
PasswordSalt = "",
|
||||
Enabled = true,
|
||||
Metadata = JsonSerializer.Serialize(new Dictionary<string, object?>
|
||||
{
|
||||
["subjectId"] = "legacy",
|
||||
["roles"] = new List<string>(),
|
||||
["attributes"] = new Dictionary<string, string?>(),
|
||||
["requirePasswordReset"] = false
|
||||
})
|
||||
});
|
||||
|
||||
var result = await store.VerifyPasswordAsync("legacy", "Legacy1!", CancellationToken.None);
|
||||
|
||||
@@ -180,16 +182,14 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
Assert.True(auditEntry.Success);
|
||||
Assert.Equal("legacy", auditEntry.Username);
|
||||
|
||||
var results = await database.GetCollection<StandardUserDocument>("authority_users_standard")
|
||||
.FindAsync(u => u.NormalizedUsername == "legacy");
|
||||
var updated = results.FirstOrDefault();
|
||||
var updated = await userRepository.GetByUsernameAsync("test-tenant", "legacy", CancellationToken.None);
|
||||
|
||||
Assert.NotNull(updated);
|
||||
Assert.StartsWith("$argon2id$", updated!.PasswordHash, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_RecordsAudit_ForUnknownUser()
|
||||
{
|
||||
auditLogger.Reset();
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Standard Plugin Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0097-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Standard.Tests. |
|
||||
| AUDIT-0097-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Standard.Tests. |
|
||||
| AUDIT-0097-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
using StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
internal sealed class InMemoryUserRepository : IUserRepository
|
||||
{
|
||||
private readonly Dictionary<Guid, UserEntity> users = new();
|
||||
private readonly Dictionary<string, Guid> byUsername = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<string, Guid> byEmail = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public Task<UserEntity> CreateAsync(UserEntity user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var created = new UserEntity
|
||||
{
|
||||
Id = user.Id,
|
||||
TenantId = user.TenantId,
|
||||
Username = user.Username,
|
||||
Email = user.Email,
|
||||
DisplayName = user.DisplayName,
|
||||
PasswordHash = user.PasswordHash,
|
||||
PasswordSalt = user.PasswordSalt,
|
||||
Enabled = user.Enabled,
|
||||
EmailVerified = user.EmailVerified,
|
||||
MfaEnabled = user.MfaEnabled,
|
||||
MfaSecret = user.MfaSecret,
|
||||
MfaBackupCodes = user.MfaBackupCodes,
|
||||
FailedLoginAttempts = user.FailedLoginAttempts,
|
||||
LockedUntil = user.LockedUntil,
|
||||
LastLoginAt = user.LastLoginAt,
|
||||
PasswordChangedAt = user.PasswordChangedAt,
|
||||
Settings = string.IsNullOrWhiteSpace(user.Settings) ? "{}" : user.Settings,
|
||||
Metadata = string.IsNullOrWhiteSpace(user.Metadata) ? "{}" : user.Metadata,
|
||||
CreatedAt = user.CreatedAt == default ? now : user.CreatedAt,
|
||||
UpdatedAt = user.UpdatedAt == default ? now : user.UpdatedAt,
|
||||
CreatedBy = user.CreatedBy
|
||||
};
|
||||
|
||||
users[created.Id] = created;
|
||||
byUsername[GetUsernameKey(created.TenantId, created.Username)] = created.Id;
|
||||
byEmail[GetEmailKey(created.TenantId, created.Email)] = created.Id;
|
||||
|
||||
return Task.FromResult(created);
|
||||
}
|
||||
|
||||
public Task<UserEntity?> GetByIdAsync(string tenantId, Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (users.TryGetValue(id, out var user) && string.Equals(user.TenantId, tenantId, StringComparison.Ordinal))
|
||||
{
|
||||
return Task.FromResult<UserEntity?>(user);
|
||||
}
|
||||
|
||||
return Task.FromResult<UserEntity?>(null);
|
||||
}
|
||||
|
||||
public Task<UserEntity?> GetByUsernameAsync(string tenantId, string username, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var key = GetUsernameKey(tenantId, username);
|
||||
if (byUsername.TryGetValue(key, out var id) && users.TryGetValue(id, out var user))
|
||||
{
|
||||
return Task.FromResult<UserEntity?>(user);
|
||||
}
|
||||
|
||||
return Task.FromResult<UserEntity?>(null);
|
||||
}
|
||||
|
||||
public Task<UserEntity?> GetByEmailAsync(string tenantId, string email, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var key = GetEmailKey(tenantId, email);
|
||||
if (byEmail.TryGetValue(key, out var id) && users.TryGetValue(id, out var user))
|
||||
{
|
||||
return Task.FromResult<UserEntity?>(user);
|
||||
}
|
||||
|
||||
return Task.FromResult<UserEntity?>(null);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<UserEntity>> GetAllAsync(
|
||||
string tenantId,
|
||||
bool? enabled = null,
|
||||
int limit = 100,
|
||||
int offset = 0,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var results = users.Values
|
||||
.Where(u => string.Equals(u.TenantId, tenantId, StringComparison.Ordinal))
|
||||
.Where(u => enabled is null || u.Enabled == enabled.Value)
|
||||
.OrderBy(u => u.Username, StringComparer.OrdinalIgnoreCase)
|
||||
.Skip(offset)
|
||||
.Take(limit)
|
||||
.ToList();
|
||||
|
||||
return Task.FromResult<IReadOnlyList<UserEntity>>(results);
|
||||
}
|
||||
|
||||
public Task<bool> UpdateAsync(UserEntity user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!users.TryGetValue(user.Id, out var existing))
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var updated = new UserEntity
|
||||
{
|
||||
Id = user.Id,
|
||||
TenantId = user.TenantId,
|
||||
Username = user.Username,
|
||||
Email = user.Email,
|
||||
DisplayName = user.DisplayName,
|
||||
PasswordHash = user.PasswordHash,
|
||||
PasswordSalt = user.PasswordSalt,
|
||||
Enabled = user.Enabled,
|
||||
EmailVerified = user.EmailVerified,
|
||||
MfaEnabled = user.MfaEnabled,
|
||||
MfaSecret = user.MfaSecret,
|
||||
MfaBackupCodes = user.MfaBackupCodes,
|
||||
FailedLoginAttempts = user.FailedLoginAttempts,
|
||||
LockedUntil = user.LockedUntil,
|
||||
LastLoginAt = user.LastLoginAt,
|
||||
PasswordChangedAt = user.PasswordChangedAt,
|
||||
Settings = string.IsNullOrWhiteSpace(user.Settings) ? existing.Settings : user.Settings,
|
||||
Metadata = string.IsNullOrWhiteSpace(user.Metadata) ? existing.Metadata : user.Metadata,
|
||||
CreatedAt = existing.CreatedAt,
|
||||
UpdatedAt = now,
|
||||
CreatedBy = user.CreatedBy ?? existing.CreatedBy
|
||||
};
|
||||
|
||||
users[updated.Id] = updated;
|
||||
byUsername[GetUsernameKey(updated.TenantId, updated.Username)] = updated.Id;
|
||||
byEmail[GetEmailKey(updated.TenantId, updated.Email)] = updated.Id;
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<bool> DeleteAsync(string tenantId, Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (users.TryGetValue(id, out var user) && string.Equals(user.TenantId, tenantId, StringComparison.Ordinal))
|
||||
{
|
||||
users.Remove(id);
|
||||
byUsername.Remove(GetUsernameKey(user.TenantId, user.Username));
|
||||
byEmail.Remove(GetEmailKey(user.TenantId, user.Email));
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public Task<bool> UpdatePasswordAsync(
|
||||
string tenantId,
|
||||
Guid userId,
|
||||
string passwordHash,
|
||||
string passwordSalt,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!users.TryGetValue(userId, out var existing) || !string.Equals(existing.TenantId, tenantId, StringComparison.Ordinal))
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var updated = new UserEntity
|
||||
{
|
||||
Id = existing.Id,
|
||||
TenantId = existing.TenantId,
|
||||
Username = existing.Username,
|
||||
Email = existing.Email,
|
||||
DisplayName = existing.DisplayName,
|
||||
PasswordHash = passwordHash,
|
||||
PasswordSalt = passwordSalt,
|
||||
Enabled = existing.Enabled,
|
||||
EmailVerified = existing.EmailVerified,
|
||||
MfaEnabled = existing.MfaEnabled,
|
||||
MfaSecret = existing.MfaSecret,
|
||||
MfaBackupCodes = existing.MfaBackupCodes,
|
||||
FailedLoginAttempts = existing.FailedLoginAttempts,
|
||||
LockedUntil = existing.LockedUntil,
|
||||
LastLoginAt = existing.LastLoginAt,
|
||||
PasswordChangedAt = now,
|
||||
Settings = existing.Settings,
|
||||
Metadata = existing.Metadata,
|
||||
CreatedAt = existing.CreatedAt,
|
||||
UpdatedAt = now,
|
||||
CreatedBy = existing.CreatedBy
|
||||
};
|
||||
|
||||
users[updated.Id] = updated;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<int> RecordFailedLoginAsync(
|
||||
string tenantId,
|
||||
Guid userId,
|
||||
DateTimeOffset? lockUntil = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!users.TryGetValue(userId, out var existing) || !string.Equals(existing.TenantId, tenantId, StringComparison.Ordinal))
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var attempts = existing.FailedLoginAttempts + 1;
|
||||
var updated = new UserEntity
|
||||
{
|
||||
Id = existing.Id,
|
||||
TenantId = existing.TenantId,
|
||||
Username = existing.Username,
|
||||
Email = existing.Email,
|
||||
DisplayName = existing.DisplayName,
|
||||
PasswordHash = existing.PasswordHash,
|
||||
PasswordSalt = existing.PasswordSalt,
|
||||
Enabled = existing.Enabled,
|
||||
EmailVerified = existing.EmailVerified,
|
||||
MfaEnabled = existing.MfaEnabled,
|
||||
MfaSecret = existing.MfaSecret,
|
||||
MfaBackupCodes = existing.MfaBackupCodes,
|
||||
FailedLoginAttempts = attempts,
|
||||
LockedUntil = lockUntil,
|
||||
LastLoginAt = existing.LastLoginAt,
|
||||
PasswordChangedAt = existing.PasswordChangedAt,
|
||||
Settings = existing.Settings,
|
||||
Metadata = existing.Metadata,
|
||||
CreatedAt = existing.CreatedAt,
|
||||
UpdatedAt = now,
|
||||
CreatedBy = existing.CreatedBy
|
||||
};
|
||||
|
||||
users[updated.Id] = updated;
|
||||
return Task.FromResult(attempts);
|
||||
}
|
||||
|
||||
public Task RecordSuccessfulLoginAsync(string tenantId, Guid userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!users.TryGetValue(userId, out var existing) || !string.Equals(existing.TenantId, tenantId, StringComparison.Ordinal))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var updated = new UserEntity
|
||||
{
|
||||
Id = existing.Id,
|
||||
TenantId = existing.TenantId,
|
||||
Username = existing.Username,
|
||||
Email = existing.Email,
|
||||
DisplayName = existing.DisplayName,
|
||||
PasswordHash = existing.PasswordHash,
|
||||
PasswordSalt = existing.PasswordSalt,
|
||||
Enabled = existing.Enabled,
|
||||
EmailVerified = existing.EmailVerified,
|
||||
MfaEnabled = existing.MfaEnabled,
|
||||
MfaSecret = existing.MfaSecret,
|
||||
MfaBackupCodes = existing.MfaBackupCodes,
|
||||
FailedLoginAttempts = 0,
|
||||
LockedUntil = null,
|
||||
LastLoginAt = now,
|
||||
PasswordChangedAt = existing.PasswordChangedAt,
|
||||
Settings = existing.Settings,
|
||||
Metadata = existing.Metadata,
|
||||
CreatedAt = existing.CreatedAt,
|
||||
UpdatedAt = now,
|
||||
CreatedBy = existing.CreatedBy
|
||||
};
|
||||
|
||||
users[updated.Id] = updated;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static string GetUsernameKey(string tenantId, string username)
|
||||
=> $"{tenantId}::{username}".ToLowerInvariant();
|
||||
|
||||
private static string GetEmailKey(string tenantId, string email)
|
||||
=> $"{tenantId}::{email}".ToLowerInvariant();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Standard Plugin Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0096-M | DONE | Maintainability audit for StellaOps.Authority.Plugin.Standard. |
|
||||
| AUDIT-0096-T | DONE | Test coverage audit for StellaOps.Authority.Plugin.Standard. |
|
||||
| AUDIT-0096-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority Plugin Abstractions Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugins.Abstractions.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: contract type validation, normalization, and edge-case behavior.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs); avoid external network calls.
|
||||
- Prefer explicit assertions over documentation-only tests.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + TestKit.
|
||||
- Cover capability parsing, normalization logic, and validation failures.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Plugin Abstractions Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0099-M | DONE | Maintainability audit for StellaOps.Authority.Plugins.Abstractions.Tests. |
|
||||
| AUDIT-0099-T | DONE | Test coverage audit for StellaOps.Authority.Plugins.Abstractions.Tests. |
|
||||
| AUDIT-0099-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,21 @@
|
||||
# Authority Plugin Abstractions AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority.Plugins.Abstractions/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: Authority plugin contracts, identity provider abstractions, and shared metadata types.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep contracts deterministic (stable ordering, normalized data).
|
||||
- Avoid breaking changes without coordinating downstream plugin implementations.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + TestKit; cover normalization, validation, and edge cases for contract types.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Plugin Abstractions Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0098-M | DONE | Maintainability audit for StellaOps.Authority.Plugins.Abstractions. |
|
||||
| AUDIT-0098-T | DONE | Test coverage audit for StellaOps.Authority.Plugins.Abstractions. |
|
||||
| AUDIT-0098-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,29 @@
|
||||
# Authority Tests Charter
|
||||
|
||||
## Mission
|
||||
Own the Authority test suite for the Authority web service and shared components. Ensure coverage for auth flows, policy enforcement, and deterministic behavior.
|
||||
|
||||
## Responsibilities
|
||||
- Maintain `StellaOps.Authority.Tests` and supporting test utilities.
|
||||
- Keep tests deterministic and offline-friendly; avoid external dependencies unless explicitly approved.
|
||||
- Surface open work on `TASKS.md`; update statuses (TODO/DOING/DONE/BLOCKED/REVIEW).
|
||||
|
||||
## Key Paths
|
||||
- `Infrastructure/AuthorityWebApplicationFactory.cs`
|
||||
- `OpenIddict/*`, `Auth/*`, `Bootstrap/*`, `Notifications/*`, `Observability/*`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
|
||||
## Coordination
|
||||
- Authority Core and Security guilds for auth flows and crypto.
|
||||
- Observability guild for trace/metrics assertions.
|
||||
|
||||
## Required Reading
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
|
||||
## Working Agreement
|
||||
- 1. Update task status to `DOING`/`DONE` in both corresponding sprint file `/docs/implplan/SPRINT_*.md` and the local `TASKS.md` when you start or finish work.
|
||||
- 2. Review this charter and the Required Reading documents before coding; confirm prerequisites are met.
|
||||
- 3. Keep changes deterministic (stable ordering, timestamps, hashes) and align with offline/air-gap expectations.
|
||||
- 4. Coordinate doc updates, tests, and cross-guild communication whenever contracts or workflows change.
|
||||
- 5. Revert to `TODO` if you pause the task without shipping changes; leave notes in commit/PR descriptions for context.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0100-M | DONE | Maintainability audit for StellaOps.Authority.Tests. |
|
||||
| AUDIT-0100-T | DONE | Test coverage audit for StellaOps.Authority.Tests. |
|
||||
| AUDIT-0100-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,25 @@
|
||||
# Authority Service AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/StellaOps.Authority/StellaOps.Authority/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: Authority web service composition, OpenIddict flows, plugins, storage adapters, and security controls.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- `docs/architecture/console-admin-rbac.md`
|
||||
- `docs/architecture/console-branding.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep auth flows deterministic (TimeProvider/ID generators where feasible).
|
||||
- Preserve offline/air-gap posture and avoid new hard network dependencies.
|
||||
- Audit events must stay structured and avoid leaking secrets.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + TestKit.
|
||||
- Cover OpenIddict handlers, auth audit sinks, storage adapters, and policy enforcement.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Service Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0085-M | DONE | Maintainability audit for StellaOps.Authority. |
|
||||
| AUDIT-0085-T | DONE | Test coverage audit for StellaOps.Authority. |
|
||||
| AUDIT-0085-A | TODO | Pending approval for changes. |
|
||||
22
src/Authority/__Libraries/StellaOps.Authority.Core/AGENTS.md
Normal file
22
src/Authority/__Libraries/StellaOps.Authority.Core/AGENTS.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Authority Core AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/__Libraries/StellaOps.Authority.Core/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: verdict manifests, replay verification, manifest signing interfaces, and deterministic serialization.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Preserve deterministic ordering and timestamps (TimeProvider where possible).
|
||||
- Keep manifests replayable with explicit inputs and stable serialization.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions.
|
||||
- Cover manifest builder/serializer, replay verification, and store pagination.
|
||||
10
src/Authority/__Libraries/StellaOps.Authority.Core/TASKS.md
Normal file
10
src/Authority/__Libraries/StellaOps.Authority.Core/TASKS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Authority Core Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0086-M | DONE | Maintainability audit for StellaOps.Authority.Core. |
|
||||
| AUDIT-0086-T | DONE | Test coverage audit for StellaOps.Authority.Core. |
|
||||
| AUDIT-0086-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority Persistence AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/__Libraries/StellaOps.Authority.Persistence/`.
|
||||
- Roles: backend engineer, QA automation.
|
||||
- Focus: Authority persistence layer (Postgres repositories, in-memory stores, migrations, and EF Core scaffolding).
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Preserve deterministic ordering and timestamps; avoid implicit NOW()/UtcNow for testable paths.
|
||||
- Keep schema usage consistent with configured Postgres options.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + Moq.
|
||||
- Cover repository CRUD, pagination, schema overrides, and in-memory store behavior.
|
||||
@@ -2,8 +2,6 @@
|
||||
-- Consolidated from migrations 001-005 (pre_1.0 archived)
|
||||
-- Creates the complete authority schema for IAM, tenants, users, tokens, RLS, and audit
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 1: Schema Creation
|
||||
-- ============================================================================
|
||||
@@ -78,15 +76,20 @@ CREATE TABLE IF NOT EXISTS authority.users (
|
||||
display_name TEXT,
|
||||
password_hash TEXT,
|
||||
password_salt TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
password_algorithm TEXT DEFAULT 'argon2id',
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'locked', 'deleted')),
|
||||
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
mfa_secret TEXT,
|
||||
mfa_backup_codes TEXT,
|
||||
failed_login_attempts INT NOT NULL DEFAULT 0,
|
||||
locked_until TIMESTAMPTZ,
|
||||
last_login_at TIMESTAMPTZ,
|
||||
password_changed_at TIMESTAMPTZ,
|
||||
last_password_change_at TIMESTAMPTZ,
|
||||
password_expires_at TIMESTAMPTZ,
|
||||
settings JSONB NOT NULL DEFAULT '{}',
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
@@ -606,4 +609,3 @@ BEGIN
|
||||
END
|
||||
$$;
|
||||
|
||||
COMMIT;
|
||||
|
||||
@@ -121,7 +121,7 @@ public sealed class TenantRepository : RepositoryBase<AuthorityDataSource>, ITen
|
||||
public async Task<bool> UpdateAsync(TenantEntity tenant, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
UPDATE auth.tenants
|
||||
UPDATE authority.tenants
|
||||
SET name = @name,
|
||||
description = @description,
|
||||
contact_email = @contact_email,
|
||||
@@ -152,7 +152,7 @@ public sealed class TenantRepository : RepositoryBase<AuthorityDataSource>, ITen
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = "DELETE FROM auth.tenants WHERE id = @id";
|
||||
const string sql = "DELETE FROM authority.tenants WHERE id = @id";
|
||||
|
||||
var rows = await ExecuteAsync(
|
||||
SystemTenantId,
|
||||
@@ -166,7 +166,7 @@ public sealed class TenantRepository : RepositoryBase<AuthorityDataSource>, ITen
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> SlugExistsAsync(string slug, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = "SELECT EXISTS(SELECT 1 FROM auth.tenants WHERE slug = @slug)";
|
||||
const string sql = "SELECT EXISTS(SELECT 1 FROM authority.tenants WHERE slug = @slug)";
|
||||
|
||||
var result = await ExecuteScalarAsync<bool>(
|
||||
SystemTenantId,
|
||||
|
||||
@@ -22,7 +22,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
public async Task<UserEntity> CreateAsync(UserEntity user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
INSERT INTO auth.users (
|
||||
INSERT INTO authority.users (
|
||||
id, tenant_id, username, email, display_name, password_hash, password_salt,
|
||||
enabled, email_verified, mfa_enabled, mfa_secret, mfa_backup_codes,
|
||||
settings, metadata, created_by
|
||||
@@ -58,7 +58,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
enabled, email_verified, mfa_enabled, mfa_secret, mfa_backup_codes,
|
||||
failed_login_attempts, locked_until, last_login_at, password_changed_at,
|
||||
settings::text, metadata::text, created_at, updated_at, created_by
|
||||
FROM auth.users
|
||||
FROM authority.users
|
||||
WHERE tenant_id = @tenant_id AND id = @id
|
||||
""";
|
||||
|
||||
@@ -82,7 +82,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
enabled, email_verified, mfa_enabled, mfa_secret, mfa_backup_codes,
|
||||
failed_login_attempts, locked_until, last_login_at, password_changed_at,
|
||||
settings::text, metadata::text, created_at, updated_at, created_by
|
||||
FROM auth.users
|
||||
FROM authority.users
|
||||
WHERE tenant_id = @tenant_id AND username = @username
|
||||
""";
|
||||
|
||||
@@ -106,7 +106,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
enabled, email_verified, mfa_enabled, mfa_secret, mfa_backup_codes,
|
||||
failed_login_attempts, locked_until, last_login_at, password_changed_at,
|
||||
settings::text, metadata::text, created_at, updated_at, created_by
|
||||
FROM auth.users
|
||||
FROM authority.users
|
||||
WHERE tenant_id = @tenant_id AND email = @email
|
||||
""";
|
||||
|
||||
@@ -135,7 +135,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
enabled, email_verified, mfa_enabled, mfa_secret, mfa_backup_codes,
|
||||
failed_login_attempts, locked_until, last_login_at, password_changed_at,
|
||||
settings::text, metadata::text, created_at, updated_at, created_by
|
||||
FROM auth.users
|
||||
FROM authority.users
|
||||
WHERE tenant_id = @tenant_id
|
||||
""";
|
||||
|
||||
@@ -167,7 +167,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
public async Task<bool> UpdateAsync(UserEntity user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
UPDATE auth.users
|
||||
UPDATE authority.users
|
||||
SET username = @username,
|
||||
email = @email,
|
||||
display_name = @display_name,
|
||||
@@ -207,7 +207,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> DeleteAsync(string tenantId, Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = "DELETE FROM auth.users WHERE tenant_id = @tenant_id AND id = @id";
|
||||
const string sql = "DELETE FROM authority.users WHERE tenant_id = @tenant_id AND id = @id";
|
||||
|
||||
var rows = await ExecuteAsync(
|
||||
tenantId,
|
||||
@@ -231,7 +231,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
UPDATE auth.users
|
||||
UPDATE authority.users
|
||||
SET password_hash = @password_hash,
|
||||
password_salt = @password_salt,
|
||||
password_changed_at = NOW()
|
||||
@@ -261,7 +261,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
UPDATE auth.users
|
||||
UPDATE authority.users
|
||||
SET failed_login_attempts = failed_login_attempts + 1,
|
||||
locked_until = @locked_until
|
||||
WHERE tenant_id = @tenant_id AND id = @id
|
||||
@@ -289,7 +289,7 @@ public sealed class UserRepository : RepositoryBase<AuthorityDataSource>, IUserR
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
UPDATE auth.users
|
||||
UPDATE authority.users
|
||||
SET failed_login_attempts = 0,
|
||||
locked_until = NULL,
|
||||
last_login_at = NOW()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Persistence Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0088-M | DONE | Maintainability audit for StellaOps.Authority.Persistence. |
|
||||
| AUDIT-0088-T | DONE | Test coverage audit for StellaOps.Authority.Persistence. |
|
||||
| AUDIT-0088-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority Core Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/__Tests/StellaOps.Authority.Core.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: verdict manifests, serialization, replay verification, and store behaviors.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs, stable ordering).
|
||||
- Avoid live network calls; use fakes for signing and evaluation.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + Moq.
|
||||
- Cover manifest builder/serializer, replay verification, and store pagination/filters.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Core Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0087-M | DONE | Maintainability audit for StellaOps.Authority.Core.Tests. |
|
||||
| AUDIT-0087-T | DONE | Test coverage audit for StellaOps.Authority.Core.Tests. |
|
||||
| AUDIT-0087-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority Persistence Tests AGENTS
|
||||
|
||||
## Purpose & Scope
|
||||
- Working directory: `src/Authority/__Tests/StellaOps.Authority.Persistence.Tests/`.
|
||||
- Roles: QA automation, backend engineer.
|
||||
- Focus: PostgreSQL repository behavior, migrations, concurrency, and storage correctness.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/authority/architecture.md`
|
||||
- Relevant sprint files.
|
||||
|
||||
## Working Agreements
|
||||
- Keep tests deterministic (fixed time/IDs); tag integration tests correctly.
|
||||
- Avoid network calls beyond local Postgres fixtures.
|
||||
- Update `docs/implplan/SPRINT_*.md` and local `TASKS.md` when starting or completing work.
|
||||
|
||||
## Testing
|
||||
- Use xUnit + FluentAssertions + Moq + TestKit fixtures.
|
||||
- Cover repository CRUD, migrations, concurrency, and pagination edge cases.
|
||||
@@ -31,6 +31,7 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private ApiKeyRepository _repository = null!;
|
||||
private AuthorityDataSource? _dataSource;
|
||||
private NpgsqlDataSource _npgsqlDataSource = null!;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
private readonly Guid _userId = Guid.NewGuid();
|
||||
@@ -44,10 +45,9 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
{
|
||||
await _fixture.TruncateAllTablesAsync();
|
||||
|
||||
var options = _fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = _fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new ApiKeyRepository(dataSource, NullLogger<ApiKeyRepository>.Instance);
|
||||
var options = _fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new ApiKeyRepository(_dataSource, NullLogger<ApiKeyRepository>.Instance);
|
||||
_npgsqlDataSource = NpgsqlDataSource.Create(_fixture.ConnectionString);
|
||||
|
||||
await SeedTenantAsync();
|
||||
@@ -57,6 +57,10 @@ public sealed class ApiKeyConcurrencyTests : IAsyncLifetime
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await _npgsqlDataSource.DisposeAsync();
|
||||
if (_dataSource is not null)
|
||||
{
|
||||
await _dataSource.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
|
||||
@@ -31,6 +31,7 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private ApiKeyRepository _repository = null!;
|
||||
private AuthorityDataSource? _dataSource;
|
||||
private NpgsqlDataSource _npgsqlDataSource = null!;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
private readonly Guid _userId = Guid.NewGuid();
|
||||
@@ -44,10 +45,9 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
{
|
||||
await _fixture.TruncateAllTablesAsync();
|
||||
|
||||
var options = _fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = _fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new ApiKeyRepository(dataSource, NullLogger<ApiKeyRepository>.Instance);
|
||||
var options = _fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new ApiKeyRepository(_dataSource, NullLogger<ApiKeyRepository>.Instance);
|
||||
_npgsqlDataSource = NpgsqlDataSource.Create(_fixture.ConnectionString);
|
||||
|
||||
await SeedTenantAsync();
|
||||
@@ -57,6 +57,10 @@ public sealed class ApiKeyIdempotencyTests : IAsyncLifetime
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await _npgsqlDataSource.DisposeAsync();
|
||||
if (_dataSource is not null)
|
||||
{
|
||||
await _dataSource.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
private readonly ApiKeyRepository _repository;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -19,10 +20,9 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new ApiKeyRepository(dataSource, NullLogger<ApiKeyRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new ApiKeyRepository(_dataSource, NullLogger<ApiKeyRepository>.Instance);
|
||||
}
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
@@ -31,7 +31,7 @@ public sealed class ApiKeyRepositoryTests : IAsyncLifetime
|
||||
await SeedTenantAsync();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
private readonly AuditRepository _repository;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -19,14 +20,13 @@ public sealed class AuditRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new AuditRepository(dataSource, NullLogger<AuditRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new AuditRepository(_dataSource, NullLogger<AuditRepository>.Instance);
|
||||
}
|
||||
|
||||
public ValueTask InitializeAsync() => new(_fixture.TruncateAllTablesAsync());
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
using System.Reflection;
|
||||
using StellaOps.Authority.Persistence.Postgres;
|
||||
using StellaOps.Infrastructure.Postgres.Testing;
|
||||
using StellaOps.Infrastructure.Postgres.Options;
|
||||
using StellaOps.TestKit;
|
||||
using StellaOps.TestKit.Fixtures;
|
||||
using Xunit;
|
||||
@@ -28,6 +29,15 @@ public sealed class AuthorityPostgresFixture : PostgresIntegrationFixture, IColl
|
||||
=> typeof(AuthorityDataSource).Assembly;
|
||||
|
||||
protected override string GetModuleName() => "Authority";
|
||||
|
||||
public PostgresOptions CreateOptions()
|
||||
{
|
||||
var options = Fixture.CreateOptions();
|
||||
options.SchemaName = SchemaName;
|
||||
options.MaxPoolSize = 10;
|
||||
options.MinPoolSize = 0;
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,19 +13,19 @@ public sealed class OfflineKitAuditRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly OfflineKitAuditRepository _repository;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
|
||||
public OfflineKitAuditRepositoryTests(AuthorityPostgresFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new OfflineKitAuditRepository(dataSource, NullLogger<OfflineKitAuditRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new OfflineKitAuditRepository(_dataSource, NullLogger<OfflineKitAuditRepository>.Instance);
|
||||
}
|
||||
|
||||
public ValueTask InitializeAsync() => new(_fixture.TruncateAllTablesAsync());
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
private readonly PermissionRepository _repository;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -19,10 +20,9 @@ public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new PermissionRepository(dataSource, NullLogger<PermissionRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new PermissionRepository(_dataSource, NullLogger<PermissionRepository>.Instance);
|
||||
}
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
@@ -31,7 +31,7 @@ public sealed class PermissionRepositoryTests : IAsyncLifetime
|
||||
await SeedTenantAsync();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
private readonly RefreshTokenRepository _repository;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -20,10 +21,9 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new RefreshTokenRepository(dataSource, NullLogger<RefreshTokenRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new RefreshTokenRepository(_dataSource, NullLogger<RefreshTokenRepository>.Instance);
|
||||
}
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
@@ -32,7 +32,7 @@ public sealed class RefreshTokenRepositoryTests : IAsyncLifetime
|
||||
await SeedTenantAsync();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private AuthorityDataSource? _dataSource;
|
||||
private RoleRepository _roleRepository = null!;
|
||||
private PermissionRepository _permissionRepository = null!;
|
||||
private UserRepository _userRepository = null!;
|
||||
@@ -42,18 +43,23 @@ public sealed class RoleBasedAccessTests : IAsyncLifetime
|
||||
{
|
||||
await _fixture.TruncateAllTablesAsync();
|
||||
|
||||
var options = _fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = _fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
var options = _fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
|
||||
_roleRepository = new RoleRepository(dataSource, NullLogger<RoleRepository>.Instance);
|
||||
_permissionRepository = new PermissionRepository(dataSource, NullLogger<PermissionRepository>.Instance);
|
||||
_userRepository = new UserRepository(dataSource, NullLogger<UserRepository>.Instance);
|
||||
_roleRepository = new RoleRepository(_dataSource, NullLogger<RoleRepository>.Instance);
|
||||
_permissionRepository = new PermissionRepository(_dataSource, NullLogger<PermissionRepository>.Instance);
|
||||
_userRepository = new UserRepository(_dataSource, NullLogger<UserRepository>.Instance);
|
||||
|
||||
await SeedTenantAsync();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_dataSource is not null)
|
||||
{
|
||||
await _dataSource.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
#region User-Role Assignment Tests
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
private readonly RoleRepository _repository;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -19,10 +20,9 @@ public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new RoleRepository(dataSource, NullLogger<RoleRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new RoleRepository(_dataSource, NullLogger<RoleRepository>.Instance);
|
||||
}
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
@@ -31,7 +31,7 @@ public sealed class RoleRepositoryTests : IAsyncLifetime
|
||||
await SeedTenantAsync();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class SessionRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
private readonly SessionRepository _repository;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -19,10 +20,9 @@ public sealed class SessionRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new SessionRepository(dataSource, NullLogger<SessionRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new SessionRepository(_dataSource, NullLogger<SessionRepository>.Instance);
|
||||
}
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
@@ -31,7 +31,7 @@ public sealed class SessionRepositoryTests : IAsyncLifetime
|
||||
await SeedTenantAsync();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Authority Persistence Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0089-M | DONE | Maintainability audit for StellaOps.Authority.Persistence.Tests. |
|
||||
| AUDIT-0089-T | DONE | Test coverage audit for StellaOps.Authority.Persistence.Tests. |
|
||||
| AUDIT-0089-A | TODO | Pending approval for changes. |
|
||||
@@ -13,6 +13,7 @@ namespace StellaOps.Authority.Persistence.Tests;
|
||||
public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
private readonly AuthorityPostgresFixture _fixture;
|
||||
private readonly AuthorityDataSource _dataSource;
|
||||
private readonly TokenRepository _repository;
|
||||
private readonly string _tenantId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -20,10 +21,9 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
{
|
||||
_fixture = fixture;
|
||||
|
||||
var options = fixture.Fixture.CreateOptions();
|
||||
options.SchemaName = fixture.SchemaName;
|
||||
var dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new TokenRepository(dataSource, NullLogger<TokenRepository>.Instance);
|
||||
var options = fixture.CreateOptions();
|
||||
_dataSource = new AuthorityDataSource(Options.Create(options), NullLogger<AuthorityDataSource>.Instance);
|
||||
_repository = new TokenRepository(_dataSource, NullLogger<TokenRepository>.Instance);
|
||||
}
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
@@ -31,7 +31,7 @@ public sealed class TokenRepositoryTests : IAsyncLifetime
|
||||
await _fixture.TruncateAllTablesAsync();
|
||||
await SeedTenantAsync();
|
||||
}
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user