diff --git a/AGENTS.md b/AGENTS.md index 047847f..7c5193d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,6 +79,52 @@ Tests are split into two projects. When creating or moving tests, follow these r ### Key constraint The Integration project does **not** have `InternalsVisibleTo` access. If a test needs internal APIs, it belongs in Unit. +## Cross-Platform Testing (Dev Environment) + +When investigating platform-specific test failures, use `scripts/dev-env.ps1` to reproduce issues locally on any platform + backend combination. + +### Quick Reference + +```powershell +# Check current state +./scripts/dev-env.ps1 status + +# Reproduce a Linux CI failure +./scripts/dev-env.ps1 test -Platform linux -Target inmemory -Project integration -Filter "FullyQualifiedName~FailingTest" + +# Reproduce a Linux + emulator CI failure +./scripts/dev-env.ps1 test -Platform linux -Target emulator-linux -Project integration -Filter "FullyQualifiedName~FailingTest" + +# Run against Linux emulator from Windows host (tests emulator behavioral differences) +./scripts/dev-env.ps1 test -Platform windows -Target emulator-linux -Project integration + +# Run arbitrary commands in the Linux container +./scripts/dev-env.ps1 exec -Cmd "dotnet build -c Release" + +# Tear down when done +./scripts/dev-env.ps1 stop +``` + +### Investigation Workflow + +When a test fails in CI on Linux but passes locally on Windows: + +1. First try reproducing on Linux with in-memory: `./scripts/dev-env.ps1 test -Platform linux -Target inmemory -Filter "FullyQualifiedName~TestName"` +2. If it fails → it's a Linux .NET runtime difference (gateway fallback, string handling, etc.) +3. If it passes → try with the emulator: `./scripts/dev-env.ps1 test -Platform linux -Target emulator-linux -Filter "FullyQualifiedName~TestName"` +4. If that fails → it's a Linux + emulator interaction issue +5. Edit source files normally (they're mounted into the container), then re-run + +### Supported Scenarios + +| Platform | Target | Docker Required | +|----------|--------|----------------| +| windows | inmemory | No | +| windows | emulator-windows | No (needs Windows emulator installed) | +| windows | emulator-linux | Yes (emulator container) | +| linux | inmemory | Yes (dev container) | +| linux | emulator-linux | Yes (dev + emulator containers) | + ## Documentation After any changes are made that might effect the public API or functionality, documentation must be updated to reflect those changes. The documentation should be clear and comprehensive, covering all new features, changes to existing features, and any deprecations or removals. This includes updating README file (if relevant), but mainly the wiki which can be found in a sister folder to the main repository - ../CosmosDB.InMemoryEmulator.wiki. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ea39fd..8d56be9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,7 @@ Thank you for your interest in contributing! - .NET 8.0 SDK (or later) - PowerShell (for test scripts) +- Docker (for cross-platform testing — Docker Desktop or Rancher Desktop) ## Building @@ -24,16 +25,43 @@ dotnet build CosmosDB.InMemoryEmulator.sln ## Running Tests ```powershell -# Unit tests only +# Unit tests only (Windows, in-memory) dotnet test tests/CosmosDB.InMemoryEmulator.Tests.Unit -# Integration tests (in-memory) +# Integration tests (Windows, in-memory) dotnet test tests/CosmosDB.InMemoryEmulator.Tests.Integration +``` + +## Cross-Platform Testing + +This project runs CI on both Windows and Linux. To reproduce platform-specific issues locally, use the dev environment script: + +```powershell +# See what's running and available scenarios +./scripts/dev-env.ps1 status + +# Run tests on Linux (catches gateway fallback / runtime differences) +./scripts/dev-env.ps1 test -Platform linux -Target inmemory -Project integration -# Full parity validation (requires Docker) -./scripts/validate-parity.ps1 +# Run tests on Linux against the Linux Cosmos emulator +./scripts/dev-env.ps1 test -Platform linux -Target emulator-linux -Project integration + +# Run tests on Windows against the Linux Cosmos emulator (emulator behavior differences) +./scripts/dev-env.ps1 test -Platform windows -Target emulator-linux -Project integration + +# Run a specific failing test on Linux for investigation +./scripts/dev-env.ps1 test -Platform linux -Target inmemory -Filter "FullyQualifiedName~MyFailingTest" + +# Start the Linux dev container for interactive exploration +./scripts/dev-env.ps1 start +./scripts/dev-env.ps1 exec -Cmd "dotnet build -c Release" + +# Tear down all containers +./scripts/dev-env.ps1 stop ``` +See [scripts/dev-env.ps1](scripts/dev-env.ps1) for full documentation of all commands and parameters. + ## Guidelines - **TDD**: Write a failing test first, then implement the minimum code to make it pass, then refactor. diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..ebf0824 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,83 @@ +# Cross-Platform Development Environment +# +# Defines services for local cross-platform testing: +# dev — Linux .NET SDK container for running tests on Linux +# emulator — Linux Cosmos DB emulator for integration tests +# +# Usage: managed via scripts/dev-env.ps1 (preferred) or directly: +# docker compose -f docker-compose.dev.yml up -d dev +# docker compose -f docker-compose.dev.yml up -d emulator +# docker compose -f docker-compose.dev.yml up -d # both +# +# Networking: The dev container shares the emulator's network namespace +# (network_mode: "service:emulator") so the tests can reach the emulator +# at localhost:8081 — exactly like CI. When only the dev container is running +# (no emulator), it uses bridge networking by default via the dev-standalone profile. + +services: + dev: + image: mcr.microsoft.com/dotnet/sdk:8.0 + container_name: cosmosdb-dev-linux + profiles: ["with-emulator"] + # Share the emulator's network namespace — tests see emulator at localhost:8081 + # just like in CI (GitHub Actions service containers share localhost). + network_mode: "service:emulator" + depends_on: + emulator: + condition: service_healthy + volumes: + - .:/src + - ${NUGET_PACKAGES:-${USERPROFILE:-.}/.nuget/packages}:/root/.nuget/fallback-packages:ro + - nuget-cache:/root/.nuget/packages + working_dir: /src + command: > + bash -c " + if ! command -v pwsh &>/dev/null; then + apt-get update -qq && apt-get install -y -qq powershell >/dev/null 2>&1; + fi; + sleep infinity + " + environment: + - DOTNET_CLI_TELEMETRY_OPTOUT=1 + - DOTNET_NOLOGO=1 + + # Standalone dev container (no emulator dependency) — for inmemory-only Linux testing + dev-standalone: + image: mcr.microsoft.com/dotnet/sdk:8.0 + container_name: cosmosdb-dev-linux + profiles: ["standalone"] + volumes: + - .:/src + - ${NUGET_PACKAGES:-${USERPROFILE:-.}/.nuget/packages}:/root/.nuget/fallback-packages:ro + - nuget-cache:/root/.nuget/packages + working_dir: /src + command: > + bash -c " + if ! command -v pwsh &>/dev/null; then + apt-get update -qq && apt-get install -y -qq powershell >/dev/null 2>&1; + fi; + sleep infinity + " + environment: + - DOTNET_CLI_TELEMETRY_OPTOUT=1 + - DOTNET_NOLOGO=1 + + emulator: + image: mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator + container_name: cosmosdb-emulator-linux + profiles: ["with-emulator", "emulator-only"] + ports: + - "8081:8081" + environment: + AZURE_COSMOS_EMULATOR_PARTITION_COUNT: 30 + AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE: "false" + AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE: "127.0.0.1" + healthcheck: + test: ["CMD", "curl", "-fks", "https://localhost:8081/_explorer/emulator.pem"] + interval: 10s + timeout: 5s + retries: 60 + start_period: 30s + +volumes: + nuget-cache: diff --git a/scripts/dev-env.ps1 b/scripts/dev-env.ps1 new file mode 100644 index 0000000..af24f98 --- /dev/null +++ b/scripts/dev-env.ps1 @@ -0,0 +1,390 @@ +<# +.SYNOPSIS + Manages the cross-platform development environment for local testing. + +.DESCRIPTION + Provides a unified interface for running tests on different platform + backend + combinations. Manages Docker containers for Linux test execution and the Linux + Cosmos DB emulator. + + Supported scenarios: + Platform=windows, Target=inmemory — runs tests directly on host + Platform=windows, Target=emulator-windows — runs tests on host against Windows emulator + Platform=windows, Target=emulator-linux — starts Linux emulator, runs tests on host + Platform=linux, Target=inmemory — runs tests inside Linux container + Platform=linux, Target=emulator-linux — runs tests in Linux container against emulator + +.PARAMETER Command + The action to perform: start, stop, status, exec, test. + +.PARAMETER Platform + Test runner platform: windows or linux. Required for 'test' command. + +.PARAMETER Target + Cosmos backend target: inmemory, emulator-linux, or emulator-windows. Required for 'test'. + +.PARAMETER Project + Test project: unit, integration, or both. Default: both. + +.PARAMETER Framework + Target framework. Default: net8.0. + +.PARAMETER Filter + dotnet test filter expression. + +.PARAMETER WithEmulator + For 'start' command: also start the Linux Cosmos emulator service. + +.PARAMETER EmulatorOnly + For 'start' command: start only the emulator (not the dev container). + +.PARAMETER Cmd + For 'exec' command: the command to run inside the Linux dev container. + +.EXAMPLE + ./scripts/dev-env.ps1 start + ./scripts/dev-env.ps1 start -WithEmulator + ./scripts/dev-env.ps1 test -Platform linux -Target inmemory -Project integration + ./scripts/dev-env.ps1 test -Platform linux -Target emulator-linux -Filter "FullyQualifiedName~CrudTests" + ./scripts/dev-env.ps1 test -Platform windows -Target emulator-linux -Project integration + ./scripts/dev-env.ps1 exec -Cmd "dotnet build -c Release" + ./scripts/dev-env.ps1 status + ./scripts/dev-env.ps1 stop +#> +param( + [Parameter(Position = 0)] + [ValidateSet('start', 'stop', 'status', 'exec', 'test')] + [string]$Command = 'status', + + [ValidateSet('windows', 'linux')] + [string]$Platform, + + [ValidateSet('inmemory', 'emulator-linux', 'emulator-windows')] + [string]$Target, + + [ValidateSet('unit', 'integration', 'both')] + [string]$Project = 'both', + + [string]$Framework = 'net8.0', + [string]$Filter, + + [switch]$WithEmulator, + [switch]$EmulatorOnly, + + [string]$Cmd +) + +$ErrorActionPreference = 'Stop' +$ComposeFile = Join-Path $PSScriptRoot '..' 'docker-compose.dev.yml' + +# ─── Helper Functions ───────────────────────────────────────────────────────── + +function Test-DockerAvailable { + $version = docker version --format '{{.Server.Version}}' 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Error "Docker is not running. Start Docker Desktop (or Rancher Desktop) and try again." + exit 1 + } + return $version +} + +function Test-ContainerRunning([string]$Name) { + $state = docker inspect --format '{{.State.Running}}' $Name 2>&1 + return ($LASTEXITCODE -eq 0 -and $state -eq 'true') +} + +function Test-EmulatorHealthy { + $health = docker inspect --format '{{.State.Health.Status}}' 'cosmosdb-emulator-linux' 2>&1 + return ($LASTEXITCODE -eq 0 -and $health -eq 'healthy') +} + +function Start-DockerService([string]$ServiceName, [string]$Profile = '') { + Write-Host "Starting service '$ServiceName'..." -ForegroundColor Cyan + if ($Profile) { + docker compose -f $ComposeFile --profile $Profile up -d $ServiceName + } else { + docker compose -f $ComposeFile up -d $ServiceName + } + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to start service '$ServiceName'" + exit 1 + } +} + +function Wait-ForEmulator { + param([int]$TimeoutSeconds = 300) + + Write-Host "Waiting for Cosmos DB emulator to become healthy..." -ForegroundColor Cyan + $elapsed = 0 + while ($elapsed -lt $TimeoutSeconds) { + if (Test-EmulatorHealthy) { + Write-Host "Emulator healthy after ${elapsed}s" -ForegroundColor Green + return + } + Start-Sleep -Seconds 5 + $elapsed += 5 + if ($elapsed % 30 -eq 0) { + Write-Host " Still waiting... (${elapsed}s)" -ForegroundColor DarkGray + } + } + Write-Error "Emulator did not become healthy within ${TimeoutSeconds}s" + exit 1 +} + +function Ensure-DevContainer { + param([switch]$WithEmulatorNetwork) + + if (-not (Test-ContainerRunning 'cosmosdb-dev-linux')) { + if ($WithEmulatorNetwork) { + # Start with the with-emulator profile — dev shares emulator's network namespace. + # The emulator must be healthy first (depends_on condition ensures this). + Start-DockerService 'dev' 'with-emulator' + } else { + # Standalone — no emulator dependency, uses default bridge networking. + Start-DockerService 'dev-standalone' 'standalone' + } + # Wait for PowerShell to be installed (container command installs it on first start) + Write-Host "Waiting for container to be ready (installing PowerShell if needed)..." -ForegroundColor Cyan + $elapsed = 0 + while ($elapsed -lt 120) { + $result = docker exec cosmosdb-dev-linux bash -c "command -v pwsh" 2>&1 + if ($LASTEXITCODE -eq 0) { break } + Start-Sleep -Seconds 5 + $elapsed += 5 + } + if ($elapsed -ge 120) { + Write-Error "Container did not become ready within 120s" + exit 1 + } + Write-Host "Container ready." -ForegroundColor Green + + # Configure NuGet to use the mounted host package cache as a fallback source, + # avoiding network issues (e.g., corporate proxy TLS interception). + Write-Host "Restoring NuGet packages in container..." -ForegroundColor Cyan + docker exec cosmosdb-dev-linux bash -c "dotnet nuget add source /root/.nuget/fallback-packages --name host-cache 2>/dev/null; dotnet restore -p:TargetFrameworks=net8.0 --source /root/.nuget/fallback-packages --verbosity quiet 2>&1 || true" + # Reset exit code — restore warnings are non-fatal + $global:LASTEXITCODE = 0 + Write-Host "Restore complete." -ForegroundColor Green + } +} + +function Ensure-Emulator { + if (-not (Test-ContainerRunning 'cosmosdb-emulator-linux')) { + Start-DockerService 'emulator' 'emulator-only' + } + if (-not (Test-EmulatorHealthy)) { + Wait-ForEmulator + } +} + +function Invoke-InContainer { + param( + [string]$Command, + [hashtable]$EnvVars = @{} + ) + + $envArgs = @() + foreach ($kv in $EnvVars.GetEnumerator()) { + $envArgs += '-e' + $envArgs += "$($kv.Key)=$($kv.Value)" + } + + docker exec @envArgs cosmosdb-dev-linux bash -c $Command + return $LASTEXITCODE +} + +# ─── Commands ───────────────────────────────────────────────────────────────── + +function Invoke-Start { + $dockerVersion = Test-DockerAvailable + Write-Host "Docker $dockerVersion detected" -ForegroundColor Cyan + + if ($EmulatorOnly) { + Ensure-Emulator + Write-Host "`nEmulator running at https://localhost:8081" -ForegroundColor Green + } elseif ($WithEmulator) { + Ensure-Emulator + Ensure-DevContainer -WithEmulatorNetwork + Write-Host "`nDev container + emulator running." -ForegroundColor Green + Write-Host " Dev container: cosmosdb-dev-linux (shares emulator network — localhost:8081)" -ForegroundColor DarkGray + Write-Host " Emulator: https://localhost:8081" -ForegroundColor DarkGray + } else { + Ensure-DevContainer + Write-Host "`nDev container running: cosmosdb-dev-linux" -ForegroundColor Green + } + exit 0 +} + +function Invoke-Stop { + Write-Host "Stopping dev environment..." -ForegroundColor Cyan + # Tear down all profiles + docker compose -f $ComposeFile --profile with-emulator --profile standalone --profile emulator-only down + Write-Host "Done." -ForegroundColor Green +} + +function Invoke-Status { + $devRunning = Test-ContainerRunning 'cosmosdb-dev-linux' + $emulatorRunning = Test-ContainerRunning 'cosmosdb-emulator-linux' + $emulatorHealthy = if ($emulatorRunning) { Test-EmulatorHealthy } else { $false } + + # Reset LASTEXITCODE — docker inspect failures above are expected when containers don't exist + $global:LASTEXITCODE = 0 + + Write-Host "Cross-Platform Dev Environment Status" -ForegroundColor Cyan + Write-Host "─────────────────────────────────────" -ForegroundColor DarkGray + $devStatus = if ($devRunning) { "Running" } else { "Stopped" } + $devColor = if ($devRunning) { "Green" } else { "DarkGray" } + Write-Host " Dev container (Linux): $devStatus" -ForegroundColor $devColor + + $emStatus = if ($emulatorHealthy) { "Healthy" } elseif ($emulatorRunning) { "Starting..." } else { "Stopped" } + $emColor = if ($emulatorHealthy) { "Green" } elseif ($emulatorRunning) { "Yellow" } else { "DarkGray" } + Write-Host " Emulator (Linux): $emStatus" -ForegroundColor $emColor + + Write-Host "" + Write-Host "Available test scenarios:" -ForegroundColor Cyan + Write-Host " ./scripts/dev-env.ps1 test -Platform windows -Target inmemory" -ForegroundColor DarkGray + Write-Host " ./scripts/dev-env.ps1 test -Platform windows -Target emulator-windows" -ForegroundColor DarkGray + if ($emulatorRunning -or $emulatorHealthy) { + Write-Host " ./scripts/dev-env.ps1 test -Platform windows -Target emulator-linux" -ForegroundColor White + } else { + Write-Host " ./scripts/dev-env.ps1 test -Platform windows -Target emulator-linux (needs: start -EmulatorOnly)" -ForegroundColor DarkGray + } + if ($devRunning) { + Write-Host " ./scripts/dev-env.ps1 test -Platform linux -Target inmemory" -ForegroundColor White + } else { + Write-Host " ./scripts/dev-env.ps1 test -Platform linux -Target inmemory (needs: start)" -ForegroundColor DarkGray + } + if ($devRunning -and $emulatorHealthy) { + Write-Host " ./scripts/dev-env.ps1 test -Platform linux -Target emulator-linux" -ForegroundColor White + } else { + Write-Host " ./scripts/dev-env.ps1 test -Platform linux -Target emulator-linux (needs: start -WithEmulator)" -ForegroundColor DarkGray + } +} + +function Invoke-Exec { + if (-not $Cmd) { + Write-Error "The -Cmd parameter is required for the 'exec' command. Example: ./scripts/dev-env.ps1 exec -Cmd 'dotnet build -c Release'" + exit 1 + } + + Test-DockerAvailable | Out-Null + Ensure-DevContainer + + Write-Host "Executing in Linux container:" -ForegroundColor Cyan + Write-Host " $Cmd" -ForegroundColor DarkGray + Write-Host "" + + docker exec cosmosdb-dev-linux bash -c $Cmd + exit $LASTEXITCODE +} + +function Invoke-Test { + if (-not $Platform) { + Write-Error "The -Platform parameter is required for the 'test' command. Use: -Platform windows or -Platform linux" + exit 1 + } + if (-not $Target) { + Write-Error "The -Target parameter is required for the 'test' command. Use: -Target inmemory, emulator-linux, or emulator-windows" + exit 1 + } + + # Validate combination + if ($Platform -eq 'linux' -and $Target -eq 'emulator-windows') { + Write-Error "Invalid combination: Linux platform cannot use the Windows emulator." + exit 1 + } + + $runTestsScript = './scripts/run-tests.ps1' + + # Build the run-tests.ps1 arguments + $testArgs = "-Target $Target -Project $Project -Framework $Framework" + if ($Filter) { + $testArgs += " -Filter '$Filter'" + } + + switch ("$Platform|$Target") { + # ── Scenario 1: Windows + in-memory ── + 'windows|inmemory' { + Write-Host "Scenario 1: Windows + in-memory" -ForegroundColor Cyan + $scriptPath = Join-Path $PSScriptRoot 'run-tests.ps1' + $invokeArgs = @{ Target = $Target; Project = $Project; Framework = $Framework } + if ($Filter) { $invokeArgs.Filter = $Filter } + & $scriptPath @invokeArgs + exit $LASTEXITCODE + } + + # ── Scenario 2: Windows + Windows emulator ── + 'windows|emulator-windows' { + Write-Host "Scenario 2: Windows + Windows emulator" -ForegroundColor Cyan + $scriptPath = Join-Path $PSScriptRoot 'run-tests.ps1' + $invokeArgs = @{ Target = $Target; Project = $Project; Framework = $Framework } + if ($Filter) { $invokeArgs.Filter = $Filter } + & $scriptPath @invokeArgs + exit $LASTEXITCODE + } + + # ── Scenario 3: Windows + Linux emulator ── + 'windows|emulator-linux' { + Write-Host "Scenario 3: Windows host + Linux emulator" -ForegroundColor Cyan + Test-DockerAvailable | Out-Null + Ensure-Emulator + Write-Host "" + + $scriptPath = Join-Path $PSScriptRoot 'run-tests.ps1' + $invokeArgs = @{ + Target = 'emulator-linux' + Project = $Project + Framework = $Framework + EmulatorEndpoint = 'https://localhost:8081' + } + if ($Filter) { $invokeArgs.Filter = $Filter } + & $scriptPath @invokeArgs + exit $LASTEXITCODE + } + + # ── Scenario 4: Linux + in-memory ── + 'linux|inmemory' { + Write-Host "Scenario 4: Linux container + in-memory" -ForegroundColor Cyan + Test-DockerAvailable | Out-Null + Ensure-DevContainer # standalone profile — no emulator dependency + Write-Host "" + + $envVars = @{ + COSMOS_TEST_TARGET = 'inmemory' + } + $cmd = "pwsh -NoProfile -Command `"& $runTestsScript -Target inmemory -Project $Project -Framework $Framework$(if ($Filter) { " -Filter '$Filter'" })`"" + $exitCode = Invoke-InContainer -Command $cmd -EnvVars $envVars + exit $exitCode + } + + # ── Scenario 5: Linux + Linux emulator ── + 'linux|emulator-linux' { + Write-Host "Scenario 5: Linux container + Linux emulator" -ForegroundColor Cyan + Test-DockerAvailable | Out-Null + Ensure-Emulator + Ensure-DevContainer -WithEmulatorNetwork # shares emulator's network namespace + Write-Host "" + + # The dev container shares the emulator's network namespace (network_mode: service:emulator), + # so the emulator is reachable at localhost:8081 — same as CI. + $emulatorEndpoint = 'https://localhost:8081' + $envVars = @{ + COSMOS_TEST_TARGET = 'emulator-linux' + COSMOS_EMULATOR_ENDPOINT = $emulatorEndpoint + } + $cmd = "pwsh -NoProfile -Command `"& $runTestsScript -Target emulator-linux -Project $Project -Framework $Framework -EmulatorEndpoint '$emulatorEndpoint'$(if ($Filter) { " -Filter '$Filter'" })`"" + $exitCode = Invoke-InContainer -Command $cmd -EnvVars $envVars + exit $exitCode + } + } +} + +# ─── Dispatch ───────────────────────────────────────────────────────────────── + +switch ($Command) { + 'start' { Invoke-Start } + 'stop' { Invoke-Stop } + 'status' { Invoke-Status } + 'exec' { Invoke-Exec } + 'test' { Invoke-Test } +} diff --git a/test-linux.ps1 b/test-linux.ps1 deleted file mode 100644 index 52147db..0000000 --- a/test-linux.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -<# -.SYNOPSIS - Runs the integration test suite inside a Linux Docker container to catch - platform-specific failures (e.g. Cosmos SDK gateway query plan path) before - pushing to CI. - -.DESCRIPTION - The Cosmos SDK uses a native ServiceInterop DLL on Windows for local query plan - computation. On Linux (including CI), it falls back to a gateway HTTP endpoint, - which exercises different code paths in FakeCosmosHandler. This script reproduces - that Linux behavior locally via Docker. - - By default this script runs only the integration test classes that the - Emulator Parity workflow validates (Crud, Ttl, Batch, QueryAdvanced) — the - other integration test classes (PartitionKey, Linq, CrudHardening, Bulk) - overload the Linux Cosmos emulator under per-test container churn and are - excluded from CI parity. Pass -Filter to override. - - NuGet packages are restored offline from the host's package cache to avoid - network dependencies. - -.PARAMETER Filter - xUnit test filter expression. Defaults to the parity-clean integration - classes; pass an explicit value to run a different set - (e.g. "FullyQualifiedName~OrderBy"). - -.EXAMPLE - .\test-linux.ps1 - # Runs the parity-clean integration test classes - -.EXAMPLE - .\test-linux.ps1 -Filter "FullyQualifiedName~FakeCosmosHandlerLinqTests" - # Runs only the Linq integration tests -#> -param( - [string]$Filter = "FullyQualifiedName~FakeCosmosHandlerCrudTests|FullyQualifiedName~FakeCosmosHandlerTtlTests|FullyQualifiedName~FakeCosmosHandlerBatchTests|FullyQualifiedName~FakeCosmosHandlerQueryAdvancedTests" -) - -$ErrorActionPreference = 'Stop' - -# Verify Docker is available -$dockerVersion = docker version --format '{{.Server.Version}}' 2>&1 -if ($LASTEXITCODE -ne 0) { - Write-Error "Docker is not running. Start Rancher Desktop (or Docker Desktop) and try again." - return -} -Write-Host "Docker $dockerVersion detected" -ForegroundColor Cyan - -$repoRoot = $PSScriptRoot -$nugetCache = Join-Path $env:USERPROFILE '.nuget\packages' - -if (-not (Test-Path $nugetCache)) { - Write-Error "NuGet package cache not found at $nugetCache" - return -} - -$testProject = 'tests/CosmosDB.InMemoryEmulator.Tests.Integration' -$filterArg = if ($Filter) { "--filter '$Filter'" } else { "" } - -$testCmd = @( - "dotnet nuget add source /root/.nuget/packages --name local-cache 2>/dev/null;" - "find /src -name 'project.assets.json' -delete 2>/dev/null;" - "dotnet restore $testProject -p:TargetFrameworks=net8.0 --source /root/.nuget/packages -p:TreatWarningsAsErrors=false 2>/dev/null;" - "dotnet test $testProject --nologo -c Release -f net8.0 --no-restore -p:TreatWarningsAsErrors=false $filterArg" -) -join ' ' - -Write-Host "Running integration tests on Linux (mcr.microsoft.com/dotnet/sdk:8.0)..." -ForegroundColor Cyan -Write-Host " Filter: $Filter" -ForegroundColor DarkGray - -docker run --rm ` - -v "${repoRoot}:/src" ` - -v "${nugetCache}:/root/.nuget/packages" ` - -w /src ` - mcr.microsoft.com/dotnet/sdk:8.0 ` - bash -c $testCmd - -if ($LASTEXITCODE -ne 0) { - Write-Host "`nLinux tests failed." -ForegroundColor Red -} else { - Write-Host "`nLinux tests passed." -ForegroundColor Green -} - -exit $LASTEXITCODE