elsa-core/test/unit/Elsa.UserTasks.Persistence.ConformanceTests
Sipke Schoorstra 249bde780b
fix(user-tasks): align query sort and cursor contract across providers (#8144)
* fix(user-tasks): align query sort and cursor contract across providers

Implement the REST updated sort on EF and VNext, drop the InMemory-only
completed sort, and use one always-ascending Id tiebreaker plus the
production JSON base64url cursor codec in every repository.

Closes #8110

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* fix(user-tasks): keep title cursor ties comparison-consistent

Use the same string.Compare relation for title ordering and cursor
tie detection so culture-equal Unicode forms are not skipped. Assert
updated sort order directly so a created fallback cannot pass.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* fix(user-tasks): use one ordinal comparer for title sort and cursors

OrderBy, title-tie detection, and cursor filtering now share
StringComparer.Ordinal on the LINQ-to-objects providers so Unicode
variants cannot skip a page.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* fix(user-tasks): share default title comparison across providers

InMemory and VNext title sort/cursors now use the same OrderBy plus
string.Compare relation as EF (SQL collation / current culture) so a
title cursor stays portable. Ties still use Compare == 0, not ordinal ==.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* docs(user-tasks): title cursors are provider-scoped, not portable

Restore InMemory/VNext ordinal title OrderBy/tie/cursor (EF stays
column-collation). Document that title pages must be recreated after a
provider or collation change; other sorts plus Id stay portable.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-14 06:43:03 +02:00
..
Faults
Infrastructure
Providers
ConformanceCoverageTests.cs
Elsa.UserTasks.Persistence.ConformanceTests.csproj
ProviderConformanceSuites.cs
README.md
UserTaskConformanceTestBase.cs fix(user-tasks): align query sort and cursor contract across providers (#8144) 2026-09-14 06:43:03 +02:00
UserTaskFaultInjectionConformanceTests.cs feat(user-tasks)!: migrate endpoints to structured permissions (#7999) 2026-08-27 12:06:52 +02:00
UserTaskGuestSessionConformanceTests.cs
UserTaskInvitationOutboxConformanceTests.cs
UserTaskRepositoryConformanceTests.cs fix(user-tasks): align query sort and cursor contract across providers (#8144) 2026-09-14 06:43:03 +02:00

User Tasks persistence conformance suite

One suite, run unchanged against every implementation of the User Tasks persistence contracts. It exists because the User Tasks build shipped four P1 defects that a single-threaded, happy-path, in-memory test suite could not see — every one of them was found by injecting a failure against a real store.

What it covers

Contract Suite Implementations
IUserTaskRepository UserTaskRepositoryConformanceTests InMemory, EF Core, VNext
IUserTaskGuestSessionIssuer UserTaskGuestSessionConformanceTests InMemory, EF Core
IUserTaskInvitationOutbox UserTaskInvitationOutboxConformanceTests InMemory, EF Core
The services above them UserTaskFaultInjectionConformanceTests InMemory, EF Core

UserTaskFaultInjectionConformanceTests runs the real DefaultUserTaskManager and DefaultUserTaskInvitationService against a real store and breaks the seams between them with the decorators in Faults/. A cross-store operation must either commit fully or leave the caller able to retry, and the retry must converge.

Providers

Availability is resolved once per run by ConformanceProviders, and nothing is ever skipped quietly:

  • Always run: in-memory, EF Core over SQLite, VNext over the SQLite document store.
  • Opt in with a connection string: SQL Server, PostgreSQL, Oracle. Each test reports as skipped, with the reason, when the variable is unset — not as passed.
  • Not coverable: MySQL. Pomelo.EntityFrameworkCore.MySql 9.0.0 caps Microsoft.EntityFrameworkCore.Relational at 9.0.x while this repository targets 10.0.9, so Elsa.UserTasks.Persistence.EFCore.MySql cannot be referenced from a test project at all (NU1107).
ELSA_USERTASKS_TEST_POSTGRES="Host=localhost;Database=elsa_conformance;Username=elsa;Password=elsa" dotnet test test/unit/Elsa.UserTasks.Persistence.ConformanceTests

The remaining variables are ELSA_USERTASKS_TEST_SQLSERVER and ELSA_USERTASKS_TEST_ORACLE.

Point them at a disposable database. The suite migrates the schema and isolates each test with its own tenant, but it never drops the database — that is deliberate, so it can never delete something an operator cared about.

ConformanceCoverageTests always runs. It fails if a provider that must run is unreachable, fails if a variable is set but empty (configured on the CI job, gating nothing), and writes the full matrix to the test output and to user-task-conformance-coverage.md in the output directory.

Adding a provider

  1. Add a ConformanceProvider entry to ConformanceProviders.All.
  2. Add a fixture under Providers/.
  3. Add a collection and one concrete class per contract in ProviderConformanceSuites.cs, each carrying [ConformanceProvider(...)].

A conformance class without [ConformanceProvider] is skipped with a wiring error rather than counted as coverage — the suite refuses to guess which provider a class exercised.