Skip to content

Prevent recursive MTP test runner invocation - #20573

Merged
David Negstad (danegsta) merged 3 commits into
mainfrom
danegsta-testing-orphaned-apphosts
Oct 1, 2026
Merged

David Negstad (danegsta) merged 3 commits into
mainfrom
danegsta-testing-orphaned-apphosts

Conversation

@danegsta

@danegsta David Negstad (danegsta) commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Description

DistributedApplicationTestingBuilder.CreateAsync assumes the supplied type belongs to an AppHost executable and reflectively invokes that assembly's entry point. When a Microsoft.Testing.Platform test project is itself built with Aspire.AppHost.Sdk, the generated Projects.* type can instead belong to the test application. Invoking that assembly's entry point recursively starts the test runner, reruns the entire suite without the original filter, and can leave many AppHost and DCP processes active.

Detect the Microsoft.Testing.Platform.Application=true assembly metadata before invoking the entry point and fail with a clear explanation. The diagnostic directs users to build test projects with Microsoft.NET.Sdk, reference the AppHost project, or use DistributedApplicationTestingBuilder.Create() when no entry-point invocation is required. Using the assembly marker also covers test-framework wrappers such as xUnit's generated entry point.

Validated with:

dotnet test --project tests/Aspire.Hosting.Testing.Tests/Aspire.Hosting.Testing.Tests.csproj --no-launch-profile -- --filter-class "*.DistributedApplicationEntryPointInvokerTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

Fixes #20572

Related to #20568

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 20573

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 20573"

@aspire-repo-bot
aspire-repo-bot Bot requested a balanced review from Copilot September 29, 2026 00:34
@github-actions github-actions Bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Sep 29, 2026
@github-actions

This comment has been minimized.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.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

🟢 Approval recommended

The focused guard prevents the reported recursion while preserving valid AppHost entry-point resolution with regression coverage.

Review effort: Balanced
Findings: None

What changed in this PR

Prevents recursive Microsoft.Testing.Platform test execution when resolving an AppHost entry point.

Changes:

  • Detects MTP application metadata and fails with actionable guidance.
  • Adds regression and valid-AppHost tests.
File Description
src/​Aspire.Hosting.Testing/​DistributedApplicationEntryPointInvoker.cs Rejects MTP test application entry points.
tests/​Aspire.Hosting.Testing.Tests/​DistributedApplicationEntryPointInvokerTests.cs Verifies rejection and normal AppHost resolution.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@afscrome

Alex Crome (afscrome) commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Rather than blocking MTP, could we concretely detect an App Host assembly? Even with this fix, you could get a similar issue if you referenced a type from a Consle app.

e.g. If the Aspire SDK adds a unique assembly marker to app hosts, could we look for that?

@github-actions

This comment has been minimized.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.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

🟢 Approval recommended

The focused guard prevents recursive test execution while preserving valid AppHost entry-point resolution with regression coverage.

Review effort: Balanced
Findings: None

@github-actions

Copy link
Copy Markdown
Contributor

Tests selector

41 / 99 PR test projects · 1 PR job, from 3 changed files.

Selected PR test projects (41 / 99)

Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.Java.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.Orleans.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.Rust.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests

Selected PR jobs (1)

typescript-api-compat


How these were chosen — grouped by what changed

⚠️ 41 of the 41 selected test projects come from a single change — src/Aspire.Hosting.Testing/DistributedApplicationEntryPointInvoker.cs.

🔧 src/Aspire.Hosting.Testing/DistributedApplicationEntryPointInvoker.cs (changed source)
→ 1 directly: Aspire.Hosting.Testing.Tests
→ 40 via the project graph

show 40

Aspire.Hosting.Azure.Kubernetes.Tests (2 hops), Aspire.Hosting.Azure.Kusto.Tests (2 hops), Aspire.Hosting.Azure.Tests (2 hops), Aspire.Hosting.Blazor.Tests (2 hops), Aspire.Hosting.Browsers.Tests (2 hops), Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests (2 hops), Aspire.Hosting.Docker.Tests (2 hops), Aspire.Hosting.Dotnet.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.EntityFrameworkCore.Tests (2 hops), Aspire.Hosting.Foundry.Tests (2 hops), Aspire.Hosting.Garnet.Tests (2 hops), Aspire.Hosting.Go.Tests (2 hops), Aspire.Hosting.Java.Tests (2 hops), Aspire.Hosting.JavaScript.Tests (2 hops), Aspire.Hosting.Kafka.Tests (2 hops), Aspire.Hosting.Keycloak.Tests (2 hops), Aspire.Hosting.Kubernetes.Tests (2 hops), Aspire.Hosting.Maui.Tests (2 hops), Aspire.Hosting.Milvus.Tests (2 hops), Aspire.Hosting.MongoDB.Tests (2 hops), Aspire.Hosting.MySql.Tests (2 hops), Aspire.Hosting.Nats.Tests (2 hops), Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests (2 hops), Aspire.Hosting.Orleans.Tests (2 hops), Aspire.Hosting.PostgreSQL.Tests (2 hops), Aspire.Hosting.Python.Tests (2 hops), Aspire.Hosting.Qdrant.Tests (2 hops), Aspire.Hosting.RabbitMQ.Tests (2 hops), Aspire.Hosting.Radius.Tests (2 hops), Aspire.Hosting.Redis.Tests (2 hops), Aspire.Hosting.Rust.Tests (2 hops), Aspire.Hosting.Seq.Tests (2 hops), Aspire.Hosting.SqlServer.Tests (2 hops), Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests (2 hops), Aspire.Hosting.Yarp.Tests (2 hops), Aspire.Playground.Tests

🔧 src/Aspire.Hosting.Testing/DistributedApplicationFactory.cs (changed source)
→ 1 directly: Aspire.Hosting.Testing.Tests

🧪 tests/Aspire.Hosting.Testing.Tests/DistributedApplicationEntryPointInvokerTests.cs (changed test)
→ 1 directly: Aspire.Hosting.Testing.Tests

Job reasons

Job Triggered by
typescript-api-compat affected project Aspire.Hosting.Testing

Selection computed for commit d1e0e8d.

@danegsta

Copy link
Copy Markdown
Member Author

Rather than blocking MTP, could we concretely detect an App Host assembly? Even with this fix, you could get a similar issue if you referenced a type from a Consle app.

e.g. If the Aspire SDK adds a unique assembly marker to app hosts, could we look for that?

Updated to a more expansive check to look for the general shape of generated test project assemblies getting shadowed by the MTP test assembly.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed locally; no high-confidence issues found. Focused DistributedApplicationEntryPointInvokerTests passed: 4 passed, 0 failed.

@danegsta
David Negstad (danegsta) merged commit cb7844e into main Oct 1, 2026
270 of 273 checks passed
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 17.0 milestone Oct 1, 2026
aspire-repo-bot Bot added a commit to microsoft/aspire.dev that referenced this pull request Oct 1, 2026
Adds a troubleshooting section to the advanced testing scenarios page
explaining why Aspire.AppHost.Sdk test projects can recursively invoke
the test runner via DistributedApplicationTestingBuilder.CreateAsync,
and how to fix it (Microsoft.NET.Sdk + ProjectReference, dynamic
assembly loading, or DistributedApplicationTestingBuilder.Create()).

Documents microsoft/aspire#20573

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@aspire-repo-bot

Copy link
Copy Markdown
Contributor

Pull request created: #1804

Generated by PR Documentation Check · copilot · auto · 58.3 AIC · ⌖ 16.6 AIC · ⊞ 20.3K

@aspire-repo-bot

Copy link
Copy Markdown
Contributor

📝 Documentation has been drafted in microsoft/aspire.dev#1804 targeting release/17.0.

Documents the new guard in DistributedApplicationEntryPointInvoker that detects when a Microsoft.Testing.Platform test project built with Aspire.AppHost.Sdk recursively invokes itself via DistributedApplicationTestingBuilder.CreateAsync. Triggered signal: pr_body_has_cli_flag_mention (evidence: the dotnet test --filter-class ... validation command in the PR body). Added a new "Avoid building test projects with Aspire.AppHost.Sdk" troubleshooting subsection to src/frontend/src/content/docs/testing/advanced-scenarios.mdx, covering the symptom, the InvalidOperationException message users will see, and the three remediation paths named in the PR body (switch to Microsoft.NET.Sdk + ProjectReference, load the AppHost assembly dynamically and use CreateAsync(Type), or use DistributedApplicationTestingBuilder.Create() when no entry-point invocation is needed).

Note

This draft PR needs human review before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CreateAsync<TEntryPoint> recursively re-runs the whole test session when the test project uses Aspire.AppHost.Sdk

4 participants