Auto-start Cosmos emulator via Testcontainers#37999
Conversation
- Add Testcontainers.CosmosDb package reference - Rewrite TestEnvironment.cs with lazy connection init that auto-starts a testcontainer when no emulator is configured or reachable - Use testcontainer's HttpMessageHandler for URI rewriting - Auto-detect Linux emulator (always true for testcontainers) - Set cosmosConfig.json DefaultConnection to null for auto-detection - Simplify helix.proj (remove manual Docker pre/post commands) - Remove EmulatorType env var from copilot-setup-steps.yml - Update SKILL.md with new testcontainer approach Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/655b1ac0-f385-43cf-9276-b3c6b2501432
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/655b1ac0-f385-43cf-9276-b3c6b2501432
AndriySvyryd
left a comment
There was a problem hiding this comment.
@copilot Remove the run-cosmos-container scripts
…re helix/copilot env vars, delete scripts
- Make TestEnvironment initialization fully async (InitializeAsync)
- Set DefaultConnection and HttpMessageHandler values during async init
- If SkipConnectionCheck=true, let testcontainer failures throw
- Create HttpClient instance outside the lambda in ApplyConfiguration
- Inline IsTestContainer (removed property)
- Restore copilot-setup-steps.yml env vars (EmulatorType, DefaultConnection, SkipConnectionCheck)
- Restore helix.proj Windows.11 block and SkipConnectionCheck for Ubuntu
- Delete eng/testing/run-cosmos-container.{sh,ps1}
- Update SKILL.md
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/7f2ba766-d1b7-484b-899c-4b6d320a97e5
There was a problem hiding this comment.
Pull request overview
This PR updates EF Core Cosmos functional test infrastructure to automatically provision the Azure Cosmos DB emulator via Testcontainers.CosmosDb, replacing explicit Docker scripts and simplifying Linux emulator detection for CI and local runs.
Changes:
- Add async Cosmos connection initialization with a configured-endpoint → localhost probe → testcontainer fallback sequence.
- Switch Cosmos test HTTP handling to optionally use the testcontainer-provided
HttpMessageHandler. - Remove Helix/Docker Cosmos emulator scripts and update CI/setup configuration and internal skill docs accordingly.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/EFCore.Cosmos.FunctionalTests/cosmosConfig.json | Sets DefaultConnection to null to enable auto-detection rather than forcing localhost. |
| test/EFCore.Cosmos.FunctionalTests/TestUtilities/TestEnvironment.cs | Implements async initialization, testcontainer startup, and Linux emulator auto-detection. |
| test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs | Ensures TestEnvironment.InitializeAsync() runs before connection availability checks. |
| test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosDbContextOptionsBuilderExtensions.cs | Uses testcontainer handler when available; adjusts HttpClient creation/factory usage. |
| test/EFCore.Cosmos.FunctionalTests/EFCore.Cosmos.FunctionalTests.csproj | Adds Testcontainers.CosmosDb dependency for Cosmos functional tests. |
| test/Directory.Packages.props | Adds centralized package version for Testcontainers.CosmosDb. |
| eng/testing/run-cosmos-container.sh | Removes Linux Docker script (superseded by testcontainers). |
| eng/testing/run-cosmos-container.ps1 | Removes Windows Docker script (superseded by testcontainers). |
| eng/helix.proj | Removes Ubuntu Cosmos Docker pre/post commands; relies on testcontainer auto-start. |
| .github/workflows/copilot-setup-steps.yml | Pins Cosmos env vars to use the workflow’s service container and avoid starting a second emulator. |
| .agents/skills/cosmos-provider/SKILL.md | Updates Cosmos test/emulator documentation to match the new testcontainer-based flow. |
…:false, add DefaultConnection initial value Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/2469bf2b-2857-4f34-89ee-e1c963e35d9d
…ILL.md suggestion Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/655b5153-de78-4ee4-988b-30f981564c60
…remove localhost probing and failure fallback Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/ab54e2d2-c2e8-4385-8da4-f514a5ab5f11 Co-authored-by: roji <1862641+roji@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/dfeb7657-0a60-4352-99d3-2c2a83f58c14 Co-authored-by: roji <1862641+roji@users.noreply.github.com>
|
|
||
| _container = container; | ||
|
|
||
| AppDomain.CurrentDomain.ProcessExit += (_, _) => |
There was a problem hiding this comment.
We'd ideally use an xunit assembly fixture to start and stop the container.
There was a problem hiding this comment.
@copilot use an xunit assembly fixture to start and stop the container.
There was a problem hiding this comment.
Done in 8ea3913. Implemented a custom xunit test framework (CosmosTestFramework → CosmosTestFrameworkExecutor → CosmosTestAssemblyRunner) that manages a CosmosEmulatorFixture as an assembly-wide fixture. The fixture calls TestEnvironment.InitializeAsync() before any tests run and TestEnvironment.DisposeAsync() after all tests complete. Removed the ProcessExit handler from TestEnvironment.
The Xunit.Extensions.AssemblyFixture NuGet package wasn't available in the configured feeds, so I implemented a minimal version inline using XunitTestFramework/XunitTestAssemblyRunner hooks.
Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/a26908c4-620d-4aab-becc-397ea330aed7 Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/adbe06d2-7d81-443d-b5fc-9d87f693de49 Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/adbe06d2-7d81-443d-b5fc-9d87f693de49 Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/cf8f0f07-50d1-47f8-b14b-4aa7f4231898 Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Done in 91b538a. Reverted the xunit assembly fixture changes from 8ea3913 and 3a44583 — deleted |
Follow up to #37110
Replace explicit Docker scripts for the Cosmos emulator with automatic
Testcontainers.CosmosDblifecycle management, matching the pattern from #37809 for SQL Server. Also auto-detect the Linux emulator instead of requiringTest__Cosmos__EmulatorType=linux.TestEnvironment connection initialization
3-path logic in
TestEnvironment.InitializeAsync()(called once at assembly start via an xunit assembly fixture):Test__Cosmos__DefaultConnectionenv var or config file → use directlyhttps://localhost:8081for an already-running emulator (e.g. Windows emulator or manually-started Docker container) → use if reachableCosmosDbContainerwithvnext-previewimage; dispose at assembly end via the fixtureThe localhost probe enables a convenient developer workflow on Windows where the emulator can be running without needing to set an environment variable. When the testcontainer fails to start, the exception is wrapped with a helpful message explaining that either the Cosmos DB emulator should be running on localhost:8081, Docker must be installed and running, or the
Test__Cosmos__DefaultConnectionenvironment variable should be set to connect to an existing emulator or Cosmos DB instance.DefaultConnectionhas a sensible default initial value (from config orhttps://localhost:8081) so it can be read safely from constructors before initialization.InitializeAsync()is called explicitly fromCosmosTestStore.InitializeAsync()rather than implicitly from property getters, to avoid accidentally starting an emulator from unit tests. After initialization,CosmosTestStoreupdates itsConnectionUriandConnectionStringto reflect any changes (e.g., testcontainer startup).Assembly fixture for container lifecycle
The testcontainer lifecycle is managed via an xunit assembly fixture (
CosmosEmulatorFixture) using a customCosmosTestFramework→CosmosTestFrameworkExecutor→CosmosTestAssemblyRunnerchain:CosmosEmulatorFixture.InitializeAsync()callsTestEnvironment.InitializeAsync()once at assembly start, before any test classes are constructedCosmosEmulatorFixture.DisposeAsync()callsTestEnvironment.DisposeAsync()at assembly end, which stops and disposes the testcontainer if one was startedProcessExitevent handler approach, ensuring proper cleanup through xunit's lifecycleLinux emulator auto-detection
IsLinuxEmulatoris now true when:EmulatorTypeconfig explicitly set to"linux"(backward compat)No env var required to opt into the Linux emulator — it is detected automatically.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.