Phase 5: Dashboard.Api DashboardExtensions test coverage#114
Merged
Conversation
…admap Four-phase plan targeting job scheduler handler unit tests, in-memory trace exporter for CI, ObjectPool dead code investigation, and Dashboard.Api coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 1 has 2 plans in 1 wave (parallel): - PLAN-1.1: Delete dead ObjectPool code (3 files, zero references) - PLAN-1.2: Add in-memory ActivityListener to SharedSetup for CI trace coverage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Refactor RunWithTraceVerification to delegate to shared helper - Make activity collection opt-in (listener still always-on) - Revert SharedSetup visibility to internal Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 1 results: ObjectPool dead code removed, in-memory ActivityListener added (always-on listener for coverage cascade, opt-in activity collection), shared trace verification helper, 2 new CLAUDE.md lessons. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 plans in 1 wave: - PLAN-1.1: New CreateJobTablesCommandHandler tests - PLAN-1.2: New GetJobId + GetJobLastKnownEvent query handler tests - PLAN-1.3: Expand existing GetDashboardJobs/ErrorRetries tests (sync+async) Rescoped via research: SetJobLastKnownEvent and SendJobToQueue are transport-specific only; deferred to Phase 3. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces TestHelpers/AdoNetMockFixture (sync, interface-based) and AdoNetAsyncMockFixture (async, DbConnection/DbCommand/DbDataReader base classes) to centralize NSubstitute scaffolding previously duplicated across Phase 2 unit tests. Refactors CreateJobTablesCommandHandlerTests, GetJobIdQueryHandlerTests, and GetJobLastKnownEventQueryHandlerTests to use the shared fixture. No behavioral changes; 216 tests still pass.
Dashboard sync tests now use AdoNetMockFixture with SetupReaderRows(); async tests use AdoNetAsyncMockFixture. Eliminates duplicated NSubstitute scaffolding and multi-row reader ladder previously copy-pasted across the four files. All 216 tests still pass.
Phase 2 results: 39 new unit tests for shared job handlers in Transport.RelationalDatabase. Extracted AdoNetMockFixture + AdoNetAsyncMockFixture helpers (-81 net lines). Added 3 CLAUDE.md lessons (sync/async DbConnection mocking, MSTest 3.x ThrowsExactly, no CancellationToken on dashboard async handlers). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…factors 10 plans across 2 waves: - Wave 1: Refactor SqlServer/PostgreSQL SetJobLastKnownEvent to inject IDbConnectionFactory (production code change to enable unit testing) - Wave 2: New test files for SqlServer/PostgreSQL/SQLite JobSchema, SendJobToQueue, and the refactored SetJobLastKnownEvent handlers Rescoped via research: LiteDb and Redis transport-specific handlers deferred to a future phase due to LiteDb concrete-type and Redis sealed-multiplexer testability issues. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dler to inject IDbConnectionFactory
…ler to inject IDbConnectionFactory Replaces the hardcoded new SqlConnection(_connectionInformation.ConnectionString) with _dbConnectionFactory.Create() so the handler can be unit tested without a live SQL Server. Parameter setup switched to generic IDbDataParameter creation to match the IDbConnection abstraction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…p NpgsqlConnection cast, expand tests The Wave 1 refactor cast IDbConnection to NpgsqlConnection, which made the handler untestable (NpgsqlConnection is sealed). Re-refactored to operate on IDbConnection directly using generic DbType (Int64 for time fields, AnsiString for JobName) and IDbCommand.CreateParameter(). Added 3 missing Handle() lifecycle tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 3 results: 41 new unit tests across 8 new test files for relational transport job handlers (SqlServer/PostgreSQL/SQLite). Refactored 2 production handlers (SqlServer + PostgreSQL SetJobLastKnownEvent) to inject IDbConnectionFactory. Mid-flight re-refactor of PostgreSQL handler to drop sealed NpgsqlConnection cast. Added 2 CLAUDE.md lessons: - Casting IDbConnection to sealed transport types breaks NSubstitute - IDbConnectionFactory injection is the correct test seam LiteDb + Redis deferred to a future phase due to concrete LiteDB types and Redis BaseLua sealed multiplexer issues. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…LiteDb+Redis - Mark Phases 1-3 as COMPLETE with accurate scope and outcomes - Insert NEW Phase 4 for the deferred LiteDb + Redis transport handlers - Renumber old Phase 4 (Dashboard.Api) to Phase 5 - Add cumulative outcomes section Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10 plans across 3 waves: - Wave 1: Production seams (BaseLua TryExecute virtualization, RedisJobQueueCreation depend on IQueueCreation interface for testability) - Wave 2: LiteDb tests using real in-memory LiteDB instances (5 plans) - Wave 3: Redis tests using the Wave 1 seams (3 plans) Key planning decisions: - LiteDb: real in-memory LiteDB (no production refactor needed) - Redis: minimal additive refactors (BaseLua virtual + IQueueCreation seam) - Per-handler plans (max 1 task each) to mitigate builder lockup risk - LiteDb InternalsVisibleTo confirmed already in place - RedisJobQueueCreation refactor uses existing IQueueCreation interface (no new types added, no removal of sealed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test seam for Lua handler subclasses to override the Redis interaction without mocking sealed StackExchange.Redis types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…erface Changes constructor to take IQueueCreation (already implemented by RedisQueueCreation) instead of concrete sealed RedisQueueCreation. Adds Guard.NotNull. Enables wrapper unit tests via NSubstitute. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dler test coverage Added 6 Handle() branch tests (message not found, empty queue, happy path, multi-row isolation, null payloads, format exception). Lifts coverage from 40.9% baseline to ~100% of executable Handle() lines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 4 (LiteDb + Redis transport job handler tests) -- post-build pipeline: - Verification: PASS (full-solution Debug build clean; LiteDb.Tests 166/166; Redis.Tests 190/190; deferred scopes in 2.2/2.4 validated against existing integration test coverage) - Security audit: CLEAN (no secrets, no new deps, seam refactors benign) - Simplification: one minor finding on DashboardUpdateMessageBodyLuaTests disposed-connection test -- resolved via documentary comment acknowledging the test is a named-for-intent duplicate of the null-return path - Documentation: three Lessons Learned added to CLAUDE.md (in-memory LiteDB pattern, BaseLua virtual TryExecute seam, LiteDbConnectionManager no-seam limitation) State: phase 4 complete. Phase 5 (Dashboard.Api DashboardExtensions) is the only remaining phase in the code-coverage milestone. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 5 (Dashboard.Api DashboardExtensions coverage) -- planning:
- CONTEXT-5.md captured (balanced 60-70% target, researcher-decides test
layer, feature branch workflow for PR merge)
- RESEARCH.md authored directly by orchestrator after the dispatched
researcher agent stalled mid-work (documented subagent lockup pattern)
- Key finding: Decision 4 (delete dead overloads) is void -- all 3 public
methods on DashboardExtensions have real callers. The IConfiguration
overload has 0% coverage despite being the production Dashboard.Ui
entrypoint; this is the highest-ROI test target
- Four plans produced across two waves:
- Wave 1 (parallel unit tests): PLAN-1.1 IConfiguration overload + 5
transport switch arms (~50 lines), PLAN-1.2 Swagger + ApiKey security
(~38 lines), PLAN-1.3 CORS + AuthorizationPolicy (~17 lines)
- Wave 2 (integration): PLAN-2.1 Swagger + CORS integration tests via
DashboardTestServer (~12 lines)
- Self-critique fixed a wave-1 file conflict (1.2 and 1.3 both appended to
same test file) and a wrong DashboardOptions property name in 1.1
- Stale Schyntax cleanup PLAN-1.1.md archived under
plans/archived-schyntax-cleanup/ (git mv, history preserved)
- Projected coverage: 33.3% -> ~64% (within balanced band)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tch tests Adds DashboardExtensionsFromConfigurationTests.cs with 5 new test methods: - Happy-path SQLite :memory: registration via IConfiguration overload - Parameterized test over all 5 transport switch arms (SqlServer, PostgreSql, SQLite, LiteDb, Redis) using throwaway connection strings - Unknown-transport negative test (ArgumentException) - Missing-Transport and missing-ConnectionString defensive-guard tests Uses Assert.ThrowsExactly<T> per MSTest 3.x convention. Asserts against DashboardOptions.ConnectionRegistrations (internal, InternalsVisibleTo already grants access from the test project). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ionConvention tests Adds DashboardExtensionsCorsAndAuthTests.cs with 4 test methods: - CORS policy registers when EnableCors=true with non-empty origins - CORS policy does NOT register when CorsOrigins is empty (branch guard) - DashboardAuthorizationConvention.Apply adds AuthorizeFilter to a dashboard controller (positive branch) - DashboardAuthorizationConvention.Apply ignores controllers from other assemblies (negative branch) Note: the DashboardExtensions-level branch "AuthorizationPolicy set -> convention added" is deliberately not asserted at unit-test scope. Debugging showed the MVC options pipeline in a bare ServiceCollection doesn't reliably surface the Dashboard's conventions via IOptions<MvcOptions> — the filters are added but the conventions are dropped somewhere in the pipeline. The convention class itself is exercised directly via Apply() instead; end-to-end coverage of the DashboardExtensions branch belongs in the Dashboard.Api.Integration.Tests project (PLAN-2.1 or a follow-up). PLAN-1.2 note: the PLAN-1.2 builder wrote DashboardExtensionsSwaggerTests.cs before hitting a sandbox block on git commit, and the file was carried along in the preceding PLAN-1.1 commit due to pre-staging. Both files are Wave 1 and both pass tests; per-plan atomicity is not strict here. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…integration tests Adds SwaggerEndpointTests.cs with 3 test classes / 3 test methods exercising the UseDotNetWorkQueueDashboard pipeline branches that unit tests cannot cover: - SwaggerEndpointTests.SwaggerJson_ReturnsOk_WithValidOpenApiShape — spins up a dashboard with EnableSwagger=true and verifies /swagger/v1/swagger.json returns 200 with valid OpenAPI content, exercising the UseSwagger() branch on line 204 of DashboardExtensions.cs. - CorsIntegrationTests.CorsRequest_ReturnsAllowOriginHeader_WhenOriginMatches — hits /api/v1/dashboard/health with an Origin header and asserts the Access-Control-Allow-Origin response header, exercising the UseCors branch on line 199. - AuthorizationPolicyIntegrationTests.DashboardController_Unauthenticated_ Returns_Unauthorized_When_AuthorizationPolicy_Set — registers a minimal Test auth scheme and a DashboardAdmin policy requiring an authenticated user, sets options.AuthorizationPolicy = "DashboardAdmin", hits /api/v1/dashboard/connections (an MVC controller covered by the Dashboard authorization convention), and asserts 401. This recovers the end-to-end coverage gap left by PLAN-1.3's scope pivot — it proves the branch guard in DashboardExtensions.cs:101-105 DOES add the convention and that the convention DOES apply AuthorizeFilter to dashboard controllers in a real ASP.NET Core pipeline. Extends DashboardTestServer with an optional configureServices and configureApp hook to allow authentication/authorization registration without touching existing call sites (the 1-arg overload remains the default path for the other 40+ integration tests). Verification: 226/226 Memory/Sqlite/LiteDb/Swagger/Cors/Authorization/Health tests pass on both net8.0 and net10.0. Zero regressions from the DashboardTestServer change. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…CLAUDE.md lesson Post-review cleanups from the phase 5 simplification + documentation gates: - Split SwaggerEndpointTests.cs into three per-class files (SwaggerEndpointTests, CorsIntegrationTests, AuthorizationPolicyIntegrationTests) to match the one-class-per-file convention used across all 28 existing integration test files. NoAuthHandler moved into AuthorizationPolicyIntegrationTests.cs. Mechanical split, zero logic change; 3/3 tests pass on both net8.0 and net10.0. - Add WHY comment to DashboardTestServer.CreateAsync documenting that configureApp runs after Dashboard middleware and before MapControllers (noted as a structural fragility in REVIEW-2.1). - Append CLAUDE.md lesson: AddControllers(action) in a bare ServiceCollection silently drops MvcOptions.Conventions while filters propagate. Cost 4 debugging iterations in PLAN-1.3; use WebApplication integration tests instead of bare ServiceCollection for any test that must assert an IControllerModelConvention was registered.
Retroactive post-build pipeline after /shipyard:resume. All 4 plans
(PLAN-1.1, 1.2, 1.3, 2.1) had been committed in earlier sessions but
the reviewer gate and later gates were missed when the session was
interrupted.
Gates run in this session:
- Reviewers (4/4 PASS, 0 critical): REVIEW-{1.1,1.2,1.3,2.1}.md
- Verifier (PASS): VERIFICATION.md — build 0 errors, unit tests
216/216, integration tests 226/226 on Memory|Sqlite|LiteDb filter,
no regressions, projected ~70% DashboardExtensions line coverage
- Auditor (CLEAN): AUDIT-5.md — no secrets, no new deps, no IaC, no
OWASP findings
- Simplifier (MINOR): SIMPLIFICATION-5.md — 1 medium applied
(split SwaggerEndpointTests), 2 low pre-existing deferred
- Documenter (MINOR): DOCUMENTATION-5.md — 1 CLAUDE.md lesson added
(MvcOptions.Conventions) + 1 inline comment on DashboardTestServer
Phase 5 COMPLETE. All 5 phases of the code-coverage milestone
complete. Ready for /shipyard:ship.
…tone Appends a dated section to .shipyard/LESSONS.md covering: - What went well: balanced-budget plan shape, 4/4 PASS on first reviewer pass, Wave-2 scope extension absorbing PLAN-1.3's gap - Surprises: MvcOptions.Conventions propagation quirk in bare ServiceCollection, clean resume after 9 session interruptions, retroactive reviewer gate viability - Pitfalls: stop-and-pivot rule for 4th-iteration DI unit tests, one-class-per-file convention, STATE.json staleness after interruptions - Process: researcher should declare wave-1-to-wave-2 scope contracts, artifacts-over-STATE.json reconciliation on resume, retroactive reviewer pattern as supported resume path
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #114 +/- ##
==========================================
+ Coverage 86.41% 88.55% +2.13%
==========================================
Files 995 994 -1
Lines 29313 29298 -15
Branches 2388 2380 -8
==========================================
+ Hits 25331 25944 +613
+ Misses 2991 2475 -516
+ Partials 991 879 -112 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final phase of the code-coverage improvement milestone. Adds targeted unit and integration tests for
DotNetWorkQueue.Dashboard.Api.DashboardExtensions, improving line coverage from 33.3% to a projected ~70% (well above the 50% success criterion).DashboardTestServer.CreateAsyncgained a 3-arg overload with optionalconfigureServicesandconfigureApphooks for auth/authz integration testsCoverage Impact
DashboardExtensions.csActual coverage will be measured by Jenkins post-merge; the projection is based on test execution evidence and the balanced-band target in
.shipyard/phases/5/RESEARCH.md.Notable Decision: PLAN-1.3 Pivot
PLAN-1.3originally targeted unit-testing theAuthorizationPolicybranch guard inDashboardExtensions.csviaIOptions<MvcOptions>.Value.Conventions. After 4 debugging iterations, we discovered thatAddControllers(action)in a bareServiceCollectionsilently drops user-addedMvcOptions.Conventionseven though filters propagate correctly — a quirk of ASP.NET Core's internalConfigureMvcOptionspipeline when no realIHostEnvironmentis present.Pivot: PLAN-1.3 tested
DashboardAuthorizationConvention.Apply()directly at the unit level (full coverage on the convention class itself), and PLAN-2.1's scope was extended to add an end-to-end integration test via a realWebApplicationpipeline with a minimal auth scheme. The end-to-end test provides a stronger guarantee than the unreliable bare-ServiceCollection path would have.This finding is captured as a new lesson in
CLAUDE.mdfor future ASP.NET Core test authors.Gates Run
SwaggerEndpointTests.csinto 3 per-class files matching the one-class-per-file convention used by all 28 existing integration test filesDashboardTestServer.csexplainingconfigureApppipeline orderingTest Plan
dotnet build "Source/DotNetWorkQueue.sln" -c Debugsucceeds (0 errors, 1 pre-existing SYSLIB0012 warning unrelated to phase 5)Files Changed
New test files (all LGPL headers, MSTest 3.x, FluentAssertions 6.12.2):
Source/DotNetWorkQueue.Dashboard.Api.Tests/Extensions/DashboardExtensionsFromConfigurationTests.csSource/DotNetWorkQueue.Dashboard.Api.Tests/Extensions/DashboardExtensionsSwaggerTests.csSource/DotNetWorkQueue.Dashboard.Api.Tests/Extensions/DashboardExtensionsCorsAndAuthTests.csSource/DotNetWorkQueue.Dashboard.Api.Integration.Tests/Tests/SwaggerEndpointTests.csSource/DotNetWorkQueue.Dashboard.Api.Integration.Tests/Tests/CorsIntegrationTests.csSource/DotNetWorkQueue.Dashboard.Api.Integration.Tests/Tests/AuthorizationPolicyIntegrationTests.csModified:
Source/DotNetWorkQueue.Dashboard.Api.Integration.Tests/Helpers/DashboardTestServer.cs— added 3-argCreateAsyncoverload + pipeline-ordering WHY commentCLAUDE.md— appended MvcOptions.Conventions lesson.shipyard/ROADMAP.md,.shipyard/STATE.json,.shipyard/HISTORY.md,.shipyard/LESSONS.md— milestone bookkeeping.shipyard/phases/5/*— RESEARCH, plans, SUMMARY/REVIEW/AUDIT/SIMPLIFICATION/DOCUMENTATION/VERIFICATION artifactsMilestone Status
Code-coverage milestone: COMPLETE (phases 1–5 all shipped)
Cumulative across the milestone:
IDbConnectionFactoryinjection for SqlServer + PostgreSQLSetJobLastKnownEventCommandHandler)BaseLuaseam refactor (virtualizedTryExecutefor Redis Lua handler unit tests)🤖 Generated with Claude Code