16. Testing
16.1 Where the real Background Jobs test evidence lives
⚠ Documentation note:
docs/ARCHITECTURE/TEST_STRATEGY.mdanddocs/ARCHITECTURE/REGRESSION_MATRIX.mdin 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 anyShumoul.BackgroundJobs.*type or the ERP'sBackgroundJobsTestsfolder. 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
| Suite | Location | What it tests |
|---|---|---|
Shumoul.BackgroundJobs.Tests | Framework 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 class | Phase | Tests | What it verifies |
|---|---|---|---|
BackgroundJobsHostDiTests | 3.1 | 12 | Every 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 |
NotificationDeadLetterCleanupPipelineJobTests | 3.2 | 12 | Adapter contract, bridge→pipeline invocation, execution-context correctness, legacy-path independence |
Phase33CutoverVerificationTests | 3.3 | 12 | Coverage audit (no method lost when legacy registrations removed), no-duplicate-execution, full chain, rollback callability |
Phase34CandidateExpansionTests | 3.4 | 12 | Candidate/job-ID match, isolation (ExpireStaleRetriesAsync never triggers ProcessDueRetriesAsync), no-duplicate, full chain, rollback |
Phase36RetryProcessorMigrationTests | 3.6 | 17 | Registration, adapter isolation, exactly-once execution, full chain, cancellation, exception propagation, rollback |
Phase37CampaignCleanupMigrationTests | 3.7 | 17 | Same category shape as 3.6, applied to campaign cleanup |
Phase39CampaignSchedulerMigrationTests | 3.9 | 17 | Adds an explicit transaction-ownership category (TransactionOwnership_Adapter_DoesNotOpenAnyTransaction) |
Phase311CampaignWorkflowExecutorMigrationTests | 3.11 | 21 | Adds 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:
- Job-ID distinctness — the new pipeline job ID doesn't collide with any other known ID.
- 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.
- Full-chain execution — Hangfire bridge → pipeline → adapter → worker actually completes.
- Cancellation propagation — both a live token and a pre-cancelled token behave correctly.
- Exception propagation — the pipeline must never suppress an exception; it must reach Hangfire so Hangfire's own retry behavior applies.
- Transaction non-ownership — the adapter, bridge, and pipeline never call
BeginTransactionAsync. - 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.