Skip to content

Commit dba485a

Browse files
committed
Run Cosmos tests in a Linux container in Helix
Don't skip tests if emulator not available
1 parent 8bfecfd commit dba485a

File tree

44 files changed

+430
-17
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+430
-17
lines changed

.agents/skills/cosmos-provider/SKILL.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,13 @@ Non-relational provider with its own parallel query pipeline. Uses JSON for docu
2020
- Partition key configuration required for performance
2121
- `ETag` for optimistic concurrency
2222
- No cross-container joins
23+
24+
## Azure Cosmos DB Emulator in Docker
25+
26+
Cosmos tests run on Helix via Docker sidecar containers:
27+
- `eng/testing/run-cosmos-container.ps1`
28+
- `eng/testing/run-cosmos-container.sh`
29+
30+
These scripts can be invoked locally for testing on machines that don't have the emulator installed, but have docker available.
31+
32+
The `Test__Cosmos__SkipConnectionCheck=true` env var is set to prevent tests from being skipped when the emulator failed to start.

.github/workflows/TestCosmos.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ jobs:
3434
- name: Test on Cosmos
3535
run: dotnet test test/EFCore.Cosmos.FunctionalTests/EFCore.Cosmos.FunctionalTests.csproj
3636
shell: cmd
37+
env:
38+
Test__Cosmos__SkipConnectionCheck: true
3739

3840
- name: Publish Test Results
3941
uses: actions/upload-artifact@v7

.github/workflows/copilot-setup-steps.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ jobs:
3838
--health-retries=30
3939
--health-timeout=5s
4040
41+
cosmosdb:
42+
image: mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview
43+
ports:
44+
- 8081:8081
45+
env:
46+
PROTOCOL: https
47+
ENABLE_EXPLORER: "false"
48+
4149
steps:
42-
- name: Export connection string for the agent's session
43-
run: echo 'Test__SqlServer__DefaultConnection=Server=localhost;Database=test;User=SA;Password=PLACEHOLDERPass$$w0rd;Connect Timeout=60;ConnectRetryCount=0;Trust Server Certificate=true' >> "$GITHUB_ENV"
50+
- name: Export environment variables the agent's session
51+
run: |
52+
echo 'Test__SqlServer__DefaultConnection=Server=localhost;Database=master;User=SA;Password=PLACEHOLDERPass$$w0rd;Connect Timeout=60;ConnectRetryCount=0;Trust Server Certificate=true' >> "$GITHUB_ENV"
53+
echo 'Test__Cosmos__EmulatorType=linux' >> "$GITHUB_ENV"

azure-pipelines-internal-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ extends:
211211
- name: _HelixBuildConfig
212212
value: $(_BuildConfig)
213213
- name: HelixTargetQueues
214-
value: Windows.10.Amd64;OSX.15.Amd64;OSX.15.ARM64;Ubuntu.2204.Amd64.XL@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-sqlserver-amd64
214+
value: Windows.10.Amd64;OSX.15.Amd64;OSX.15.ARM64;Ubuntu.2204.Amd64.XL@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-sqlserver-amd64;Ubuntu.2204.Amd64.XL;Ubuntu.2204.Amd64
215215
- name: _HelixAccessToken
216216
# Needed for internal queues
217217
value: $(HelixApiAccessToken)

azure-pipelines-public.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ stages:
152152
- name: _HelixBuildConfig
153153
value: $(_BuildConfig)
154154
- name: HelixTargetQueues
155-
value: Windows.10.Amd64.Open;OSX.15.Amd64.Open;Ubuntu.2204.Amd64.XL.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-sqlserver-amd64
155+
value: Windows.10.Amd64.Open;OSX.15.Amd64.Open;OSX.15.ARM64.Open;Ubuntu.2204.Amd64.XL.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-sqlserver-amd64;Ubuntu.2204.Amd64.XL.Open;Ubuntu.2204.Amd64.Open
156156
- name: _HelixAccessToken
157157
value: '' # Needed for public queues
158158
steps:

eng/helix.proj

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@
1212

1313
<EnableAzurePipelinesReporter>true</EnableAzurePipelinesReporter>
1414
<FailOnTestFailure>true</FailOnTestFailure>
15-
<SqlServerTests>$(RepoRoot)/test/EFCore.SqlServer.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.SqlServer.HierarchyId.Tests/*.csproj;$(RepoRoot)/test/EFCore.OData.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.AspNet.SqlServer.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.VisualBasic.FunctionalTests/*.vbproj;$(RepoRoot)/test/EFCore.FSharp.FunctionalTests/*.fsproj</SqlServerTests>
15+
<SqlServerTests>$(RepoRoot)/test/EFCore.SqlServer.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.SqlServer.HierarchyId.Tests/*.csproj;$(RepoRoot)/test/EFCore.CrossStore.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.OData.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.AspNet.SqlServer.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.VisualBasic.FunctionalTests/*.vbproj;$(RepoRoot)/test/EFCore.FSharp.FunctionalTests/*.fsproj</SqlServerTests>
16+
<CosmosTests>$(RepoRoot)/test/EFCore.Cosmos.FunctionalTests/*.csproj</CosmosTests>
1617
</PropertyGroup>
1718

1819
<PropertyGroup Condition = "'$(SYSTEM_ACCESSTOKEN)' == ''">
1920
<!-- Local build outside of Azure Pipeline -->
20-
<HelixTargetQueues Condition = "'$(HelixTargetQueues)' == ''">Windows.10.Amd64.Open;OSX.1200.Amd64.Open;OSX.1200.ARM64.Open;Ubuntu.2204.Amd64.XL.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-sqlserver-amd64</HelixTargetQueues>
21+
<HelixTargetQueues Condition = "'$(HelixTargetQueues)' == ''">Windows.10.Amd64.Open;OSX.15.Amd64.Open;OSX.15.ARM64.Open;Ubuntu.2204.Amd64.XL.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-sqlserver-amd64;Ubuntu.2204.Amd64.XL.Open;Ubuntu.2204.Amd64.Open</HelixTargetQueues>
2122
<EnableAzurePipelinesReporter>false</EnableAzurePipelinesReporter>
2223
<HelixSource>efcore/localbuild/</HelixSource>
2324
<HelixBuild>t001</HelixBuild>
@@ -33,6 +34,10 @@
3334
<AdditionalDotNetPackage Include="$(MicrosoftNETCorePlatformsVersion)">
3435
<PackageType>runtime</PackageType>
3536
</AdditionalDotNetPackage>
37+
38+
<HelixCorrelationPayload Include="$(RepoRoot)/eng/testing">
39+
<Destination>testing</Destination>
40+
</HelixCorrelationPayload>
3641
</ItemGroup>
3742

3843
<ItemGroup>
@@ -53,25 +58,48 @@
5358
</XUnitProject>
5459
</ItemGroup>
5560

56-
<!-- Start LocalDb instance for test projects which uses SqlServer on windows -->
61+
<!-- Start LocalDb instance for test projects which uses SqlServer on Windows -->
5762
<ItemGroup Condition = "'$(HelixTargetQueue.StartsWith(`Windows`))'">
58-
<XUnitProject Update="$(SqlServerTests);$(RepoRoot)/test/EFCore.CrossStore.FunctionalTests/*.csproj">
63+
<XUnitProject Update="$(SqlServerTests)">
5964
<PreCommands>$(PreCommands); SqlLocalDB start</PreCommands>
6065
</XUnitProject>
6166
</ItemGroup>
67+
68+
<ItemGroup Condition = "'$(HelixTargetQueue.StartsWith(`Windows.11`))'">
69+
<XUnitProject Update="$(CosmosTests)">
70+
<PreCommands>$(PreCommands); set Test__Cosmos__SkipConnectionCheck=true</PreCommands>
71+
</XUnitProject>
72+
</ItemGroup>
6273

63-
<!-- Start SqlServer instance for test projects which uses SqlServer on docker. Also remove other projects as they will be run outside of docker. -->
64-
<ItemGroup Condition = "'$(HelixTargetQueue.Contains(`ubuntu-22.04-helix-sqlserver-amd64`))'">
74+
<!-- Start SqlServer instance for test projects which uses SqlServer on docker. Only run SqlServer tests. -->
75+
<ItemGroup Condition = "'$(HelixTargetQueue.Contains(`helix-sqlserver`))'">
6576
<XUnitProject Remove="$(RepoRoot)/test/**/*.csproj"/>
6677
<XUnitProject Remove="$(RepoRoot)/test/**/*.fsproj"/>
67-
<XUnitProject Include="$(SqlServerTests);$(RepoRoot)/test/EFCore.CrossStore.FunctionalTests/*.csproj">
78+
<XUnitProject Include="$(SqlServerTests)">
6879
<PreCommands>$(PreCommands); export MSSQL_SA_PASSWORD=$(MSSQL_SA_PASSWORD); export Test__SqlServer__DefaultConnection="Server=localhost;;Database=master;;User=sa;;Password=$(MSSQL_SA_PASSWORD);;Connect Timeout=60;;ConnectRetryCount=0;;TrustServerCertificate=True"; /opt/mssql/bin/sqlservr --accept-eula &amp;; sleep 120; </PreCommands>
6980
</XUnitProject>
7081
</ItemGroup>
7182

72-
<!-- Remove test projects which requires SqlServer from Ubuntu/OSX. -->
73-
<ItemGroup Condition = "'$(HelixTargetQueue.StartsWith(`OSX`))' OR '$(HelixTargetQueue)' == 'Ubuntu.2204.Amd64.XL.Open' OR '$(HelixTargetQueue)' == 'Ubuntu.2204.Amd64.XL'">
83+
<!-- Start Cosmos emulator in Docker on Ubuntu and only run Cosmos tests -->
84+
<ItemGroup Condition = "'$(HelixTargetQueue)' == 'Ubuntu.2204.Amd64.XL.Open' OR '$(HelixTargetQueue)' == 'Ubuntu.2204.Amd64.XL'">
85+
<XUnitProject Remove="$(RepoRoot)/test/**/*.csproj"/>
86+
<XUnitProject Remove="$(RepoRoot)/test/**/*.fsproj"/>
87+
<XUnitProject Include="$(CosmosTests)">
88+
<PreCommands>$(PreCommands); chmod +x $HELIX_CORRELATION_PAYLOAD/testing/run-cosmos-container.sh; $HELIX_CORRELATION_PAYLOAD/testing/run-cosmos-container.sh; export Test__Cosmos__DefaultConnection=https://localhost:8081; export Test__Cosmos__SkipConnectionCheck=true; export Test__Cosmos__EmulatorType=linux</PreCommands>
89+
<PostCommands>$(PostCommands); docker stop cosmos-emulator || true; docker rm -f cosmos-emulator || true</PostCommands>
90+
</XUnitProject>
91+
</ItemGroup>
92+
93+
<!-- Run tests that don't need SqlServer or Cosmos on bare Ubuntu -->
94+
<ItemGroup Condition = "'$(HelixTargetQueue)' == 'Ubuntu.2204.Amd64.Open' OR '$(HelixTargetQueue)' == 'Ubuntu.2204.Amd64'">
95+
<XUnitProject Remove="$(SqlServerTests)"/>
96+
<XUnitProject Remove="$(CosmosTests)"/>
97+
</ItemGroup>
98+
99+
<!-- Remove test projects which require SqlServer or Cosmos from OSX -->
100+
<ItemGroup Condition = "'$(HelixTargetQueue.StartsWith(`OSX`))'">
74101
<XUnitProject Remove="$(SqlServerTests)"/>
102+
<XUnitProject Remove="$(CosmosTests)"/>
75103
</ItemGroup>
76104

77105
<PropertyGroup>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Starts the Azure Cosmos DB Emulator in a Windows Docker container and waits for it to be ready.
2+
# Tests run on the host machine connecting to the emulator via https://localhost:8081.
3+
# Usage: .\run-cosmos-container.ps1
4+
param()
5+
6+
$ErrorActionPreference = 'Stop'
7+
$image = 'mcr.microsoft.com/cosmosdb/windows/azure-cosmos-emulator'
8+
$containerName = 'cosmos-emulator'
9+
$port = 8081
10+
$maxRetries = 90
11+
$retryDelaySec = 2
12+
13+
Write-Host "Pulling image: $image"
14+
docker pull $image
15+
if ($LASTEXITCODE -ne 0) { throw "docker pull failed with exit code $LASTEXITCODE" }
16+
17+
# -t is required because Start.ps1 sets [Console]::BufferWidth which needs a TTY handle.
18+
Write-Host "Starting Cosmos DB Emulator container on port $port..."
19+
docker run -d -t `
20+
--name $containerName `
21+
--publish "${port}:8081" `
22+
--memory 2G `
23+
$image
24+
if ($LASTEXITCODE -ne 0) { throw "docker run failed with exit code $LASTEXITCODE" }
25+
26+
Write-Host "Waiting for emulator to be ready (up to $($maxRetries * $retryDelaySec)s)..."
27+
$ready = $false
28+
for ($i = 0; $i -lt $maxRetries; $i++) {
29+
Start-Sleep -Seconds $retryDelaySec
30+
try {
31+
# Any HTTP response (even 401) means the emulator is up and accepting connections.
32+
$null = Invoke-WebRequest -Uri "https://localhost:${port}/" -UseBasicParsing -TimeoutSec 5
33+
$ready = $true
34+
} catch [Microsoft.PowerShell.Commands.HttpResponseException] {
35+
# Got an HTTP error response (401, 404, etc.) — emulator is reachable.
36+
$ready = $true
37+
} catch {
38+
Write-Host " Attempt $($i+1)/$maxRetries - not ready yet..."
39+
}
40+
if ($ready) {
41+
Write-Host "Cosmos DB Emulator is ready on port $port."
42+
break
43+
}
44+
}
45+
46+
if (-not $ready) {
47+
Write-Host "Emulator did not become ready. Container logs:"
48+
docker logs $containerName
49+
docker stop $containerName 2>$null
50+
docker rm -f $containerName 2>$null
51+
exit 1
52+
}
53+
54+
exit 0
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env bash
2+
# Starts the Azure Cosmos DB Linux (vNext) Emulator in a Docker container and waits for it to be ready.
3+
# Tests run on the host machine connecting to the emulator via https://localhost:8081.
4+
# The --protocol https flag is required because the .NET SDK does not support HTTP mode.
5+
# Usage: ./run-cosmos-container.sh
6+
7+
set -e
8+
9+
image='mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview'
10+
container_name='cosmos-emulator'
11+
port=8081
12+
max_retries=30
13+
retry_delay=2
14+
15+
echo "Pulling image: $image"
16+
docker pull "$image"
17+
18+
if docker ps -a --format '{{.Names}}' | grep -Eq "^${container_name}\$"; then
19+
echo "Removing existing Cosmos DB Emulator container: $container_name"
20+
docker rm -f "$container_name"
21+
fi
22+
23+
echo "Starting Cosmos DB Emulator container on port $port with HTTPS..."
24+
docker run -d \
25+
--name "$container_name" \
26+
--publish "${port}:8081" \
27+
"$image" \
28+
--protocol https \
29+
--enable-explorer false
30+
31+
echo "Waiting for emulator to be ready (up to $((max_retries * retry_delay))s)..."
32+
ready=false
33+
for i in $(seq 1 "$max_retries"); do
34+
sleep "$retry_delay"
35+
if curl -ks "https://localhost:${port}/" -o /dev/null; then
36+
ready=true
37+
echo "Cosmos DB Emulator is ready."
38+
break
39+
fi
40+
echo " Attempt $i/$max_retries - not ready yet..."
41+
done
42+
43+
if [ "$ready" != true ]; then
44+
echo "Emulator did not become ready. Container logs:"
45+
docker logs "$container_name"
46+
docker stop "$container_name" 2>/dev/null || true
47+
docker rm -f "$container_name" 2>/dev/null || true
48+
exit 1
49+
fi
50+
51+
exit 0

src/EFCore/Query/Internal/QueryCompiler.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ public virtual Expression ExtractParameters(
156156
Expression query,
157157
Dictionary<string, object?> parameters,
158158
IDiagnosticsLogger<DbLoggerCategory.Query> logger,
159-
bool compiledQuery = false,
160-
bool generateContextAccessors = false)
159+
bool compiledQuery = false)
161160
=> new ExpressionTreeFuncletizer(_model, _evaluatableExpressionFilter, _contextType, generateContextAccessors: false, logger)
162161
.ExtractParameters(query, parameters, parameterize: !compiledQuery, clearParameterizedValues: true);
163162
}

test/EFCore.Cosmos.FunctionalTests/AddHocFullTextSearchCosmosTest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
141141
#region SettingDefaultFullTextSearchLanguage
142142

143143
[ConditionalFact]
144+
[CosmosCondition(CosmosCondition.IsNotLinuxEmulator)]
144145
public async Task Set_unsupported_full_text_search_default_language()
145146
{
146147
var exception = (await Assert.ThrowsAsync<CosmosException>(() => InitializeNonSharedTest<ContextSettingDefaultFullTextSearchLanguage>()));
@@ -224,6 +225,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
224225
#region DefaultFullTextSearchLanguageNoMismatchWhenNotSpecified
225226

226227
[ConditionalFact]
228+
[CosmosCondition(CosmosCondition.IsNotLinuxEmulator)]
227229
public async Task
228230
Explicitly_setting_default_full_text_language_doesnt_clash_with_not_setting_it_on_other_entity_for_the_same_container()
229231
{
@@ -298,6 +300,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
298300
#region DefaultFullTextSearchLanguageUsedWhenPropertyDoesntSpecifyOneExplicitly
299301

300302
[ConditionalFact]
303+
[CosmosCondition(CosmosCondition.IsNotLinuxEmulator)]
301304
public async Task Default_full_text_language_is_used_for_full_text_properties_if_they_dont_specify_language_themselves()
302305
{
303306
var exception = (await Assert.ThrowsAsync<CosmosException>(()
@@ -337,6 +340,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
337340
#region ExplicitFullTextLanguageOverridesTheDefault
338341

339342
[ConditionalFact]
343+
[CosmosCondition(CosmosCondition.IsNotLinuxEmulator)]
340344
public async Task Explicitly_setting_full_text_language_overrides_default()
341345
{
342346
var exception =

0 commit comments

Comments
 (0)