Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9859f36
Experiment with sharded SQL E2E PR tests
mikaelweave May 25, 2026
c2ad20a
Refine SQL E2E shard isolation experiment
mikaelweave May 25, 2026
0598388
Enforce serialized E2E test execution
mikaelweave May 25, 2026
b4338aa
Run BulkDelete after SQL E2E shards
mikaelweave May 25, 2026
420d5b2
Shard SQL integration test PR jobs
mikaelweave May 26, 2026
12bad20
Add SMART search integration timing diagnostics
mikaelweave May 27, 2026
8176b3f
Reuse SMART search integration fixture
mikaelweave May 27, 2026
c60fcbe
Skip long import E2E stress test in PR
mikaelweave May 27, 2026
44f786e
Run SQL E2E Reindex after parallel shards
mikaelweave May 27, 2026
cd5221c
Run isolation-sensitive SQL E2E tests in terminal lane
mikaelweave May 28, 2026
6cdb6c2
Skip unsupported SMART integration shards
mikaelweave May 28, 2026
a75b128
Run AAD setup parallel to app deployment
mikaelweave May 28, 2026
8f05db4
Run integration tests by class in single jobs
mikaelweave May 28, 2026
b8d9a16
Inject integration parallel runner after extraction
mikaelweave May 28, 2026
d987e58
Enable PR E2E class parallelism experiment
mikaelweave May 28, 2026
cf7e33a
Fix E2E class duration artifact YAML
mikaelweave May 28, 2026
9d5f675
Restore category-based SQL E2E isolation
mikaelweave May 28, 2026
9b08033
Restore E2E isolation lane for parallel runs
mikaelweave May 28, 2026
d1519d9
Run isolation E2E jobs after shard failures
mikaelweave May 28, 2026
3e2f2c1
Shard Cosmos PR tests by category
mikaelweave May 28, 2026
fd9423b
Build integration tests once and run pre-built DLLs in CI
mikaelweave May 28, 2026
2f32875
Fix Cosmos integration CosmosSpecific shard running tests twice
mikaelweave May 28, 2026
cf00ab0
Decouple AnalyzeSecurity stage from BuildUnitTests
mikaelweave May 29, 2026
af87ac3
Add integration duration-by-class report to SQL sharded job
mikaelweave Jun 1, 2026
5e451fb
Merge main and remove nested SmartSearchSharedFixture
mikaelweave Jun 4, 2026
b81140a
Fix build errors in SmartSearch test files
mikaelweave Jun 4, 2026
8449961
remove unneeded changes
mikaelweave Jun 6, 2026
44aecad
Fix broken sharded SQL integration job structure
mikaelweave Jun 8, 2026
e24159b
Enable retry-aware test result publishing for sharded E2E jobs
mikaelweave Jun 8, 2026
3812827
Require explicit E2E test filters and remove fallback composition
mikaelweave Jun 8, 2026
b187ed8
Inline full E2E shard filters and remove shard composition
mikaelweave Jun 8, 2026
752eb2f
Fix template expression not allowed in object parameter defaults
mikaelweave Jun 8, 2026
5a0bbd7
Fix SQL CustomSearch E2E shard filter
mikaelweave Jun 8, 2026
1981fd2
Fix: add TokenOverflowTests.cs to shared E2E projitems
mikaelweave Jun 8, 2026
2573514
Fix TokenOverflowTests reindex status call
mikaelweave Jun 9, 2026
f4feb1d
Retarget SQL CustomSearch shard to stable tests
mikaelweave Jun 9, 2026
6dd9558
Refactor: extract shared integration test steps into integration-test…
mikaelweave Jun 9, 2026
fc580a6
Fix PR RG cleanup quoting in PowerShell
mikaelweave Jun 9, 2026
7711d9a
Fix PR RG creation macro evaluation in PowerShell
mikaelweave Jun 9, 2026
598e553
Simplify E2E sharding by keeping xUnit serial per shard
mikaelweave Jun 9, 2026
3df3390
Run BulkDelete E2E tests as regular shards
mikaelweave Jun 9, 2026
8089ef2
Address PR review feedback for test sharding
mikaelweave Jun 9, 2026
b633a48
Fail integration publish task on failed tests
mikaelweave Jun 10, 2026
c54a5a7
Use project-based integration test execution
mikaelweave Jun 12, 2026
e7e52cb
Add R4 SQL isolation endpoint so RequiresIsolation E2E runs in parallel
mikaelweave Jun 13, 2026
61f9836
Fix R4 SQL iso Key Vault name exceeding 24-char limit
mikaelweave Jun 13, 2026
7515f81
Replicate SQL isolation endpoint to Stu3 and R5
mikaelweave Jun 14, 2026
261dea8
Add dedicated isolation Cosmos endpoints for Stu3/R4 RequiresIsolatio…
mikaelweave Jun 14, 2026
e37c252
Merge remote-tracking branch 'origin/main' into mikaelweave/e2e-paral…
mikaelweave Jun 29, 2026
f444383
Stabilize flaky SqlServerCreateStatsTests gender no-fanout assertion
mikaelweave Jun 30, 2026
e1175d5
Make stats no-fanout test deterministic under co-location
mikaelweave Jun 30, 2026
22f19aa
Merge remote-tracking branch 'origin/main' into mikaelweave/e2e-paral…
mikaelweave Jul 3, 2026
85a174c
Scope isolation group to BulkDelete + Reindex (swap BulkUpdate out)
mikaelweave Jul 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions build/build-variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,53 @@ variables:
azureContainerRegistryName: 'healthplatformregistry'
azureContainerRegistry: '$(azureContainerRegistryName).azurecr.io'
DeploymentEnvironmentNameSql: '$(DeploymentEnvironmentName)-sql'
# Isolation endpoint for Stu3 SQL (mirrors the R4 SQL isolation endpoint): dedicated container app +
# dedicated database on the SAME app SQL server so RequiresIsolation E2E tests run concurrently with shards.
DeploymentEnvironmentNameSqlIso: '$(DeploymentEnvironmentNameSql)-iso'
DeploymentEnvironmentNameR4: '$(DeploymentEnvironmentName)-r4'
DeploymentEnvironmentNameR4Sql: '$(DeploymentEnvironmentNameR4)-sql'
# Isolation endpoint for R4 SQL: dedicated container app + dedicated database on the SAME app SQL
# server, so RequiresIsolation E2E tests (reindex, bulk delete/update, custom search params) run
# concurrently with the parallel shards instead of in a serialized terminal lane.
DeploymentEnvironmentNameR4SqlIso: '$(DeploymentEnvironmentNameR4Sql)-iso'
DeploymentEnvironmentNameR4B: '$(DeploymentEnvironmentName)-r4b'
DeploymentEnvironmentNameR4BSql: '$(DeploymentEnvironmentNameR4B)-sql'
DeploymentEnvironmentNameR5: '$(DeploymentEnvironmentName)-r5'
DeploymentEnvironmentNameR5Sql: '$(DeploymentEnvironmentNameR5)-sql'
# Isolation endpoint for R5 SQL (mirrors R4): dedicated container app + dedicated database on the shared server.
DeploymentEnvironmentNameR5SqlIso: '$(DeploymentEnvironmentNameR5Sql)-iso'
# Cosmos isolation endpoints (mirror the SQL isolation endpoints): each gets its OWN dedicated Cosmos
# account (the deploy script names the Cosmos account after the container app), so RequiresIsolation
# Cosmos E2E tests run concurrently with the parallel shards instead of in a serialized terminal lane.
# Use a compact '-ciso' (cosmos-iso) suffix; it is shorter than the proven-safe SQL '-sql-iso' suffix,
# so it stays within the ACA container-app (32 char) and Cosmos account (44 char) name limits.
DeploymentEnvironmentNameCosmosIso: '$(DeploymentEnvironmentName)-ciso'
DeploymentEnvironmentNameR4CosmosIso: '$(DeploymentEnvironmentNameR4)-ciso'
AcaEnvironmentName: '$(DeploymentEnvironmentName)-acae'
# Key Vault names (shorter due to 24 character limit)
KeyVaultNameSql: '$(KeyVaultBaseName)-sql'
# Stu3 SQL isolation vault. Must be <= 24 chars. Use compact 'sqi' (sql-iso) token: '$(KeyVaultBaseName)-sqi'
# (base + 4 chars), well under the limit and distinct from the main Stu3 SQL vault.
KeyVaultNameSqlIso: '$(KeyVaultBaseName)-sqi'
KeyVaultNameR4: '$(KeyVaultBaseName)-r4'
KeyVaultNameR4Sql: '$(KeyVaultNameR4)-sql'
# Key Vault names must be <= 24 chars. KeyVaultNameR4Sql is already 'f<buildNumber>-r4-sql' (21 chars at the
# proven-safe max), so a '-iso' suffix would overflow. Use a compact 'sqi' (sql-iso) token to stay the same
# length as the main R4 SQL vault while remaining distinct (each ACA app provisions its own vault).
KeyVaultNameR4SqlIso: '$(KeyVaultNameR4)-sqi'
KeyVaultNameR4B: '$(KeyVaultBaseName)-r4b'
KeyVaultNameR4BSql: '$(KeyVaultNameR4B)-sql'
KeyVaultNameR5: '$(KeyVaultBaseName)-r5'
KeyVaultNameR5Sql: '$(KeyVaultNameR5)-sql'
# R5 SQL isolation vault. '$(KeyVaultNameR5)-sqi' = '<base>-r5-sqi' (base + 7 chars), same length as the
# proven-safe R4 SQL iso vault and under the 24-char limit.
KeyVaultNameR5SqlIso: '$(KeyVaultNameR5)-sqi'
# Cosmos isolation vaults. Each iso ACA app provisions its own vault. Use a compact 'csi' (cosmos-iso)
# token to stay <= 24 chars: '$(KeyVaultBaseName)-csi' = '<base>-csi' (base + 4 = 20 chars) for Stu3,
# and '$(KeyVaultNameR4)-csi' = '<base>-r4-csi' (base + 7 = 23 chars) for R4 — same lengths as the
# proven-safe SQL iso vaults.
KeyVaultNameCosmosIso: '$(KeyVaultBaseName)-csi'
KeyVaultNameR4CosmosIso: '$(KeyVaultNameR4)-csi'
TestEnvironmentUrl: 'https://$(DeploymentEnvironmentName).azurewebsites.net'
# These variables are not used in the deployment scripts, but are used in the E2E tests files.
TestEnvironmentUrl_Sql: 'https://$(DeploymentEnvironmentName)-sql.azurewebsites.net'
Expand Down
30 changes: 27 additions & 3 deletions build/jobs/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ parameters:
type: string
- name: appServiceType
type: string
- name: categoryFilter
- name: testFilter
type: string
default: 'Category!=ExportLongRunning'
default: ''
- name: testRunTitleSuffix
type: string
default: ''
Expand Down Expand Up @@ -37,7 +37,11 @@ steps:
throw "Could not find $testName.dll under $testRoot"
}

$filter = "FullyQualifiedName~${{ parameters.appServiceType }}&${{ parameters.categoryFilter }}"
$filter = "${{ parameters.testFilter }}"
if ([string]::IsNullOrWhiteSpace($filter)) {
throw "testFilter is required and must not be empty."
}

$args = @('--filter', $filter, '--retry-failed-tests', '3', '--report-trx')

Write-Host "Running dotnet $($dll.FullName)"
Expand Down Expand Up @@ -95,3 +99,23 @@ steps:
testRunTitle: '${{ parameters.version }} ${{parameters.appServiceType}}${{ parameters.testRunTitleSuffix }}'
failTaskOnFailedTests: true
condition: succeededOrFailed()

- task: PowerShell@2
displayName: 'Report E2E duration by class'
condition: succeededOrFailed()
inputs:
targetType: filePath
filePath: '$(System.DefaultWorkingDirectory)/build/scripts/Write-TestClassDurationReport.ps1'
pwsh: true
arguments: >-
-ResultsDirectory "$(Agent.TempDirectory)/testresults"
-OutputDirectory "$(Agent.TempDirectory)/test-class-duration"
-ReportName "E2E_${{ parameters.appServiceType }}_${{ parameters.version }}${{ replace(parameters.testRunTitleSuffix, ' ', '_') }}_ByClass"

- task: PublishBuildArtifacts@1
displayName: 'Publish E2E class duration report'
condition: succeededOrFailed()
inputs:
pathToPublish: '$(Agent.TempDirectory)/test-class-duration'
artifactName: "TestClassDuration_E2E_${{ parameters.appServiceType }}_${{ parameters.version }}${{ replace(parameters.testRunTitleSuffix, ' ', '_') }}"
artifactType: 'container'
144 changes: 144 additions & 0 deletions build/jobs/integration-tests-run.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
parameters:
- name: version
type: string
- name: dataStore
type: string # 'Sql' or 'Cosmos'
- name: keyVaultName
type: string
- name: testFilter
type: string
- name: testRunTitle
type: string
- name: artifactSuffix
type: string
- name: containerAppName
type: string
default: ''
- name: integrationSqlServerName
type: string
default: ''

steps:
# Keep integration tests on the project-based dotnet test path used by main. Directly invoking
# prebuilt test DLLs works for execution, but coverage + retry TRX files are not merged correctly
# by PublishTestResults. When the pipeline moves to .NET 10, revisit prebuilt execution with
# `dotnet test --test-modules`.
- task: DotNetCoreCLI@2
displayName: 'Build Integration Test Projects'
inputs:
command: build
projects: '$(Pipeline.Workspace)/source/test/**/*${{ parameters.version }}.Tests.Integration.csproj'
arguments: '--configuration $(buildConfiguration) -f $(defaultBuildFramework)'

- task: AzureKeyVault@1
displayName: 'Azure Key Vault: ${{ parameters.keyVaultName }}'
inputs:
azureSubscription: $(ConnectedServiceName)
KeyVaultName: '${{ parameters.keyVaultName }}'

- ${{ if eq(parameters.dataStore, 'Sql') }}:
- task: AzurePowerShell@5
displayName: 'Set Workload Identity Variables'
inputs:
azureSubscription: $(ConnectedServiceName)
azurePowerShellVersion: latestVersion
pwsh: true
ScriptType: inlineScript
Inline: |
Write-Host "##vso[task.setvariable variable=AZURESUBSCRIPTION_CLIENT_ID]$env:AZURESUBSCRIPTION_CLIENT_ID"
Write-Host "##vso[task.setvariable variable=AZURESUBSCRIPTION_TENANT_ID]$env:AZURESUBSCRIPTION_TENANT_ID"
Write-Host "##vso[task.setvariable variable=AZURESUBSCRIPTION_SERVICE_CONNECTION_ID]$env:AZURESUBSCRIPTION_SERVICE_CONNECTION_ID"

- ${{ if eq(parameters.dataStore, 'Cosmos') }}:
- task: AzurePowerShell@5
displayName: 'Set Workload Identity Variables'
inputs:
azureSubscription: $(ConnectedServiceName)
azurePowerShellVersion: latestVersion
pwsh: true
ScriptType: inlineScript
Inline: |
Write-Host "##vso[task.setvariable variable=AZURESUBSCRIPTION_CLIENT_ID]$env:AZURESUBSCRIPTION_CLIENT_ID"
Write-Host "##vso[task.setvariable variable=AZURESUBSCRIPTION_TENANT_ID]$env:AZURESUBSCRIPTION_TENANT_ID"
Write-Host "##vso[task.setvariable variable=AZURESUBSCRIPTION_SERVICE_CONNECTION_ID]$env:AZURESUBSCRIPTION_SERVICE_CONNECTION_ID"

$containerAppName = '${{ parameters.containerAppName }}'
$containerApp = Get-AzResource -ResourceGroupName $(UniqueResourceGroupName) -ResourceType "Microsoft.App/containerApps" -Name $containerAppName -ApiVersion "2023-05-01" -ExpandProperties
if ($null -eq $containerApp) {
throw "Container App '$containerAppName' was not found in resource group '$(UniqueResourceGroupName)'"
}

$containerAppData = $containerApp | ConvertTo-Json -Depth 100 | ConvertFrom-Json
$envSettings = $containerAppData.Properties.template.containers[0].env

$dataStoreResourceId = ($envSettings | Where-Object { $_.name -eq "FhirServer__ResourceManager__DataStoreResourceId" } | Select-Object -First 1).value
Write-Host "$dataStoreResourceId"
Write-Host "##vso[task.setvariable variable=DataStoreResourceId]$($dataStoreResourceId)"

- task: DotNetCoreCLI@2
displayName: 'Run ${{ parameters.dataStore }} Integration Tests ${{ parameters.testRunTitle }} with coverage'
inputs:
command: test
projects: '$(Pipeline.Workspace)/source/test/**/*${{ parameters.version }}.Tests.Integration.csproj'
arguments: '--configuration $(buildConfiguration) --no-build -f $(defaultBuildFramework) -- --filter "${{ parameters.testFilter }}" --retry-failed-tests 3 --coverage --coverage-output-format cobertura --coverage-settings "$(System.DefaultWorkingDirectory)/CodeCoverage.Mtp.settings.xml" --report-trx'
testRunTitle: '${{ parameters.testRunTitle }}'
# Disable the task's built-in (non-retry-aware) result publishing so the
# explicit retry-aware PublishTestResults@2 task below is the sole publisher.
publishTestResults: false
env:
platformOptions__resultDirectory: '$(Agent.TempDirectory)/coverage'
'AZURESUBSCRIPTION_CLIENT_ID': '$(AZURESUBSCRIPTION_CLIENT_ID)'
'AZURESUBSCRIPTION_TENANT_ID': '$(AZURESUBSCRIPTION_TENANT_ID)'
'AZURESUBSCRIPTION_SERVICE_CONNECTION_ID': '$(AZURESUBSCRIPTION_SERVICE_CONNECTION_ID)'
'SYSTEM_ACCESSTOKEN': $(System.AccessToken)
${{ if eq(parameters.dataStore, 'Sql') }}:
'SqlServer:ConnectionString': 'Server=tcp:${{ parameters.integrationSqlServerName }}.database.windows.net,1433;Initial Catalog=master;Persist Security Info=False;Authentication=ActiveDirectoryWorkloadIdentity;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;User Id=$(AZURESUBSCRIPTION_CLIENT_ID);'
${{ if eq(parameters.dataStore, 'Cosmos') }}:
'CosmosDb__Host': $(CosmosDb--Host)
'FhirServer__ResourceManager__DataStoreResourceId': '$(DataStoreResourceId)'
'CosmosDb__UseManagedIdentity': true

- task: PublishTestResults@2
displayName: 'Publish integration test results'
inputs:
testResultsFormat: 'VSTest'
testResultsFiles: '$(Agent.TempDirectory)/coverage/**/*.trx'
mergeTestResults: true
testRunTitle: '${{ parameters.testRunTitle }}'
failTaskOnFailedTests: true
condition: succeededOrFailed()

- task: PowerShell@2
displayName: 'Report integration duration by class'
condition: succeededOrFailed()
inputs:
targetType: filePath
filePath: '$(System.DefaultWorkingDirectory)/build/scripts/Write-TestClassDurationReport.ps1'
pwsh: true
arguments: >-
-ResultsDirectory "$(Agent.TempDirectory)/coverage"
-OutputDirectory "$(Agent.TempDirectory)/test-class-duration"
-ReportName "${{ parameters.artifactSuffix }}_ByClass"

- task: PublishBuildArtifacts@1
displayName: 'Publish integration class duration report'
condition: succeededOrFailed()
inputs:
pathToPublish: '$(Agent.TempDirectory)/test-class-duration'
artifactName: 'TestClassDuration_IntegrationTests_${{ parameters.artifactSuffix }}'
artifactType: 'container'

- task: reportgenerator@5
displayName: 'Aggregate integration test coverage'
condition: succeededOrFailed()
inputs:
reports: '$(Agent.TempDirectory)/coverage/**/*.cobertura.xml'
reporttypes: 'Cobertura'
targetdir: '$(Agent.TempDirectory)/coverage-aggregated'

- task: PublishBuildArtifacts@1
displayName: 'Publish integration test coverage'
inputs:
pathToPublish: '$(Agent.TempDirectory)/coverage-aggregated'
artifactName: 'Coverage_IntegrationTests_${{ parameters.artifactSuffix }}'
artifactType: 'container'
4 changes: 4 additions & 0 deletions build/jobs/provision-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ parameters:
- name: sqlServerName
type: string
default: ''
- name: sqlDatabaseName
type: string
default: ''
- name: sqlComputeTier
type: string
default: ''
Expand Down Expand Up @@ -70,6 +73,7 @@ jobs:
-AzureContainerRegistry "$(azureContainerRegistry)"
-TenantIdGuid "$(tenant-id-guid)"
-SqlServerName "${{ parameters.sqlServerName }}"
-SqlDatabaseName "${{ parameters.sqlDatabaseName }}"
-SqlElasticPoolName "${{ parameters.sqlElasticPoolName }}"
-SchemaAutomaticUpdatesEnabled "${{ parameters.schemaAutomaticUpdatesEnabled }}"
-ReindexEnabled "${{ parameters.reindexEnabled }}"
Expand Down
Loading
Loading