Skip to content

v4.1.0: NamedDestinationProvider, command queues, Aspire E2E testing and AI skills - #188

Open
chullybun wants to merge 15 commits into
mainfrom
aspire-testing
Open

chullybun wants to merge 15 commits into
mainfrom
aspire-testing

Conversation

@chullybun

Copy link
Copy Markdown
Collaborator

Summary

Events go to the shared topic; commands go to a per-domain, session-enabled queue ({Destination}-{domain}), so only the addressed domain receives them.

Framework

  • NamedDestinationProvider (AddNamedDestinationProvider()): commands are routed to {Destination}-{domain}; a null or empty command domain self-addresses to IHostSettings.DomainName. Unit tests included.
  • Keyed Service Bus receiving: keyed WithReceiver / WithSessionReceiver and WithKeyedSubscribedSubscriber, allowing multiple receivers (events topic and commands queue) in one Subscribe host.
  • Aspire E2E testing (CoreEx.UnitTesting): DistributedApplication helpers for SQL Server/PostgreSQL migration, Redis clear and Service Bus emulator reset; AddEndpoints / AddHostedServiceSupport / DisableHttpCertificateValidation AppHost extensions; WireMock-based MockHost for third-party HTTP.

Samples

  • Contoso migrated to command queues (contoso-products queue; Products.Subscribe has an events receiver and a commands receiver).
  • Contoso.Test.Aspire E2E, including SendGrid stubbed via the MockHost.

CoreEx.Template

  • Hosts use NamedDestinationProvider and a keyed subscriber by default.
  • Service Bus topology is code-based in the generated Test.Common ServiceBus class; Relay, Subscribe and Aspire tests use ResetAzureServiceBusAsync.
  • coreex-aspire template generates the AppHost, MockHost and Test.Aspire (OnBeforeStartAsync / OnAfterStartAsync).

AI assets and docs

  • New skills: coreex-command-publish-e2e, coreex-command-subscribe-e2e, coreex-aspire, with prompts, Claude commands and coreex-aspire.instructions.md.
  • README, catalog, agent routing and related skills/docs updated; stale docs fixed.

CI / versioning

  • New CoreEx.Samples.Test.E2E.slnf and a net10.0-only Aspire E2E step after the sample tests; Contoso.Aspire.MockHost and Contoso.Test.Aspire added to the samples build filter.
  • Version bumped to 4.1.0, with CHANGELOG entries.

Testing

  • tools/validate-template-pack.ps1 passes (19/19), including packing and building the template scaffolds.
  • The E2E filter builds locally. The Aspire E2E has not yet run in CI; expect the first run to show any Linux runner issues (dashboard/OTLP port conflicts, Aspire environment variables).
  • The AI skill snippets are modelled on the Contoso samples and have not been compiled in a scaffolded solution.

chullybun and others added 9 commits September 24, 2026 15:51
Added Reset-ServiceBusEmulator.ps1 to automate recovery of the Service Bus emulator and SQL Server containers. Registered the script in CoreEx.slnx. Updated AppHost.cs to model infra as Aspire connection-string resources with icons and explicit project references for dashboard topology. Synced appsettings.Development.json connection strings to match Aspire resource names. Upgraded UnitTestEx packages to 5.12.0. Improved EventExpectations and UnitTestExExpectations.ChangeLog with updated XML docs, constructor signatures, and runtime type checks for TesterBaseCore.
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…in database retry pipeline

- Add Contoso.Test.Aspire E2E test project exercising the full Aspire AppHost topology.
- Add WithAspireTester unit-testing helper alongside FusionCache Redis-connection-string cache
  clearing and Service Bus admin-client-based queue/subscription reset/purge helpers, replacing
  the Service Bus emulator Config.json-driven approach.
- Add per-database IsTransientException/HandleDbException classification (SqlServer + Postgres),
  reviewed against Azure SQL/Postgres transient error code lists.
- Add opt-in DatabaseArgsBase.RetryOnTransient + RetryResiliencePipeline, wired through
  DatabaseInvoker via a generalized RetryResiliency<TOwner>.Create<TResult> (Result<T>-aware)
  pipeline. Exhausted-retry failures convert directly to a Result (no throw) when the caller's
  result type is ROP-shaped; otherwise a single unwrapped exception (no AggregateException
  wrapping) is rethrown, preserving the existing TransformException conversion layer.
- Add unit test coverage for the retry pipeline (DatabaseInvokerRetryTests, RetryResiliencyTests).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…ic type argument

The semaphore-protected 'slow path' in ReferenceDataHybridCache.GetOrCreateAsync relied on
C# generic type inference to pick the T used for the underlying IHybridCache.GetOrCreateByKeyAsync<T>
call. Since the inner lambda's return expression resolved to the factory delegate's declared
IReferenceDataCollection return type, T was always inferred as the interface - never the concrete
collection type (e.g. UnitOfMeasureCollection) - unlike the 'fast path' read, which already used a
reflection-built typed invoker specifically to avoid this.

With a serializing distributed cache (FusionCache + Redis), this caused intermittent
System.Text.Json.NotSupportedException: 'The collection type IReferenceDataCollection is abstract,
an interface, or is read only, and could not be instantiated and populated' whenever the slow path's
internal L2 check deserialized a previously-written entry using the interface as the generic argument -
observed as FUSION [DC] deserialization errors in the Aspire E2E test logs.

Fix: add a GetOrCreateByKeyInvoker (mirroring the existing TryGetByKeyInvoker) so the slow path also
invokes the underlying cache with the concrete collection type, never the interface.

Added a regression test (GetOrCreateAsync_CacheMiss_NeverUsesInterfaceAsUnderlyingCacheGenericType)
using a type-recording IHybridCache stand-in; verified it fails against the pre-fix code and passes
against the fix. Full CoreEx.RefData.Test.Unit suite (137 tests) passes on net8.0/net9.0/net10.0.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…trust

Adds Contoso.Aspire.MockHost (WireMock.Net) as a test-only Aspire resource, wired into the
AppHost's DI container, and disables ASP.NET Core dev-cert validation for the AppHost's own
HttpClient defaults - dotnet dev-certs https --trust is not fully supported on Linux, so the
'api' resource's https endpoint dev cert is not OS-trusted on Linux CI runners; this covers both
the health check probe and AspireTesterBase's CreateHttpClient(), which resolve HttpClient via the
same DI container.

Also reorders two E2ETest.cs checkpoints (discount before basket-item-add) to match the actual
sync timing, and picks up the Spectre.Console/WireMock.Net package version housekeeping in
Directory.Packages.props and CoreEx.slnx needed by the new project.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
- Move the templated Aspire project out of src/ into a sibling aspire/
  directory (src, tests, aspire at the same level) since it is not a
  to-be-deployed project.
- Wire AppHost.cs resources first, then reference them from each project,
  mirroring samples/aspire/Contoso.Aspire/AppHost.cs.
- Add UnitTestExExtensions.Aspire.cs (CoreEx.UnitTesting) providing the
  WireMockConsole/AddMockHostProject/AddEndpoints Aspire helpers; delete
  the templated Extensions.cs, which these replace.
- Emit two new sibling projects into aspire/: app-name.Aspire.MockHost
  (WireMock.Net stub host) and solution-name.Test.Aspire (NUnit,
  WithAspireTester-based HostTests.cs).
- Apply the same MockHost simplification to samples/aspire/Contoso.Aspire.MockHost
  and wire it into Contoso.Aspire/AppHost.cs.
- Add Development.json connection strings matching Contoso.Aspire.
- Bump the templated AppHost's Aspire.AppHost.Sdk to 13.5.4 to match
  UnitTestEx.Aspire's Aspire.Hosting.AppHost requirement.
- Harden tools/validate-template-pack.ps1's local NuGet feed handling:
  copy missing third-party packages into the trusted local feed instead
  of adding a second, potentially stale, private source.
- Update samples/docs/aspire.md and testing.md to document the MockHost
  and Contoso.Test.Aspire; fix a stale AppHost.cs path in
  .github/copilot-instructions.md; update coreex-scaffold skill docs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…or SendGrid mocking

- New INotificationAdapter (Application) / NotificationAdapter (Infrastructure) backed by a
  SendGrid v3 Mail Send typed HTTP client (no SDK dependency).
- New BasketCheckedOutSubscriber (Contoso.Shopping.Subscribe) subscribing to
  contoso.shopping.basket.checkedout.v1, delegating to the notification adapter.
- Register SendGrid typed client + options in Subscribe/Api Program.cs and appsettings.json.
- Wire the Aspire AppHost to route the Subscribe host's SendGrid BaseAddress through the
  WireMock-based MockHost, stubbing v3/mail/send with 202 Accepted for local/dev E2E runs.
- Refactor CoreEx.UnitTesting Aspire helpers (ClearRedisCacheAsync, ResetAzureServiceBusAsync,
  etc.) to operate on DistributedApplication directly, and extend WithAspireTester with
  OnBeforeStartAsync/OnAfterStartAsync hooks supporting the MockHost wiring.
- New SendGridHttpClientTests (Test.Unit) and SubscriberTests.BasketCheckedOut (Test.Subscribe)
  proving the SendGrid API is invoked via mocked HTTP.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…ueues

- Add NamedDestinationProvider: events to a shared topic, commands to per-domain queues ({Destination}-{domain}); null command domain self-addresses to the host domain.
- Add AddNamedDestinationProvider() DI extension.
- Tighten EventData/EventFormatter DomainName semantics (owner of the message contract; commands default to host domain, throw if none).
- Add optional serviceKey to WithReceiver/WithSessionReceiver so multiple receivers can coexist.
- Samples: add session-enabled contoso-products queue, register the provider in publishing hosts, Products.Subscribe consumes both topic subscription and command queue.
- Update tests (unit, relay queue test, command destination assertions) and docs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…ervice Bus topology

- Api/Subscribe host templates register AddNamedDestinationProvider().
- Subscribe host uses keyed receiver/subscriber/hosted service (hosted-subscriber-events).
- New Test.Common ServiceBus class; Relay/Subscribe/Aspire tests use ResetAzureServiceBusAsync.
- Aspire test template uses OnBeforeStartAsync/OnAfterStartAsync.
- Sample hosted service keys renamed to hosted-subscriber-*; docs and validator updated.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
- New skills coreex-command-publish-e2e, coreex-command-subscribe-e2e and coreex-aspire
  (with prompts, Claude commands, aspire instructions) registered in the template pack.
- Update catalogs, README, agent routing, subscriber/adapter/test skills and stale docs.
- CI: add Aspire E2E solution filter and net10.0-only test step after sample tests.
- Bump version to 4.1.0 and add CHANGELOG entries.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 29, 2026 22:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Generated runtime topology and standalone service discovery are incomplete, while two unbounded asynchronous timing assumptions make the new E2E test unreliable.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
What changed in this PR

Introduces domain-targeted command queues, keyed Service Bus receivers, Aspire end-to-end testing, transient database retries, and supporting templates/documentation for v4.1.0.

Changes:

  • Adds NamedDestinationProvider, multi-destination publishing, keyed receivers, and command queue samples.
  • Adds Aspire orchestration, test helpers, MockHost integration, and E2E coverage.
  • Adds database retry support, SendGrid notifications, templates, skills, documentation, and release updates.
File Description
.claude/​commands/​coreex-aspire.md Adds Aspire command.
.claude/​commands/​coreex-command-publish-e2e.md Adds command-publishing workflow.
.claude/​commands/​coreex-command-subscribe-e2e.md Adds command-subscriber workflow.
.claude/​commands/​coreex-subscriber-e2e.md Narrows subscriber workflow scope.
.github/​agents/​coreex-expert.agent.md Updates agent routing guidance.
.github/​copilot-instructions.md Updates repository guidance.
.github/​coreex-ai-workflows.md Documents new workflows.
.github/​instructions/​coreex-aspire.instructions.md Adds Aspire conventions.
.github/​instructions/​coreex-event-subscribers.instructions.md Updates subscriber conventions.
.github/​instructions/​coreex-host-setup.instructions.md Updates host setup guidance.
.github/​instructions/​coreex-tests.instructions.md Updates testing guidance.
.github/​prompts/​coreex-aspire.prompt.md Adds Aspire prompt.
.github/​prompts/​coreex-command-publish-e2e.prompt.md Adds command-publishing prompt.
.github/​prompts/​coreex-command-subscribe-e2e.prompt.md Adds command-subscriber prompt.
.github/​prompts/​coreex-subscriber-e2e.prompt.md Updates subscriber prompt.
.github/​skills/​coreex-adapter/​SKILL.md Extends adapter guidance.
.github/​skills/​coreex-aspire/​SKILL.md Adds Aspire skill.
.github/​skills/​coreex-aspire/​references/​workflow.md Documents Aspire workflow.
.github/​skills/​coreex-command-publish-e2e/​SKILL.md Adds command-publishing skill.
.github/​skills/​coreex-command-publish-e2e/​references/​workflow.md Documents publishing workflow.
.github/​skills/​coreex-command-subscribe-e2e/​SKILL.md Adds command-subscriber skill.
.github/​skills/​coreex-command-subscribe-e2e/​references/​workflow.md Documents subscriber workflow.
.github/​skills/​coreex-scaffold/​SKILL.md Updates scaffolding guidance.
.github/​skills/​coreex-scaffold/​references/​workflow.md Updates scaffold workflow.
.github/​skills/​coreex-subscriber-e2e/​SKILL.md Refines event subscriber skill.
.github/​skills/​coreex-subscriber-e2e/​references/​workflow.md Refines subscriber workflow.
.github/​skills/​coreex-subscriber/​SKILL.md Updates subscriber guidance.
.github/​skills/​coreex-test-relay/​SKILL.md Updates relay testing.
.github/​skills/​coreex-test-subscribe/​SKILL.md Updates subscriber testing.
.github/​skills/​coreex-test-subscribe/​references/​workflow.md Updates test workflow.
.github/​workflows/​CI.yml Adds Aspire E2E CI.
AGENTS.md Updates repository context.
CHANGELOG.md Records v4.1.0 changes.
CoreEx.Samples.Build.slnf Adds Aspire sample projects.
CoreEx.Samples.Test.E2E.slnf Adds E2E solution filter.
CoreEx.slnx Adds new projects.
Directory.Packages.props Updates package versions.
README.md Documents new capabilities.
Version.props Bumps version to 4.1.0.
samples/​aspire/​Contoso.Aspire.MockHost/​Contoso.Aspire.MockHost.csproj Adds mock-host project.
samples/​aspire/​Contoso.Aspire.MockHost/​Program.cs Starts WireMock host.
samples/​aspire/​Contoso.Aspire/​AppHost.cs Orchestrates sample services.
samples/​aspire/​Contoso.Aspire/​Contoso.Aspire.csproj Updates AppHost dependencies.
samples/​aspire/​Contoso.Aspire/​appsettings.Development.json Configures Aspire resources.
samples/​aspire/​Contoso.Test.Aspire/​Contoso.Test.Aspire.csproj Adds Aspire test project.
samples/​aspire/​Contoso.Test.Aspire/​E2ETest.cs Adds cross-domain E2E test.
samples/​aspire/​Contoso.Test.Aspire/​GlobalUsing.cs Adds test imports.
samples/​aspire/​Contoso.Test.Aspire/​ServiceBus.cs Defines E2E broker topology.
samples/​docs/​aspire.md Documents Aspire usage.
samples/​docs/​hosts-layer.md Updates host documentation.
samples/​docs/​local-dev.md Updates local setup.
samples/​docs/​testing.md Updates testing documentation.
samples/​src/​Contoso.Customers.Api/​Program.cs Registers named destinations.
samples/​src/​Contoso.Orders.Api/​Program.cs Registers named destinations.
samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs Adds product activation.
samples/​src/​Contoso.Products.Api/​Program.cs Registers named destinations.
samples/​src/​Contoso.Products.Subscribe/​Program.cs Adds keyed command receiver.
samples/​src/​Contoso.Shopping.Api/​Program.cs Adds clients and routing.
samples/​src/​Contoso.Shopping.Api/​appsettings.Development.json Configures Products discovery.
samples/​src/​Contoso.Shopping.Application/​Adapters/​Notifications/​INotificationAdapter.cs Defines notification adapter.
samples/​src/​Contoso.Shopping.Infrastructure/​Adapters/​Notifications/​NotificationAdapter.cs Implements notifications.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridContent.cs Adds SendGrid content model.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridEmailAddress.cs Adds email address model.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridHttpClient.cs Adds SendGrid client.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridMailRequest.cs Adds mail request model.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridOptions.cs Adds SendGrid options.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridPersonalization.cs Adds personalization model.
samples/​src/​Contoso.Shopping.Infrastructure/​GlobalUsing.cs Adds notification imports.
samples/​src/​Contoso.Shopping.Subscribe/​GlobalUsing.cs Adds subscriber imports.
samples/​src/​Contoso.Shopping.Subscribe/​Program.cs Wires notification handling.
samples/​src/​Contoso.Shopping.Subscribe/​Subscribers/​BasketCheckedOutSubscriber.cs Sends checkout notifications.
samples/​src/​Contoso.Shopping.Subscribe/​appsettings.Development.json Configures discovered Products API.
samples/​src/​Contoso.Shopping.Subscribe/​appsettings.json Adds SendGrid settings.
samples/​tests/​Contoso.Products.Test.Subscribe/​SubscriberTests.ReservationCancel.cs Tests cancellation commands.
samples/​tests/​Contoso.Products.Test.Subscribe/​SubscriberTests.ReservationConfirm.cs Tests confirmation commands.
samples/​tests/​Contoso.Products.Test.Subscribe/​SubscriberTests.cs Updates subscriber setup.
samples/​tests/​Contoso.Shopping.Test.Api/​MutateTests.Basket.cs Tests basket command publishing.
samples/​tests/​Contoso.Shopping.Test.Api/​MutateTests.cs Resets broker topology.
samples/​tests/​Contoso.Shopping.Test.Common/​ServiceBus.cs Defines shared topology.
samples/​tests/​Contoso.Shopping.Test.Relay/​RelayTests.cs Tests command queue relay.
samples/​tests/​Contoso.Shopping.Test.Subscribe/​GlobalUsing.cs Adds subscriber test imports.
samples/​tests/​Contoso.Shopping.Test.Subscribe/​SubscriberTests.BasketCheckedOut.cs Tests checkout notification.
samples/​tests/​Contoso.Shopping.Test.Subscribe/​SubscriberTests.cs Configures subscriber tests.
samples/​tests/​Contoso.Shopping.Test.Unit/​Clients/​SendGrid/​SendGridHttpClientTests.cs Tests SendGrid client.
samples/​tests/​Contoso.Shopping.Test.Unit/​EntryPoint.cs Registers test clients.
samples/​tests/​Contoso.Shopping.Test.Unit/​GlobalUsing.cs Adds test imports.
servicebus/​Config.json Adds command queue topology.
servicebus/​Reset-ServiceBusEmulator.ps1 Updates emulator reset.
src/​CoreEx.Azure.Messaging.ServiceBus/​AGENTS.md Documents keyed receiving.
src/​CoreEx.Azure.Messaging.ServiceBus/​CoreExServiceBusExtensions.DependencyInjection.cs Adds keyed receiver registration.
src/​CoreEx.Database.Postgres/​Outbox/​PostgresOutboxRelay.cs Reuses transient classification.
src/​CoreEx.Database.Postgres/​PostgresDatabase.cs Classifies transient errors.
src/​CoreEx.Database.SqlServer/​Outbox/​SqlServerOutboxRelay.cs Reuses transient classification.
src/​CoreEx.Database.SqlServer/​SqlServerDatabase.cs Classifies transient errors.
src/​CoreEx.Database/​Abstractions/​DatabaseArgsBase.cs Adds retry configuration.
src/​CoreEx.Database/​Abstractions/​DatabaseInvoker.cs Executes retry pipelines.
src/​CoreEx.Database/​Abstractions/​DatabaseInvokerResiliency.cs Defines default retry policy.
src/​CoreEx.Database/​Database.cs Exposes transient classification.
src/​CoreEx.Database/​GlobalUsing.cs Adds resilience imports.
src/​CoreEx.Database/​IDatabase.cs Extends database contract.
src/​CoreEx.Database/​Outbox/​DatabaseOutboxRelayBase.cs Delegates transient checks.
src/​CoreEx.Events/​AGENTS.md Documents destination routing.
src/​CoreEx.Events/​CoreExEventsExtensions.DependencyInjection.cs Registers named provider.
src/​CoreEx.Events/​EventData.cs Updates message metadata docs.
src/​CoreEx.Events/​EventFormatter.cs Preserves supplied metadata.
src/​CoreEx.Events/​Publishing/​FixedDestinationProvider.cs Updates provider implementation.
src/​CoreEx.Events/​Publishing/​IDestinationProvider.cs Extends destination API.
src/​CoreEx.Events/​Publishing/​NamedDestinationProvider.cs Implements command routing.
src/​CoreEx.Events/​Publishing/​README.md Documents routing behavior.
src/​CoreEx.RefData/​ReferenceDataHybridCache.TypedInvoker.cs Adds typed cache invocation.
src/​CoreEx.RefData/​ReferenceDataHybridCache.cs Uses concrete collection types.
src/​CoreEx.Template/​CoreEx.Template.csproj Packs new template assets.
src/​CoreEx.Template/​README.md Documents Aspire template.
src/​CoreEx.Template/​content/​CoreEx.Api/​src/​app-name.Api/​Program.cs Uses named destinations.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​.template.config/​template.json Defines Aspire template symbols.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire.MockHost/​Program.cs Templates WireMock host.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire.MockHost/​app-name.Aspire.MockHost.csproj Templates mock project.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​AGENTS.md Templates Aspire guidance.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​AppHost.cs Templates AppHost wiring.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​Properties/​launchSettings.json Templates launch settings.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​app-name.Aspire.csproj Templates AppHost project.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​appsettings.Development.json Templates development settings.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​appsettings.json Templates AppHost settings.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​solution-name.Test.Aspire/​GlobalUsing.cs Templates test imports.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​solution-name.Test.Aspire/​HostTests.cs Templates Aspire tests.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​solution-name.Test.Aspire/​solution-name.Test.Aspire.csproj Templates E2E test project.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​AGENTS.md Updates legacy Aspire guidance.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​AppHost.cs Updates legacy AppHost.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​Extensions.cs Removes superseded helpers.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​appsettings.Development.json Updates legacy settings.
src/​CoreEx.Template/​content/​CoreEx.Core/​.template.config/​template.json Adds conditional topology files.
src/​CoreEx.Template/​content/​CoreEx.Core/​AGENTS.md Documents test topology.
src/​CoreEx.Template/​content/​CoreEx.Core/​README.md Updates generated README.
src/​CoreEx.Template/​content/​CoreEx.Core/​_Directory.Packages.props Adds Aspire package versions.
src/​CoreEx.Template/​content/​CoreEx.Core/​servicebus/​Config.json Empties generated topology.
src/​CoreEx.Template/​content/​CoreEx.Core/​tests/​app-name.Test.Common/​GlobalUsing.cs Adds Service Bus imports.
src/​CoreEx.Template/​content/​CoreEx.Core/​tests/​app-name.Test.Common/​ServiceBus.cs Templates broker topology.
src/​CoreEx.Template/​content/​CoreEx.Core/​tests/​app-name.Test.Common/​app-name.Test.Common.csproj Adds messaging dependency.
src/​CoreEx.Template/​content/​CoreEx.Relay/​tests/​solution-name.Test.Relay/​RelayTests.cs Resets relay topology.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​src/​app-name.Subscribe/​AGENTS.md Documents keyed receivers.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​src/​app-name.Subscribe/​Program.cs Templates keyed subscriber.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​tests/​solution-name.Test.Subscribe/​GlobalUsing.cs Updates test imports.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​tests/​solution-name.Test.Subscribe/​HostTests.cs Updates hosted-service tests.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​tests/​solution-name.Test.Subscribe/​SubscriberTests.cs Updates subscriber tests.
src/​CoreEx.UnitTesting/​CoreEx.UnitTesting.csproj Adds Aspire testing dependency.
src/​CoreEx.UnitTesting/​Events/​EventExpectations.cs Supports revised tester base.
src/​CoreEx.UnitTesting/​GlobalUsing.cs Adds Aspire dependencies.
src/​CoreEx.UnitTesting/​UnitTestExExpectations.ChangeLog.cs Updates expectation compatibility.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.Aspire.cs Adds AppHost helpers.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.Caching.cs Adds Redis reset helper.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.Postgres.cs Adds Aspire migration helper.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.ServiceBus.cs Adds topology reset helpers.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.SqlServer.cs Adds Aspire migration helper.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.WithAspireTester.cs Adds Aspire tester support.
src/​CoreEx/​CoreEx.csproj Adds service discovery dependency.
src/​CoreEx/​CoreExExtensions.ApplicationBuilder.cs Enables HTTP service discovery.
src/​CoreEx/​Hosting/​RetryResiliency.cs Adds reusable retry policy.
tests/​CoreEx.Azure.Messaging.ServiceBus.Test.Unit/​ServiceBusReceiverTests.cs Tests keyed receiver behavior.
tests/​CoreEx.Database.Test.Unit/​DatabaseInvokerRetryTests.cs Tests database retries.
tests/​CoreEx.Events.Test.Unit/​EventFormatterTests.cs Tests metadata preservation.
tests/​CoreEx.Events.Test.Unit/​Publishing/​NamedDestinationProviderTests.cs Tests destination routing.
tests/​CoreEx.RefData.Test.Unit/​ReferenceDataHybridCacheTests.cs Tests typed cache behavior.
tests/​CoreEx.Test.Unit/​Hosting/​RetryResiliencyTests.cs Tests retry policies.
tools/​validate-template-pack.ps1 Validates new templates.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/CoreEx.Template/content/CoreEx.Core/servicebus/Config.json
Comment thread samples/aspire/Contoso.Test.Aspire/E2ETest.cs
Comment thread samples/aspire/Contoso.Test.Aspire/E2ETest.cs Outdated
},
"ProductsApi": {
"BaseAddress": "https://localhost:7200"
"BaseAddress": "https+http://products-api"
… non-flaky

- Restart the Cosmos emulator after the core tests and wait for readiness before the sample tests.
- PartitionPickerTests: assert on the average overlap over many pairs rather than a single random pair (~2% failure rate).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 29, 2026 23:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Standalone service discovery is broken, the Aspire E2E contains an asynchronous replication race, and the activation endpoint is missing established API safeguards.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Enable idempotency middleware and document POST 404 response

samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs:42

This idempotent POST can return NotFoundException, but it neither opts into the idempotency middleware nor documents the 404 response, unlike the established mutating-controller pattern. Retries therefore bypass response deduplication and the generated OpenAPI omits a real outcome.

Aspire launches project resources with 'dotnet run --no-build', which fails for
the multi-targeted sample hosts ("Your project targets multiple frameworks"), so
the hosts exited at startup and the E2E test timed out.

- Contoso.Test.Aspire.runsettings sets DOTNET_TARGET_FRAMEWORK=net10.0 for the test run.
- samples/Directory.Build.props also clears TargetFrameworks when it is set.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 01:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Standalone host resolution regresses, the new activation endpoint lacks focused integration coverage, and asynchronous E2E assertions remain timing-dependent.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Add integration coverage for product activation API

samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs:43

Add a matching ProductMutateTests.Activate.cs integration test for this new API operation. Every other Product mutation has operation-specific API coverage, while the broad Aspire happy path does not verify not-found/no-op behavior or the activated outbox event.

Medium severity Add fallback products-api endpoint for standalone Subscribe runs

samples/​src/​Contoso.Shopping.Subscribe/​appsettings.Development.json:41

This logical Aspire URI has no fallback endpoint when the Subscribe host is launched directly, so the documented non-Aspire dotnet run workflow (samples/docs/local-dev.md:158-176) can no longer resolve products-api. Add local Services:products-api endpoints; Aspire's WithReference configuration can then override those endpoints during orchestration.

TestSynchronizer incremented its enter/exit counts before recording the last name/type, so tests
polling the count could read a null value. Record the values first.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 01:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Generated local Service Bus topology is unavailable outside tests, and the new Aspire E2E contains timing races that can fail intermittently.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Missing focused API tests for product activation

samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs:43

This adds a production API operation, but Contoso.Products.Test.Api has no activation test; the only coverage is the much broader Aspire happy path. Add focused API tests for activation success (including persistence/event output), already-active idempotency, and not-found behavior so endpoint/service regressions are isolated.

Medium severity Shopping subscriber registration conflicts with keyed guidance

samples/​tests/​Contoso.Shopping.Test.Subscribe/​SubscriberTests.BasketCheckedOut.cs:27

This new event-subscriber test resolves an unkeyed subscriber, while the updated coreex-test-subscribe guidance says event subscribers use the subscriber-events key and the templates now generate that pattern. Because these samples are the model for the new skills, align Shopping's receiver and tests with the keyed event registration (or narrow the guidance) to avoid generating code that disagrees with its reference implementation.

Low severity Undocumented public database retry API and release behavior

src/​CoreEx.Database/​Abstractions/​DatabaseArgsBase.cs:31

This introduces a public opt-in database retry API (plus provider transient classifications and relay behavior changes), but neither the PR summary nor the v4.1.0 changelog mentions it. That is a material release feature unrelated to the stated command/Aspire scope; document its behavior and compatibility impact in this release or split it into a separately reviewed change.

The E2E hosts call each other (shopping -> products) over https using the dev certificate, which
is untrusted on Linux runners (UntrustedRoot). Export it and add it to the system trust store.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 14:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

- Contoso Aspire E2E now detects the stubbed SendGrid call by polling the
  WireMock admin request journal (bounded 30x1s) instead of scraping logs.
- CoreEx.Core / CoreEx.Aspire templates emit tests/ and aspire/
  Directory.Build.props (NoWarn IDE1006, imports the solution-root props);
  validate-template-pack.ps1 asserts both.
- Instructions/skills: patch/subset contracts must be standalone (no
  inheriting a sibling and suppressing members); Aspire test guidance uses
  the admin-API polling pattern; new end-of-file rule (exactly one trailing
  newline).
- Strip extra trailing blank lines from 14 existing files.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 00:29
- Application-service instructions/skill: add guidance on propagating a failed
  Result (return the result itself; never rebuild from .Error).
- docs/: refresh agent guides, capabilities, codebase map and slides.
- CodeGenCounter: validate the output directory exists and tolerate a missing
  parent when choosing the directory to count.
- ExtensionsTests: end-of-file newline fix.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The activation flow leaves Shopping’s replicated product state stale, and the E2E test contains an eventual-consistency race.

Review effort: Balanced
Findings: 2 High severity · 2 Medium severity

Open (4)
Resolved since last review (1)

Comment on lines +40 to +43
[HttpPost("{id}/activate")]
[ProducesResponseType(typeof(Product), 200)]
public Task<IActionResult> ActivateAsync(string id, CancellationToken cancellationToken = default) => _webApi.PostAsync<Product>(Request, (_, ct)
=> _service.ActivateAsync(id.Required(), ct), HttpStatusCode.OK, cancellationToken: cancellationToken);
Copilot AI balanced review requested due to automatic review settings October 2, 2026 00:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The new E2E flow contains an asynchronous replication race, and several API/documentation/template issues require correction.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
Resolved since last review (2)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Add idempotency middleware to Product activation POST

samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs:41

This create-style POST is safe to retry, but without [IdempotencyKey] concurrent retries can both observe an inactive Product and publish duplicate activation events. Apply the standard POST idempotency middleware used by the other Products POST endpoints.

Low severity Document TitlePrefix removal before parsing DomainName

src/​CoreEx.Events/​EventData.cs:45

Title includes the optional solution-name prefix, so DomainName is not necessarily its first segment. The formatter strips TitlePrefix before parsing; document that distinction to avoid consumers treating the solution segment (for example contoso) as the domain (shopping).

Low severity Sort GlobalUsing.cs entries in ordinal order

src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​solution-name.Test.Aspire/​GlobalUsing.cs:3

The generated GlobalUsing.cs is not ordinally sorted (AwesomeAssertions must precede CoreEx). CoreEx requires the entire global-using file to remain sorted so generated scaffolds start convention-compliant.

- **Before building Path C:** confirm `I{Name}Repository` already has `QueryAsync`/`QuerySchemaAsync` backed by a `{Name}QueryArgsConfig`. If not, stop and invoke `coreex-repository` first — never add filtering/ordering logic or a hand-rolled query in the service to work around a missing repository method
- Always `.ConfigureAwait(false)` on every `await`
- A Domain value object persisted via a JSON column (e.g. `Basket.ShippingAddress`) is mapped with a `BiDirectionMapper<TDomain, TContract, TSelf>` in `Application/Mapping/` (not the uni-directional `Mapper<TSource,TDest,TSelf>` used for the root aggregate) — see [`coreex-application-services.instructions.md#json-backed-value-object-mapping`](/.github/instructions/coreex-application-services.instructions.md#json-backed-value-object-mapping)
- `Result<T>` failure propagation: prefer a `Then*` chain; for a guard clause `return r;` (same type, or `Result` → `Result<U>`) or `return r.AsResult();` (`Result<T>` → `Result<U>`) — **never** `Result<X>.Fail(r.Error!)` / `return r.Error!;`, and don't touch `.Error` in service code- A Domain value object persisted via a JSON column (e.g. `Basket.ShippingAddress`) is mapped with a `BiDirectionMapper<TDomain, TContract, TSelf>` in `Application/Mapping/` (not the uni-directional `Mapper<TSource,TDest,TSelf>` used for the root aggregate) — see [`coreex-application-services.instructions.md#json-backed-value-object-mapping`](/.github/instructions/coreex-application-services.instructions.md#json-backed-value-object-mapping)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants