Harden Spring-parity modules: real DI, real scaffolds, integration tests#24
Merged
Harden Spring-parity modules: real DI, real scaffolds, integration tests#24
Conversation
added 2 commits
May 7, 2026 23:40
Round 2 on the 16 new modules. Every issue found while writing the
follow-up integration tests is fixed in the source, not the tests:
- Cli scaffolds were producing uncompilable C#. HandlerScaffold now
emits real ICommandHandler<T,TResult> / IQueryHandler<T,TResult> with
matching record types, and ServiceScaffold writes a valid Visual
Studio Format 12.00 .sln + per-module csproj instead of a placeholder
comment. CliScaffoldTests parses every output through Roslyn to keep
it that way.
- FireflySecurity dropped the BuildServiceProvider() anti-pattern
inside AddJwtBearer. JwtBearerOptions is now configured through
IConfigureNamedOptions, so the bearer scheme resolves through the
real container without spinning up a duplicate root.
- FireflySession middleware was writing Set-Cookie *after* the inner
pipeline flushed headers, which threw on any request that wrote a
body. Cookie issuance moved before _next; persistence runs in
Response.OnCompleted. New SessionMiddlewareTests exercises the full
cookie round-trip via TestServer.
- Actuator BeansEndpoint now resolves via IReadOnlyList<BeanRegistration>
so the snapshot is callable from DI; MappingsEndpoint takes
EndpointDataSource as optional so the actuator loads cleanly outside
ASP.NET Core hosts (test rigs, console hosts, hosted services). New
ActuatorRouteTests covers the real /actuator/{id} routing.
- Aop AspectRegistry now scans BindingFlags.Public only — advice on
private methods would never be reachable from the framework anyway,
and locking it to public makes the contract explicit.
- New StarterApplicationWiringTests asserts that every module advertised
by AddFireflyApplication actually resolves out of the container, so
future starter-bundle drift breaks the build instead of consumers.
- Agentic README rewritten to match what actually ships: the core
module deliberately has no provider SDK reference; consumers
implement IChatModel against their preferred adapter. Removed the
list of adapter packages that don't yet exist.
- Messaging.SendToAttribute removed (it was advertised but never wired);
MessageListenerAttribute kept with a docstring noting it's a
placeholder marker for future framework-side scanners.
- XML docs swept across FireflyResilienceOptions (every strategy bag),
FireflySecurityOptions, AgentInvocation/AgentInvocationResult, and
Message<T>. Every public type has at least a one-line summary.
Tests: 9 new integration tests bringing the suite to 399 total, all
green:
- SecurityMiddlewareTests (2): TestServer-driven, asserts holder bind
+ tenant header propagation
- SessionMiddlewareTests (1): cookie issued, value persisted across
requests
- ActuatorRouteTests (1): /actuator index + /actuator/{id} 200/404
routing
- CliScaffoldTests (4): every scaffold parses with Roslyn + sln file
is real
- StarterApplicationWiringTests (1): all 14 modules in
AddFireflyApplication resolve
The original test used a 1ms eviction window and depended on real wall clock progress between Register and EvictStale. Locally that worked (host clock granularity is ~16ms); GitHub Actions runners can resolve DateTimeOffset.UtcNow with sub-ms precision and the eviction window slipped before the comparison ran, dropping the instance. Replaced with two deterministic cases: - EvictStale_keeps_fresh_instances: two fresh registrations + 5min window — both stay. - EvictStale_drops_instances_past_timeout: register, await 50ms, evict with 1ms window — instance is gone. Adjusted Register_then_heartbeat assertion to BeOnOrAfter (heartbeat in the same millisecond as registration is a legitimate outcome on fast clocks).
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
What changed
Real DI / no anti-patterns
FireflySecurityno longer callsservices.BuildServiceProvider()insideAddJwtBearer.JwtBearerOptionsis now configured through anIConfigureNamedOptions<JwtBearerOptions>so the bearer scheme resolves through the real container.FireflyActuator.BeansEndpointresolves viaIReadOnlyList<BeanRegistration>instead of the implicitList<>(which the DI graph couldn't satisfy).FireflyActuator.MappingsEndpointtakesEndpointDataSourceas optional — the actuator loads cleanly in console hosts / hosted services / test rigs that don't have ASP.NET Core routing wired.FireflySessionmiddleware was writingSet-Cookieafter the inner pipeline flushed headers; cookie issuance moved before_next, persistence runs inResponse.OnCompleted.Real scaffolds
Cli.HandlerScaffoldnow emits realICommandHandler<TCommand, TResult>/IQueryHandler<TQuery, TResult>with matching record types. The previous output didn't compile.Cli.ServiceScaffoldnow writes a valid Visual Studio Format 12.00.slnplus per-modulecsprojfiles. The previous output was a placeholder comment.Cli.SagaScaffoldincludes namespace + cleaner method bodies. Roslyn parses every scaffold inCliScaffoldTeststo keep it that way.Honest docs
AgenticREADME rewritten to match what ships: the core module has no provider SDK reference; the user implementsIChatModelfor their preferred adapter. The list of adapter packages that don't yet exist is gone.Messaging.SendToAttributeremoved (advertised but never wired).MessageListenerAttributekept with a docstring noting it's a marker for future framework-side scanners.FireflyResilienceOptions(every strategy bag),FireflySecurityOptions,AgentInvocation/AgentInvocationResult, andMessage<T>. Every public type has at least a one-line summary.Tighter contracts
Aop.AspectRegistrynow scansBindingFlags.Publiconly — advice on private methods would never be reachable from the framework, and locking it down makes the contract explicit.New tests (399 total, all green)
SecurityMiddlewareTests(2)X-Tenant-Idheader propagationSessionMiddlewareTests(1)ActuatorRouteTests(1)/actuatorindex +/actuator/{id}200/404 routingCliScaffoldTests(4).slnis a real Format-12 solutionStarterApplicationWiringTests(1)AddFireflyApplicationactually resolve out of DITest plan
dotnet build FireflyFramework.sln— cleandotnet test FireflyFramework.sln— 399 / 399 pass, 0 skipped