Skip to main content
Version: Next

16. Testing

16.1 Where the real Background Jobs test evidence lives

Documentation note: docs/ARCHITECTURE/TEST_STRATEGY.md and docs/ARCHITECTURE/REGRESSION_MATRIX.md in the source repository, despite living in the same folder as every other Background Jobs document, describe the Notification Framework's test architecture — four Notification test projects, dispatch/ delivery/retry/DI regression rows — not this framework's. Neither file names any Shumoul.BackgroundJobs.* type or the ERP's BackgroundJobsTests folder. This guide draws Background Jobs test evidence from the migration phase documents instead, where it actually lives, and does not attribute Notification Framework test counts to this framework.

16.2 Two independent test suites

SuiteLocationWhat it tests
Shumoul.BackgroundJobs.TestsFramework repo (Shumoul.Saas.BackgroundJobsFramework)The 5 packages themselves — DependencyGraphTests, HostDiVerificationTests, ConstructorCoverageTests/AdapterConstructorCoverageTests, FailureClassifierTests — with no ERP dependency at all
BackgroundJobsTests (within Shumoul.Application.Tests)ERP consumer repo (Shumoul.Saas.Api)Host DI wiring + one migration-verification test class per migrated job

Framework-repo suite: 200/200 passing, as of Phase 4.1. ERP consumer suite (BackgroundJobsTests subset): 121/121 passing, as of Phase 4.1.

16.3 ERP consumer test classes, one per migration phase

Test classPhaseTestsWhat it verifies
BackgroundJobsHostDiTests3.112Every public Core service and Adapter interface resolves from the host DI container; transient services are safe to resolve repeatedly; the framework never registers ERP types
NotificationDeadLetterCleanupPipelineJobTests3.212Adapter contract, bridge→pipeline invocation, execution-context correctness, legacy-path independence
Phase33CutoverVerificationTests3.312Coverage audit (no method lost when legacy registrations removed), no-duplicate-execution, full chain, rollback callability
Phase34CandidateExpansionTests3.412Candidate/job-ID match, isolation (ExpireStaleRetriesAsync never triggers ProcessDueRetriesAsync), no-duplicate, full chain, rollback
Phase36RetryProcessorMigrationTests3.617Registration, adapter isolation, exactly-once execution, full chain, cancellation, exception propagation, rollback
Phase37CampaignCleanupMigrationTests3.717Same category shape as 3.6, applied to campaign cleanup
Phase39CampaignSchedulerMigrationTests3.917Adds an explicit transaction-ownership category (TransactionOwnership_Adapter_DoesNotOpenAnyTransaction)
Phase311CampaignWorkflowExecutorMigrationTests3.1121Adds a concurrency/claim-gate category (TwoConcurrentBridgeRuns_EachInvokeWorkerOnce_NoAdditionalDedupAtThisLayer) proving the pipeline layer performs no deduplication of its own

Running totals as stated per-phase in the source documents: 12 → 36 (after 3.3) → 48 (after 3.4) → 82 (after 3.7, stated explicitly as "Total BackgroundJobs suite: 82/82") → 100 (after 3.9, stated explicitly).

Arithmetic note, reported rather than silently corrected: 82 + 17 (Phase 3.9's new tests) = 99, not the 100 explicitly stated in the Phase 3.9 source document. Neither document explains the extra test. This guide reports each phase's own stated figure rather than asserting a computed running total. 100 + 21 (Phase 3.11) = 121, which does cross-check cleanly against the separately-cited 121/121 baseline used from Phase 4.1 onward.

16.4 Full ERP application test suite context

The full Shumoul.Application.Tests suite (not filtered to Background Jobs) stood at 213 passing / 35 pre-existing failures after Phase 3.1, growing to 322 passing / 35 pre-existing failures / 357 total after Phase 3.11. The 35 pre-existing failures (OAuthServiceTests, in-memory EF Core transaction incompatibility) are explicitly called out as unrelated to Background Jobs work in every phase document and never change in count across the initiative.

16.5 What every migration test class checks, as a category template

Every Phase{N}...MigrationTests.cs class covers the same fixed set of categories, making this the template for any future job migration's test class:

  1. Job-ID distinctness — the new pipeline job ID doesn't collide with any other known ID.
  2. Adapter method isolation — the adapter calls the correct worker method exactly once; if the worker interface is shared with a sibling job, an explicit test proves the other method is never called.
  3. Full-chain execution — Hangfire bridge → pipeline → adapter → worker actually completes.
  4. Cancellation propagation — both a live token and a pre-cancelled token behave correctly.
  5. Exception propagation — the pipeline must never suppress an exception; it must reach Hangfire so Hangfire's own retry behavior applies.
  6. Transaction non-ownership — the adapter, bridge, and pipeline never call BeginTransactionAsync.
  7. Rollback callability — the legacy worker method is still directly invokable, proving the rollback path works without any code restoration.

16.6 DI verification

HostDiVerificationTests and ConstructorCoverageTests/AdapterConstructorCoverageTests (framework-repo side) reflection-sweep every public service and adapter to confirm constructor dependencies resolve cleanly. BackgroundJobsHostDiTests (ERP consumer side, Phase 3.1) is the equivalent 12-test DI verification suite confirming the framework's services resolve inside the actual host container, and confirming the framework never registers ERP-owned types itself.

16.7 Pipeline / execution verification

DependencyGraphTests.DependencyGraph_HasNoCircularDependencies and DependencyGraph_FrameworkDoesNotRegisterAdapters are treated as stop-the-line checks — a failure here means a genuine architecture violation was just introduced, never a test to skip or suppress.

16.8 Release gate

Per the framework's Release Checklist, a release is not considered complete unless: the framework-repo suite passes 200/200, the consumer-repo targeted BackgroundJobsTests subset passes 121/121, and the full consumer suite shows no new failures versus its documented baseline (322 passing / 357 total). "A release is complete only when every checkbox is checked — if time pressure requires cutting scope, cut the change, not the checklist." There is no CI/CD pipeline enforcing this today (Shumoul.Saas.BackgroundJobsFramework has no .github/workflows/ directory) — every release to date has been a manual developer workflow, a condition explicitly noted (not hidden) in the framework's own Product Readiness Report.