Skip to content

[TEST] Encina.Audit.Marten integration tests — real Marten store end-to-end #951

@dlrivada

Description

@dlrivada

Test Category

  • Unit Tests
  • Integration Tests (Docker/Testcontainers)
  • Property-Based Tests (FsCheck)
  • Contract Tests
  • Guard Clause Tests
  • Load Tests (NBomber)
  • Benchmark Tests (BenchmarkDotNet)
  • Coverage Gap (below 85% target)

Description

Encina.Audit.Marten currently has no integration tests — the folder tests/Encina.IntegrationTests/AuditMarten/ does not exist. This gap allowed #949 (IServiceProvider rejected by JasperFx projection validator) to ship undetected: every unit test bypassed Marten's projection graph, so ConfigureMartenAuditProjections.Configure(StoreOptions) was never actually invoked end-to-end in CI.

The fix for #949 added a unit test (Configure_ValidOptions_RegistersAuditProjections) that exercises the StoreOptions.Projections.Add(...) path on a plain StoreOptions instance, which is sufficient to regression-lock the InvalidProjectionException. However, the full async projection daemon, temporal key lookup via IDocumentOperations, and crypto-shredding lifecycle have never been exercised against a real PostgreSQL-backed Marten store.

This issue tracks building that integration coverage.

Packages / Providers Affected

  • Package(s): Encina.Audit.Marten
  • Provider(s): Marten (PostgreSQL)

Current Coverage

Package Test Types Present Missing
Encina.Audit.Marten Unit (124), Guard (83) Integration (0)

Per CLAUDE.md database test policy: "For database-related milestones, DO NOT create .md justification files for IntegrationTests — create real IntegrationTests using Docker/Testcontainers. The whole point of database features is to interact with real databases."

Infrastructure Required

  • Docker / Testcontainers
  • Real database — PostgreSQL (via existing postgres:17-alpine service in docker-compose.yml)
  • Message broker
  • NBomber load testing framework
  • BenchmarkDotNet
  • None (pure unit tests)

A MartenAuditFixture needs to be created following the same pattern as EFCorePostgreSqlFixture: a collection fixture that boots Marten against the shared PostgreSQL container and runs ClearAllDataAsync() between tests (dropping the mt_doc_audit_entry_read_model, mt_doc_read_audit_entry_read_model, mt_doc_temporal_key_document, mt_doc_temporal_key_destroyed_marker, and mt_events tables).

Test Plan

Tests to Implement

Store registration & boot

Write path (MartenAuditStore)

  • AddAsync_PersistsEncryptedEntry_AndAppendsAuditEntryRecordedEvent
  • AddAsync_MultipleEntries_SameTemporalPeriod_ReuseSameKey
  • AddAsync_EntriesInDifferentPeriods_CreateSeparateKeys

Read path (MartenReadAuditStore)

  • AddReadAsync_PersistsEncryptedReadEntry

Async projection daemon (AuditEntryProjection.Create)

  • DaemonRebuild_ProjectsAllEventsIntoAuditEntryReadModel_WithDecryptedPii — starts the async daemon, waits for high-water-mark catch-up, then asserts the AuditEntryReadModel documents match the events with decrypted PII fields
  • DaemonRebuild_WithDestroyedTemporalKey_ProjectsShreddedPlaceholder — creates events, destroys the key via DestroyKeysBeforeAsync, rebuilds projection, asserts IsShredded == true and UserId == \"[SHREDDED]\"
  • DaemonRebuild_WithCustomPlaceholder_ProjectsCustomPlaceholder — verifies MartenAuditOptions.ShreddedPlaceholder is wired through the projection constructor

Read audit projection (ReadAuditEntryProjection.Create)

  • DaemonRebuild_ProjectsAllReadEvents_WithDecryptedPii
  • DaemonRebuild_ReadEvent_WithDestroyedKey_ProjectsShreddedPlaceholder

Temporal key lifecycle

  • DestroyKeysBeforeAsync_RemovesKeyDocuments_AndStoresDestroyedMarker
  • DestroyKeysBeforeAsync_IdempotentForAlreadyDestroyedPeriods
  • GetOrCreateKeyAsync_AfterDestruction_ReturnsKeyNotFoundError

Retention service (MartenAuditRetentionService)

  • MartenAuditRetentionService_CrossesRetentionBoundary_DestroysExpiredKeys

Success Criteria

Collection Fixture (Integration Tests Only)

  • Collection: Marten-AuditMarten (new — per-package collection since Marten audit has its own document schema)
  • Fixture: MartenAuditFixture (new) — reuses the existing shared PostgreSQL container via the postgres docker-compose service, but scopes the Marten document store to a dedicated schema (encina_audit_test) to avoid collisions with other Marten integration tests

Alternatively, this could use the EFCorePostgreSqlFixture connection string with a unique DatabaseSchemaName — evaluate which approach minimizes containers during implementation.

Related Issues

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-auditAudit trails and change trackingarea-complianceGDPR, EU Laws, and Regulatory Compliance patternsarea-event-sourcingEvent Sourcing patterns and infrastructurearea-martenMarten event sourcing providerarea-testingTesting utilities and frameworkscomplexity-mediumComplexity: MediumdogfoodingUsing Encina.Testing infrastructure in Encina's own testsmarten-integrationMarten library integrationpriority-highPriority: High (⭐⭐⭐⭐)testing-integrationIntegration testing utilities

    Projects

    Status
    Todo

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions