From 54f87f37a8d67d9771a4e90c1e7d1fd0e19cdfe7 Mon Sep 17 00:00:00 2001 From: John Simons Date: Sat, 8 Aug 2026 14:21:47 +1000 Subject: [PATCH 01/12] Optimize CI by categorizing tests and parallelizing builds This refactors the CI workflow to improve efficiency and reduce execution time. Test projects are now assigned `TestCategory` properties, enabling matrix jobs to dynamically select, build, and run only the relevant projects for their category. This significantly reduces build times and resource consumption. A new `compile` job ensures the entire `src` directory remains buildable. The build step for test jobs is now backgrounded to overlap with infrastructure setup, further improving parallelization. Also corrects inconsistent `PostgreSQL` casing to `PostgreSql` in the workflow. --- .github/workflows/ci.yml | 85 ++++++++++++------ .gitignore | 2 + ...icular.LicensingComponent.UnitTests.csproj | 1 + ...viceControl.AcceptanceTests.RavenDB.csproj | 1 + ...ntrol.Audit.AcceptanceTests.RavenDB.csproj | 1 + ...erviceControl.Audit.AcceptanceTests.csproj | 1 + ...rol.Audit.Persistence.Tests.RavenDB.csproj | 1 + ...viceControl.Audit.Persistence.Tests.csproj | 1 + .../ServiceControl.Audit.UnitTests.csproj | 1 + .../ServiceControl.Config.Tests.csproj | 1 + ...ServiceControl.Infrastructure.Tests.csproj | 1 + ...eControl.Monitoring.AcceptanceTests.csproj | 1 + ...ServiceControl.Monitoring.UnitTests.csproj | 1 + ...ntrol.MultiInstance.AcceptanceTests.csproj | 1 + ...eControl.Persistence.Tests.InMemory.csproj | 1 + ...ontrol.Persistence.Tests.PostgreSql.csproj | 1 + ...ceControl.Persistence.Tests.RavenDB.csproj | 1 + ...Control.Persistence.Tests.SqlServer.csproj | 1 + ...erviceControl.Transports.ASBS.Tests.csproj | 1 + ...ServiceControl.Transports.ASQ.Tests.csproj | 3 +- ...rviceControl.Transports.IBMMQ.Tests.csproj | 1 + ...erviceControl.Transports.Msmq.Tests.csproj | 1 + ...Control.Transports.PostgreSql.Tests.csproj | 1 + ...itMQClassicConventionalRoutingTests.csproj | 3 +- ....RabbitMQClassicDirectRouting.Tests.csproj | 3 +- ...itMQQuorumConventionalRouting.Tests.csproj | 3 +- ...s.RabbitMQQuorumDirectRouting.Tests.csproj | 3 +- ...ServiceControl.Transports.SQS.Tests.csproj | 1 + ...eControl.Transports.SqlServer.Tests.csproj | 1 + .../ServiceControl.Transports.Tests.csproj | 1 + .../ServiceControl.UnitTests.csproj | 1 + ...ceControlInstaller.Engine.UnitTests.csproj | 1 + ...ontrolInstaller.Packaging.UnitTests.csproj | 1 + tools/run-tests.ps1 | 60 +++++++++++++ tools/select-test-projects.ps1 | 89 +++++++++++++++++++ 35 files changed, 244 insertions(+), 33 deletions(-) create mode 100644 tools/run-tests.ps1 create mode 100644 tools/select-test-projects.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3bcdfb490..7cbcf0bfbf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest] - test-category: [ Default, SqlServer, SqlServerPersistence, AzureServiceBus, RabbitMQ, AzureStorageQueues, MSMQ, SQS, PrimaryRavenAcceptance, PrimaryRavenPersistence, PostgreSQL, PostgreSQLPersistence, IBMMQ ] + test-category: [ Default, SqlServer, SqlServerPersistence, AzureServiceBus, RabbitMQ, AzureStorageQueues, MSMQ, SQS, PrimaryRavenAcceptance, PrimaryRavenPersistence, PostgreSql, PostgreSqlPersistence, IBMMQ ] include: - os: windows-latest os-name: Windows @@ -43,30 +43,24 @@ jobs: uses: actions/setup-dotnet@v6.0.0 with: global-json-file: global.json - - parallel: - - name: Setup WSL - uses: Particular/setup-wsl-action@v1.1.0 - - name: Download RavenDB Server - run: ./tools/download-ravendb-server.ps1 - - name: Build - run: dotnet build src --configuration Release -graph - - name: Zip PowerShell module - run: | - New-Item assets\PowerShellModules -ItemType Directory - Compress-Archive -Path deploy\PowerShellModules\Particular.ServiceControl.Management\* -DestinationPath assets\PowerShellModules\Particular.ServiceControl.Management.zip - - name: Upload assets - uses: actions/upload-artifact@v7.0.1 + - name: Select test projects + id: select + run: ./tools/select-test-projects.ps1 -Category ${{ matrix.test-category }} + # ServiceControlInstaller.Packaging is only in the Default category's build closure, and its zip + # targets fail without a self-contained server in deploy/. Tests never read it: RavenDB.Embedded + # supplies its own server into each test project's output directory. + - name: Download RavenDB Server if: matrix.test-category == 'Default' - with: - name: ${{ matrix.os-name }}-assets - path: | - nugets/ - zip/ - assets/ - retention-days: 1 - - name: Smoke test PowerShell module import - if: matrix.os-name == 'Windows' - run: Import-Module ./deploy/PowerShellModules/Particular.ServiceControl.Management + run: ./tools/download-ravendb-server.ps1 + # Backgrounded so that it overlaps the infrastructure steps below. Those steps are an ordered + # chain (Setup WSL provisions the Docker host the database actions rely on), so they stay in the + # foreground and the job joins the build at the `wait` before running tests. + - name: Build + id: build + background: true + run: dotnet build tests.proj --configuration Release -graph + - name: Setup WSL + uses: Particular/setup-wsl-action@v1.0.0 # there is an issue with az cli and python 3.14, so for now we need to pin it # once the issue is resolved it should be able to be re-floated @@ -86,7 +80,7 @@ jobs: - name: Azure login uses: azure/login@v3.0.1 - if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' || matrix.test-category == 'RabbitMQ' || matrix.test-category == 'PostgreSQL' || matrix.test-category == 'PostgreSQLPersistence' + if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' || matrix.test-category == 'RabbitMQ' || matrix.test-category == 'PostgreSql' || matrix.test-category == 'PostgreSqlPersistence' with: creds: ${{ secrets.AZURE_ACI_CREDENTIALS }} - name: Setup SQL Server @@ -104,14 +98,14 @@ jobs: enable-full-text-search: true - name: Setup PostgreSQL uses: Particular/setup-postgres-action@v3.0.0 - if: matrix.test-category == 'PostgreSQL' + if: matrix.test-category == 'PostgreSql' with: connection-string-name: ServiceControl_TransportTests_PostgreSQL_ConnectionString registry-username: ${{ secrets.DOCKERHUB_USERNAME }} registry-password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Setup PostgreSQL persistence uses: Particular/setup-postgres-action@v3.0.0 - if: matrix.test-category == 'PostgreSQLPersistence' + if: matrix.test-category == 'PostgreSqlPersistence' with: connection-string-name: ServiceControl_Persistence_PostgreSql_ConnectionString registry-username: ${{ secrets.DOCKERHUB_USERNAME }} @@ -159,13 +153,48 @@ jobs: # Cleanup of queues starting with `GHA-` handled by https://github.com/Particular/NServiceBus.AmazonSQS/blob/master/.github/workflows/tests-cleanup.yml $connectString = "AccessKeyId=${{ secrets.AWS_ACCESS_KEY_ID }};SecretAccessKey=${{ secrets.AWS_SECRET_ACCESS_KEY }};Region=${{ secrets.AWS_REGION }};QueueNamePrefix=GHA-${{ github.run_id }}" echo "ServiceControl_TransportTests_SQS_ConnectionString=$connectString" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append + - name: Wait for build + wait: build - name: Run tests - uses: Particular/run-tests-action@v1.7.0 + run: ./tools/run-tests.ps1 -Projects $Env:TEST_PROJECTS env: + TEST_PROJECTS: ${{ steps.select.outputs.test-projects }} ServiceControl_TESTS_FILTER: ${{ matrix.test-category }} PARTICULARSOFTWARE_LICENSE: ${{ secrets.LICENSETEXT }} AZURE_ACI_CREDENTIALS: ${{ secrets.AZURE_ACI_CREDENTIALS }} + # The test matrix only builds each category's own dependency closure, so this job is what keeps + # projects outside every closure (HealthCheckApp, Particular.PlatformSample.ServiceControl, + # LegacyArtifacts) compiling. It runs alongside the matrix and is not on its critical path. + compile: + name: ${{ matrix.os-name }}-Compile + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: windows-latest + os-name: Windows + - os: ubuntu-latest + os-name: Linux + fail-fast: false + steps: + - name: Check for secrets + env: + SECRETS_AVAILABLE: ${{ secrets.SECRETS_AVAILABLE }} + run: exit $(If ($env:SECRETS_AVAILABLE -eq 'true') { 0 } Else { 1 }) + - name: Checkout + uses: actions/checkout@v7.0.1 + with: + fetch-depth: 0 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v6.0.0 + with: + global-json-file: global.json + - name: Download RavenDB Server + run: ./tools/download-ravendb-server.ps1 + - name: Build + run: dotnet build src --configuration Release -graph + windows-installers: uses: ./.github/workflows/build-windows.yml secrets: inherit diff --git a/.gitignore b/.gitignore index 1653797127..b3b621401f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /binaries /deploy /nugets +# Generated by tools/select-test-projects.ps1 +/tests.proj build32 *.vshost.* .nu diff --git a/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj b/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj index b0094aab94..4c62b01f22 100644 --- a/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj +++ b/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj b/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj index 0cf6c7efa5..26650ebe99 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj +++ b/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + PrimaryRavenAcceptance diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj index 223e117c20..b6da0faf58 100644 --- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj +++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj b/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj index 2bbf59bc45..0d59ffe0ef 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj +++ b/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj index 7b0f2fbb6a..1d1e5d2635 100644 --- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj +++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj b/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj index b7f6909c1f..8d02310f98 100644 --- a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj +++ b/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj index ec6be43716..0b26c9f9a0 100644 --- a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj +++ b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj b/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj index 8bafc4ae31..17ca280f67 100644 --- a/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj +++ b/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj @@ -2,6 +2,7 @@ net10.0-windows10.0.19041.0 + Default x64 true diff --git a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj index 7664d8aa63..8feaa7df5a 100644 --- a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj +++ b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj b/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj index 19e62962ab..54b53b33e5 100644 --- a/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj +++ b/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj b/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj index 14236ddac2..05e697c0c7 100644 --- a/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj +++ b/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj index 10b78c9ba4..5998bd5906 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj b/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj index 5854e9fa05..bac12ff076 100644 --- a/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj +++ b/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj b/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj index e585bb53be..46290f8252 100644 --- a/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj +++ b/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj @@ -2,6 +2,7 @@ net10.0 + PostgreSqlPersistence diff --git a/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj b/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj index 85d3b51c65..4b8c1de262 100644 --- a/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj +++ b/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + PrimaryRavenPersistence diff --git a/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj b/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj index 2ed196baf4..e268e9acdc 100644 --- a/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj +++ b/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj @@ -2,6 +2,7 @@ net10.0 + SqlServerPersistence diff --git a/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj b/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj index a7d3313ad3..fb053d09be 100644 --- a/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj +++ b/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + AzureServiceBus diff --git a/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj b/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj index 6a9f7fb7e9..dc1f08141d 100644 --- a/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj +++ b/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + AzureStorageQueues diff --git a/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj b/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj index f441f9cf89..c22367753e 100644 --- a/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj +++ b/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + IBMMQ diff --git a/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj b/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj index e94380b92c..356a3c96fc 100644 --- a/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj +++ b/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj @@ -2,6 +2,7 @@ net10.0-windows + MSMQ diff --git a/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj b/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj index 3071023ee0..2e16270353 100644 --- a/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj +++ b/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + PostgreSql diff --git a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj index 3bd02418c1..e8bd77784a 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQ diff --git a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj index d5bf028a66..e81d6b5a12 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQ diff --git a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj index d5bf028a66..e81d6b5a12 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQ diff --git a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj index 3bd02418c1..e8bd77784a 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQ diff --git a/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj b/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj index cee5fd3419..8057265d47 100644 --- a/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj +++ b/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + SQS diff --git a/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj b/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj index 62fe2403f2..a19182e7f4 100644 --- a/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj +++ b/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + SqlServer diff --git a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj index 261cccf063..883bace779 100644 --- a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj +++ b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj b/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj index 5c3a965155..10922578ae 100644 --- a/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj +++ b/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj b/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj index 3247d3f367..77ce0c087b 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj +++ b/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0-windows + Default diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj b/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj index be95909ae0..e4ff643e53 100644 --- a/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj +++ b/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + Default diff --git a/tools/run-tests.ps1 b/tools/run-tests.ps1 new file mode 100644 index 0000000000..5927f0d073 --- /dev/null +++ b/tools/run-tests.ps1 @@ -0,0 +1,60 @@ +# Runs dotnet test against an explicit list of test projects, rather than discovering every test +# project under src. The list comes from tools/select-test-projects.ps1, so a job only pays for the +# assemblies belonging to its test category. +# +# This is a scoped replacement for Particular/run-tests-action, which has no way to be told which +# projects to run. It should fold back into that action once it grows a 'projects' input. + +param( + [Parameter(Mandatory)] + [string]$Projects, + + [string]$TargetPlatform = 'x64', + + [switch]$ReportWarnings +) + +$ErrorActionPreference = 'Stop' + +$projectPaths = $Projects -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ } + +if ($projectPaths.Count -eq 0) { + throw 'No test projects were supplied.' +} + +Write-Output "Target Platform = $TargetPlatform" + +$reportWarningsValue = if ($ReportWarnings) { 'true' } else { 'false' } +$isUnix = $PSVersionTable.Platform -eq 'Unix' +$exitCode = 0 + +foreach ($project in $projectPaths) { + $frameworks = @( + (Select-Xml -Path $project -XPath "/Project/PropertyGroup/TargetFramework").Node.InnerText + (Select-Xml -Path $project -XPath "/Project/PropertyGroup/TargetFrameworks").Node.InnerText -split ';' + ) | Where-Object { $_ } + + if ($frameworks.Count -eq 0) { + throw "Could not determine a target framework for $project." + } + + foreach ($framework in $frameworks) { + if ($isUnix -and ($framework.StartsWith('net4') -or $framework.Contains('-windows'))) { + Write-Output "Skipping $(Split-Path $project -Leaf) ($framework) because it cannot run on this platform." + continue + } + + Write-Output "::group::Running $(Split-Path $project -Leaf) ($framework)" + + dotnet test $project --configuration Release --no-build --framework $framework --logger "GitHubActions;report-warnings=$reportWarningsValue" -- RunConfiguration.TreatNoTestsAsError=true "RunConfiguration.TargetPlatform=$TargetPlatform" + + Write-Output '::endgroup::' + + if ($LASTEXITCODE -ne 0) { + Write-Output "::error::Exit code = $LASTEXITCODE" + $exitCode = 1 + } + } +} + +exit $exitCode diff --git a/tools/select-test-projects.ps1 b/tools/select-test-projects.ps1 new file mode 100644 index 0000000000..f6c50c099a --- /dev/null +++ b/tools/select-test-projects.ps1 @@ -0,0 +1,89 @@ +# Selects the test projects belonging to a test category, so that CI can build and run only those +# rather than the whole solution. The category is declared by the property in each test +# project, which must match the Filter of the assembly-level IncludeIn*Tests attribute in that project. +# +# Writes the selected project paths to $GITHUB_OUTPUT as 'test-projects', and generates an MSBuild +# traversal project so that `dotnet build` can build the whole selection in one graph. +# +# Use -List to print every category and its projects without writing any files. + +[CmdletBinding(DefaultParameterSetName = 'Select')] +param( + [Parameter(Mandatory, ParameterSetName = 'Select')] + [string]$Category, + + [Parameter(ParameterSetName = 'Select')] + [string]$TraversalProjectPath = 'tests.proj', + + [Parameter(Mandatory, ParameterSetName = 'List')] + [switch]$List +) + +$ErrorActionPreference = 'Stop' + +$projectsByCategory = @{} + +# -Filter rather than -Include: the provider applies it during traversal, which is dramatically faster +# on a tree that already contains bin/obj output. +Get-ChildItem -Path src -Filter '*.csproj' -Recurse -File | ForEach-Object { + $testCategory = (Select-Xml -Path $_.FullName -XPath "/Project/PropertyGroup/TestCategory").Node.InnerText + + if ([string]::IsNullOrWhiteSpace($testCategory)) { + return + } + + if (-not $projectsByCategory.ContainsKey($testCategory)) { + $projectsByCategory[$testCategory] = New-Object Collections.Generic.List[String] + } + + $projectsByCategory[$testCategory].Add((Resolve-Path -Relative $_.FullName)) +} + +if ($List) { + foreach ($key in $projectsByCategory.Keys | Sort-Object) { + Write-Output "$key" + $projectsByCategory[$key] | Sort-Object | ForEach-Object { Write-Output " $_" } + } + return +} + +if (-not $projectsByCategory.ContainsKey($Category)) { + $known = ($projectsByCategory.Keys | Sort-Object) -join ', ' + throw "No test project declares $Category. Known categories: $known" +} + +$projects = $projectsByCategory[$Category] | Sort-Object + +Write-Output "Test category '$Category' selects $($projects.Count) project(s):" +$projects | ForEach-Object { Write-Output " $_" } + +# ReferenceOutputAssembly is irrelevant here because the traversal project produces nothing itself. +# TargetFramework is undefined per reference so that projects targeting net10.0-windows are not forced +# onto the traversal project's own framework, matching the convention in ProjectReferences.Transports.props. +$references = $projects | ForEach-Object { + " " +} + +$traversal = @" + + + + + net10.0 + + + +$($references -join [Environment]::NewLine) + + + +"@ + +Set-Content -Path $TraversalProjectPath -Value $traversal -Encoding utf8 +Write-Output "Wrote traversal project to $TraversalProjectPath" + +if ($Env:GITHUB_OUTPUT) { + "test-projects< Date: Sat, 8 Aug 2026 14:36:46 +1000 Subject: [PATCH 02/12] Split `Default` and `RabbitMQ` test categories for CI optimization Refines the CI workflow by breaking down the broad `Default` and `RabbitMQ` test categories into more granular ones. This addresses instances where these categories were running several slow assemblies in a single job. - `Default` is split into `DefaultCore`, `DefaultAudit`, and `DefaultMonitoring`. - `RabbitMQ` is split into `RabbitMQClassicConventional`, `RabbitMQClassicDirect`, `RabbitMQQuorumConventional`, and `RabbitMQQuorumDirect`. This change enables finer-grained parallelization of tests in the CI workflow, which reduces overall execution time and improves efficiency. It also standardizes test category declarations across projects using the `` property and `IncludeInTestCategory` attribute, and updates CI conditions accordingly. --- .github/workflows/ci.yml | 16 ++++++----- README.md | 27 ++++++++++++++++--- ...icular.LicensingComponent.UnitTests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...ntrol.Audit.AcceptanceTests.RavenDB.csproj | 2 +- .../TestsFilter.cs | 2 +- ...erviceControl.Audit.AcceptanceTests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...rol.Audit.Persistence.Tests.RavenDB.csproj | 2 +- .../TestsFilter.cs | 2 +- ...viceControl.Audit.Persistence.Tests.csproj | 2 +- .../TestsFilter.cs | 2 +- .../ServiceControl.Audit.UnitTests.csproj | 2 +- .../TestsFilter.cs | 2 +- .../ServiceControl.Config.Tests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...ServiceControl.Infrastructure.Tests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...eControl.Monitoring.AcceptanceTests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...ServiceControl.Monitoring.UnitTests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...ntrol.MultiInstance.AcceptanceTests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...eControl.Persistence.Tests.InMemory.csproj | 2 +- .../TestsFilter.cs | 2 +- ...itMQClassicConventionalRoutingTests.csproj | 2 +- .../TestsFilter.cs | 2 +- ....RabbitMQClassicDirectRouting.Tests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...itMQQuorumConventionalRouting.Tests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...s.RabbitMQQuorumDirectRouting.Tests.csproj | 2 +- .../TestsFilter.cs | 2 +- .../ServiceControl.Transports.Tests.csproj | 2 +- .../TestsFilter.cs | 2 +- .../ServiceControl.UnitTests.csproj | 2 +- src/ServiceControl.UnitTests/TestsFilter.cs | 2 +- ...ceControlInstaller.Engine.UnitTests.csproj | 2 +- .../TestsFilter.cs | 2 +- ...ontrolInstaller.Packaging.UnitTests.csproj | 2 +- .../TestsFilter.cs | 2 +- .../IncludeInDefaultTestsAttribute.cs | 4 --- .../IncludeInRabbitMQTestsAttribute.cs | 4 --- 44 files changed, 72 insertions(+), 59 deletions(-) delete mode 100644 src/TestHelper/IncludeInDefaultTestsAttribute.cs delete mode 100644 src/TestHelper/IncludeInRabbitMQTestsAttribute.cs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cbcf0bfbf..1fa82d8cd9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,9 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest] - test-category: [ Default, SqlServer, SqlServerPersistence, AzureServiceBus, RabbitMQ, AzureStorageQueues, MSMQ, SQS, PrimaryRavenAcceptance, PrimaryRavenPersistence, PostgreSql, PostgreSqlPersistence, IBMMQ ] + # Categories are declared by the property in each test project. Default and + # RabbitMQ are split because each was a single job running several slow assemblies back to back. + test-category: [ DefaultCore, DefaultAudit, DefaultMonitoring, SqlServer, SqlServerPersistence, AzureServiceBus, RabbitMQClassicConventional, RabbitMQClassicDirect, RabbitMQQuorumConventional, RabbitMQQuorumDirect, AzureStorageQueues, MSMQ, SQS, PrimaryRavenAcceptance, PrimaryRavenPersistence, PostgreSql, PostgreSqlPersistence, IBMMQ ] include: - os: windows-latest os-name: Windows @@ -46,11 +48,11 @@ jobs: - name: Select test projects id: select run: ./tools/select-test-projects.ps1 -Category ${{ matrix.test-category }} - # ServiceControlInstaller.Packaging is only in the Default category's build closure, and its zip - # targets fail without a self-contained server in deploy/. Tests never read it: RavenDB.Embedded - # supplies its own server into each test project's output directory. + # ServiceControlInstaller.Packaging is only in the DefaultCore category's build closure, and its + # zip targets fail without a self-contained server in deploy/. Tests never read it: + # RavenDB.Embedded supplies its own server into each test project's output directory. - name: Download RavenDB Server - if: matrix.test-category == 'Default' + if: matrix.test-category == 'DefaultCore' run: ./tools/download-ravendb-server.ps1 # Backgrounded so that it overlaps the infrastructure steps below. Those steps are an ordered # chain (Setup WSL provisions the Docker host the database actions rely on), so they stay in the @@ -80,7 +82,7 @@ jobs: - name: Azure login uses: azure/login@v3.0.1 - if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' || matrix.test-category == 'RabbitMQ' || matrix.test-category == 'PostgreSql' || matrix.test-category == 'PostgreSqlPersistence' + if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' || startsWith(matrix.test-category, 'RabbitMQ') || matrix.test-category == 'PostgreSql' || matrix.test-category == 'PostgreSqlPersistence' with: creds: ${{ secrets.AZURE_ACI_CREDENTIALS }} - name: Setup SQL Server @@ -112,7 +114,7 @@ jobs: registry-password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Setup RabbitMQ uses: Particular/setup-rabbitmq-action@v2.0.0 - if: matrix.test-category == 'RabbitMQ' + if: startsWith(matrix.test-category, 'RabbitMQ') with: connection-string-name: ServiceControl_TransportTests_RabbitMQ_ConnectionString registry-username: ${{ secrets.DOCKERHUB_USERNAME }} diff --git a/README.md b/README.md index bdff9eb042..c0bde0c286 100644 --- a/README.md +++ b/README.md @@ -46,18 +46,37 @@ Testing using the [CI workflow](/.github/workflows/ci.yml) depends on the follow Running all tests all the times takes a lot of resources. Tests are filtered based on the `ServiceControl_TESTS_FILTER` environment variable. To run only a subset, e.g., SQS transport tests, define the variable as `ServiceControl_TESTS_FILTER=SQS`. The following list contains all the possible `ServiceControl_TESTS_FILTER` values: -- `Default` - runs only non-transport-specific tests +Non-transport-specific: + +- `DefaultCore` +- `DefaultAudit` +- `DefaultMonitoring` + +Transports: + - `AzureServiceBus` - `AzureStorageQueues` +- `IBMMQ` - `MSMQ` -- `RabbitMQ` +- `PostgreSql` +- `RabbitMQClassicConventional` +- `RabbitMQClassicDirect` +- `RabbitMQQuorumConventional` +- `RabbitMQQuorumDirect` - `SqlServer` -- `SqlServerPersistence` -- `PostgresSqlPersistence` - `SQS` +Persisters: + +- `PostgreSqlPersistence` +- `PrimaryRavenAcceptance` +- `PrimaryRavenPersistence` +- `SqlServerPersistence` + NOTE: If no variable is defined all tests will be executed. +Each category is declared by the `` property in the test project and by the matching assembly-level `IncludeInTestCategory` attribute. CI uses the property to build and run only that category's projects; the attribute is the runtime safety net. Run `./tools/select-test-projects.ps1 -List` to see every category and the projects it selects. + ## Security Configuration Documentation for configuring security features: diff --git a/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj b/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj index 4c62b01f22..529a3f226c 100644 --- a/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj +++ b/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultCore diff --git a/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs b/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs +++ b/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj index b6da0faf58..f3834a4a81 100644 --- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj +++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultAudit diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs +++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj b/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj index 0d59ffe0ef..2388115142 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj +++ b/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultAudit diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs +++ b/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj index 1d1e5d2635..3dd94be5ea 100644 --- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj +++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultAudit diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs +++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj b/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj index 8d02310f98..ece853badc 100644 --- a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj +++ b/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultAudit diff --git a/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs b/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs +++ b/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj index 0b26c9f9a0..44a4d9efa0 100644 --- a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj +++ b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultAudit diff --git a/src/ServiceControl.Audit.UnitTests/TestsFilter.cs b/src/ServiceControl.Audit.UnitTests/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.UnitTests/TestsFilter.cs +++ b/src/ServiceControl.Audit.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj b/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj index 17ca280f67..6c0a7c644b 100644 --- a/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj +++ b/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj @@ -2,7 +2,7 @@ net10.0-windows10.0.19041.0 - Default + DefaultCore x64 true diff --git a/src/ServiceControl.Config.Tests/TestsFilter.cs b/src/ServiceControl.Config.Tests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Config.Tests/TestsFilter.cs +++ b/src/ServiceControl.Config.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj index 8feaa7df5a..5c53d4918f 100644 --- a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj +++ b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultCore diff --git a/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs b/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs +++ b/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj b/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj index 54b53b33e5..dd95e8cfad 100644 --- a/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj +++ b/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultMonitoring diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs index cbb64ab1fc..35e9af11b9 100644 --- a/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs +++ b/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultMonitoring")] \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj b/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj index 05e697c0c7..9261529433 100644 --- a/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj +++ b/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultMonitoring diff --git a/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs b/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs index cbb64ab1fc..35e9af11b9 100644 --- a/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs +++ b/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultMonitoring")] \ No newline at end of file diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj index 5998bd5906..cbaba829ae 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultCore diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj b/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj index bac12ff076..7bf877fa51 100644 --- a/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj +++ b/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultCore diff --git a/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs b/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs +++ b/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj index e8bd77784a..ae91141a0e 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj @@ -2,7 +2,7 @@ net10.0 - RabbitMQ + RabbitMQClassicConventional diff --git a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs index 3d1ec7f59c..b8b0d9f08b 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQClassicConventional")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj index e81d6b5a12..c8b65116c8 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj @@ -2,7 +2,7 @@ net10.0 - RabbitMQ + RabbitMQClassicDirect diff --git a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs index 3d1ec7f59c..605a7b298b 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQClassicDirect")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj index e81d6b5a12..0e5b9c9b3a 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj @@ -2,7 +2,7 @@ net10.0 - RabbitMQ + RabbitMQQuorumConventional diff --git a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs index 3d1ec7f59c..4587c56b95 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQQuorumConventional")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj index e8bd77784a..c9cf087156 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj @@ -2,7 +2,7 @@ net10.0 - RabbitMQ + RabbitMQQuorumDirect diff --git a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs index 3d1ec7f59c..9977bd7ab6 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQQuorumDirect")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj index 883bace779..fd649e1b60 100644 --- a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj +++ b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultCore diff --git a/src/ServiceControl.Transports.Tests/TestsFilter.cs b/src/ServiceControl.Transports.Tests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Transports.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj b/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj index 10922578ae..280ef9eced 100644 --- a/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj +++ b/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultCore diff --git a/src/ServiceControl.UnitTests/TestsFilter.cs b/src/ServiceControl.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.UnitTests/TestsFilter.cs +++ b/src/ServiceControl.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj b/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj index 77ce0c087b..3908dc1ba1 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj +++ b/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj @@ -2,7 +2,7 @@ net10.0-windows - Default + DefaultCore diff --git a/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs b/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj b/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj index e4ff643e53..38dd4fbc7d 100644 --- a/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj +++ b/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj @@ -2,7 +2,7 @@ net10.0 - Default + DefaultCore diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs b/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs +++ b/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/TestHelper/IncludeInDefaultTestsAttribute.cs b/src/TestHelper/IncludeInDefaultTestsAttribute.cs deleted file mode 100644 index 83442d9b40..0000000000 --- a/src/TestHelper/IncludeInDefaultTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInDefaultTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "Default"; -} \ No newline at end of file diff --git a/src/TestHelper/IncludeInRabbitMQTestsAttribute.cs b/src/TestHelper/IncludeInRabbitMQTestsAttribute.cs deleted file mode 100644 index 23002f4336..0000000000 --- a/src/TestHelper/IncludeInRabbitMQTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInRabbitMQTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "RabbitMQ"; -} From 6e31073228f8862edaa1ae386c878034dacad5a0 Mon Sep 17 00:00:00 2001 From: John Simons Date: Sun, 9 Aug 2026 08:47:03 +1000 Subject: [PATCH 03/12] Introduce local action for containerized IBM MQ in CI RabbitMQ now uses Particular/setup-rabbitmq-action@v2.0.0, which gained WSL support upstream, so no local action is needed for it. --- .github/actions/setup-ibmmq/action.yml | 80 ++++++++++++++++++++++++++ .github/workflows/ci.yml | 23 ++++---- 2 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 .github/actions/setup-ibmmq/action.yml diff --git a/.github/actions/setup-ibmmq/action.yml b/.github/actions/setup-ibmmq/action.yml new file mode 100644 index 0000000000..58bb5c11a6 --- /dev/null +++ b/.github/actions/setup-ibmmq/action.yml @@ -0,0 +1,80 @@ +name: Setup IBM MQ +description: > + Runs IBM MQ in a Linux container for the duration of the job. On Linux runners the container runs + directly through Docker. On Windows runners it runs inside the WSL2 Docker host provisioned by + Particular/setup-wsl-action, which must run first, because the IBM MQ image is Linux-only and Windows + runners can otherwise only run Windows containers. + +inputs: + connection-string-name: + description: The name of the environment variable to fill with the IBM MQ connection string. + required: true + queue-manager: + description: The name of the queue manager to create. + required: false + default: QM1 + admin-password: + description: The password for the admin user. + required: false + default: passw0rd + image: + description: The IBM MQ container image to run. + required: false + default: icr.io/ibm-messaging/mq:latest + +runs: + using: composite + steps: + - name: Run IBM MQ container + shell: pwsh + env: + CONNECTION_STRING_NAME: ${{ inputs.connection-string-name }} + QUEUE_MANAGER: ${{ inputs.queue-manager }} + ADMIN_PASSWORD: ${{ inputs.admin-password }} + IMAGE: ${{ inputs.image }} + run: | + $ErrorActionPreference = 'Stop' + + $onWindows = $Env:RUNNER_OS -eq 'Windows' + + if ($onWindows) { + if (-not $Env:WSL_TOOLS_MODULE_PATH) { + throw "WSL_TOOLS_MODULE_PATH is not set. Particular/setup-wsl-action must run before this action on Windows." + } + Import-Module $Env:WSL_TOOLS_MODULE_PATH + } + + function Invoke-Docker([string]$Arguments) { + if ($onWindows) { Invoke-Wsl -CheckExitCode -Command "docker $Arguments" } + else { Invoke-Expression "docker $Arguments" } + } + + $containerName = 'ibmmq' + + Write-Output "Starting $($Env:IMAGE) as $containerName" + Invoke-Docker ("run --name $containerName --detach --publish 1414:1414 --publish 9443:9443 " + + "--health-cmd dspmq --health-interval 10s --health-timeout 5s --health-retries 10 --health-start-period 30s " + + "-e LICENSE=accept -e MQ_QMGR_NAME=$($Env:QUEUE_MANAGER) -e MQ_ADMIN_PASSWORD=$($Env:ADMIN_PASSWORD) " + + $Env:IMAGE) + + Write-Output "::group::Waiting for container health check" + + # Out-String because the WSL and native paths differ in whether they hand back a string or a + # collection of output lines. + $deadline = (Get-Date).AddMinutes(5) + while ((Invoke-Docker "inspect --format '{{.State.Health.Status}}' $containerName" | Out-String).Trim() -ne 'healthy') { + if ((Get-Date) -gt $deadline) { + Invoke-Docker "logs $containerName" + throw "IBM MQ did not report healthy within 5 minutes." + } + Start-Sleep -Seconds 2 + } + + Write-Output "::endgroup::" + + # On Windows the queue manager listens inside the WSL VM, reachable on its gateway address. + $mqHost = if ($onWindows) { $Env:WSL_IP } else { 'localhost' } + $connectionString = "mq://admin:$($Env:ADMIN_PASSWORD)@${mqHost}:1414/$($Env:QUEUE_MANAGER)?channel=DEV.ADMIN.SVRCONN&topicprefix=DEV" + + Write-Output "IBM MQ is healthy on $mqHost" + Write-Output "$($Env:CONNECTION_STRING_NAME)=$connectionString" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fa82d8cd9..914fccf4f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,6 @@ jobs: exclude: - os: ubuntu-latest test-category: MSMQ - - os: windows-latest - test-category: IBMMQ fail-fast: false steps: - name: Check for secrets @@ -61,7 +59,11 @@ jobs: id: build background: true run: dotnet build tests.proj --configuration Release -graph + # Provisions the WSL2 Docker host that the SQL Server, PostgreSQL, RabbitMQ and IBM MQ containers + # run in on Windows. A no-op on Linux, but still run there so those actions see the same + # environment variables on both runners. - name: Setup WSL + if: startsWith(matrix.test-category, 'RabbitMQ') || contains(fromJSON('["SqlServer", "SqlServerPersistence", "PostgreSql", "PostgreSqlPersistence", "IBMMQ"]'), matrix.test-category) uses: Particular/setup-wsl-action@v1.0.0 # there is an issue with az cli and python 3.14, so for now we need to pin it @@ -80,9 +82,11 @@ jobs: python -m pip install --user "azure-cli==2.64.0" echo "$HOME/.local/bin" >> "$GITHUB_PATH" + # Only the Azure Service Bus and Azure Storage Queues actions provision cloud resources. RabbitMQ, + # PostgreSQL and SQL Server all run as containers now, so they no longer need an Azure session. - name: Azure login uses: azure/login@v3.0.1 - if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' || startsWith(matrix.test-category, 'RabbitMQ') || matrix.test-category == 'PostgreSql' || matrix.test-category == 'PostgreSqlPersistence' + if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' with: creds: ${{ secrets.AZURE_ACI_CREDENTIALS }} - name: Setup SQL Server @@ -134,17 +138,10 @@ jobs: azure-credentials: ${{ secrets.AZURE_ACI_CREDENTIALS }} tag: ServiceControl - name: Setup IBM MQ + uses: ./.github/actions/setup-ibmmq if: matrix.test-category == 'IBMMQ' - run: | - docker run --name ibmmq -d -p 1414:1414 -p 9443:9443 ` - --health-cmd "dspmq" --health-interval 10s --health-timeout 5s --health-retries 10 --health-start-period 30s ` - -e LICENSE=accept -e MQ_QMGR_NAME=QM1 -e MQ_ADMIN_PASSWORD=passw0rd ` - icr.io/ibm-messaging/mq:latest - # Wait for container health check to pass - while ((docker inspect --format '{{.State.Health.Status}}' ibmmq) -ne 'healthy') { - Start-Sleep -Seconds 2 - } - echo "ServiceControl_TransportTests_IBMMQ_ConnectionString=mq://admin:passw0rd@localhost:1414/QM1?channel=DEV.ADMIN.SVRCONN&topicprefix=DEV" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append + with: + connection-string-name: ServiceControl_TransportTests_IBMMQ_ConnectionString - name: Setup SQS environment variables if: matrix.test-category == 'SQS' run: | From 5819c1945a28dc1730e0b59b6997283ba485f78a Mon Sep 17 00:00:00 2001 From: John Simons Date: Sat, 8 Aug 2026 15:48:02 +1000 Subject: [PATCH 04/12] Explicitly create all required event sources for multi-instance tests The `ServiceControl.MultiInstance.AcceptanceTests` now explicitly creates both the primary and audit event sources on Windows. This ensures the necessary EventLog sources are available for the tests. Previously, the audit event source was implicitly created by other acceptance tests, which is no longer guaranteed due to CI changes like parallelization. --- .../ServiceControl.Audit.csproj | 1 + .../SetupFixture.cs | 36 +++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/ServiceControl.Audit/ServiceControl.Audit.csproj b/src/ServiceControl.Audit/ServiceControl.Audit.csproj index f88c753d1b..a16a75c832 100644 --- a/src/ServiceControl.Audit/ServiceControl.Audit.csproj +++ b/src/ServiceControl.Audit/ServiceControl.Audit.csproj @@ -59,6 +59,7 @@ + diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs index 4c74e8dcc9..a2b3d0c746 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs @@ -3,9 +3,12 @@ namespace ServiceControl.MultiInstance.AcceptanceTests; using System; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; +using AuditEventSourceCreator = global::ServiceControl.Audit.Infrastructure.EventSourceCreator; +using PrimaryEventSourceCreator = ServiceBus.Management.Infrastructure.Installers.EventSourceCreator; [SetUpFixture] public class SetupFixture @@ -13,17 +16,30 @@ public class SetupFixture [OneTimeSetUp] public async Task Setup() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - ServiceBus.Management.Infrastructure.Installers.EventSourceCreator.Create(); + return; + } + + // These tests host a primary and an audit instance, so both event sources have to exist before + // either instance runs its setup. The audit source used to be created as a side effect of the + // audit acceptance tests running earlier in the same CI job, which is no longer the case. + PrimaryEventSourceCreator.Create(); + AuditEventSourceCreator.Create(); + + await WaitForSource(PrimaryEventSourceCreator.SourceName); + await WaitForSource(AuditEventSourceCreator.SourceName); + } - //There is a delay for this becoming true, tests will fall over if they interleave in the wrong way. - var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - while (!EventLog.SourceExists(ServiceBus.Management.Infrastructure.Installers.EventSourceCreator.SourceName)) - { - cts.Token.ThrowIfCancellationRequested(); - await Task.Delay(500, CancellationToken.None); - } + //There is a delay for this becoming true, tests will fall over if they interleave in the wrong way. + [SupportedOSPlatform("windows")] + static async Task WaitForSource(string sourceName) + { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + while (!EventLog.SourceExists(sourceName)) + { + cts.Token.ThrowIfCancellationRequested(); + await Task.Delay(500, CancellationToken.None); } } -} \ No newline at end of file +} From 1be70066c3f4d02d3d9ae09939e849ac8e86bef7 Mon Sep 17 00:00:00 2001 From: John Simons Date: Sat, 8 Aug 2026 16:07:30 +1000 Subject: [PATCH 05/12] Disable aggressive caching for notifications settings Notifications settings are rarely read and primarily edited by hand. Aggressive caching, which invalidates asynchronously via the Changes API, could lead to stale reads immediately following a save. This change ensures the latest settings are always retrieved, preventing potential consistency issues. --- .../Editing/NotificationsManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs b/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs index 57e8d2b454..6531b6ed7b 100644 --- a/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs +++ b/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs @@ -1,6 +1,5 @@ namespace ServiceControl.Persistence.RavenDB.Editing { - using System; using System.Threading.Tasks; using Notifications; using Raven.Client.Documents.Session; @@ -8,11 +7,12 @@ class NotificationsManager(IAsyncDocumentSession session) : AbstractSessionManager(session), INotificationsManager { const string SingleDocumentId = "NotificationsSettings/All"; - static readonly TimeSpan CacheTimeout = TimeSpan.FromMinutes(5); // Raven requires this to be at least 1 second public async Task LoadSettings() { - using var aggressivelyCacheFor = await Session.Advanced.DocumentStore.AggressivelyCacheForAsync(CacheTimeout); + // Deliberately not aggressively cached. These settings are read rarely and edited by hand, + // and aggressive caching invalidates asynchronously via the Changes API, so a read straight + // after a save can return the pre-save document. var settings = await Session .LoadAsync(SingleDocumentId); From ec6bfdf70e0242426e019a342ccf311b86b573fb Mon Sep 17 00:00:00 2001 From: John Simons Date: Sat, 8 Aug 2026 16:51:27 +1000 Subject: [PATCH 06/12] Enhance CI stability for messaging service setup Improves the reliability of GitHub Actions for IBM MQ and RabbitMQ. IBM MQ's health check now directly probes the listener port, as Docker's internal check was insufficient for detecting connectivity issues, especially on Windows/WSL. RabbitMQ setup is made more robust by automatically restarting the container once if it hangs during boot within 90 seconds. Additionally, WSL memory allocation is increased to 8GB to provide more headroom and prevent resource-related failures during concurrent builds. --- .github/actions/setup-ibmmq/action.yml | 29 +++++++++++++++++++------- .github/workflows/ci.yml | 4 ++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/actions/setup-ibmmq/action.yml b/.github/actions/setup-ibmmq/action.yml index 58bb5c11a6..7f581ad722 100644 --- a/.github/actions/setup-ibmmq/action.yml +++ b/.github/actions/setup-ibmmq/action.yml @@ -57,23 +57,36 @@ runs: "-e LICENSE=accept -e MQ_QMGR_NAME=$($Env:QUEUE_MANAGER) -e MQ_ADMIN_PASSWORD=$($Env:ADMIN_PASSWORD) " + $Env:IMAGE) - Write-Output "::group::Waiting for container health check" + # On Windows the queue manager listens inside the WSL VM, reachable on its gateway address. + $mqHost = if ($onWindows) { $Env:WSL_IP } else { 'localhost' } + + # Docker's health check runs dspmq inside the container, which reports the queue manager + # process rather than whether the listener is reachable through the WSL port forward. Probe + # the endpoint the tests actually connect to instead, the way setup-postgres-action does. + function Test-Endpoint([string]$TargetHost, [int]$Port) { + $client = [System.Net.Sockets.TcpClient]::new() + try { return $client.ConnectAsync($TargetHost, $Port).Wait(5000) -and $client.Connected } + catch { return $false } + finally { $client.Dispose() } + } + + Write-Output "::group::Waiting for IBM MQ to accept connections on ${mqHost}:1414" - # Out-String because the WSL and native paths differ in whether they hand back a string or a - # collection of output lines. $deadline = (Get-Date).AddMinutes(5) - while ((Invoke-Docker "inspect --format '{{.State.Health.Status}}' $containerName" | Out-String).Trim() -ne 'healthy') { + while (-not (Test-Endpoint $mqHost 1414)) { if ((Get-Date) -gt $deadline) { + # Out-String because the WSL and native paths differ in whether they hand back a string + # or a collection of output lines. + $status = (Invoke-Docker "inspect --format '{{.State.Status}} {{.State.Health.Status}}' $containerName" | Out-String).Trim() + Write-Output "Container state: $status" + Invoke-Docker "ps --all --filter name=$containerName" Invoke-Docker "logs $containerName" - throw "IBM MQ did not report healthy within 5 minutes." + throw "IBM MQ did not accept connections on ${mqHost}:1414 within 5 minutes." } Start-Sleep -Seconds 2 } Write-Output "::endgroup::" - - # On Windows the queue manager listens inside the WSL VM, reachable on its gateway address. - $mqHost = if ($onWindows) { $Env:WSL_IP } else { 'localhost' } $connectionString = "mq://admin:$($Env:ADMIN_PASSWORD)@${mqHost}:1414/$($Env:QUEUE_MANAGER)?channel=DEV.ADMIN.SVRCONN&topicprefix=DEV" Write-Output "IBM MQ is healthy on $mqHost" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 914fccf4f1..271959c838 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,10 @@ jobs: - name: Setup WSL if: startsWith(matrix.test-category, 'RabbitMQ') || contains(fromJSON('["SqlServer", "SqlServerPersistence", "PostgreSql", "PostgreSqlPersistence", "IBMMQ"]'), matrix.test-category) uses: Particular/setup-wsl-action@v1.0.0 + with: + # The action defaults to 4GB. The runner has 16GB and the build runs concurrently with the + # container starting up, so give the VM real headroom. + memory: 8GB # there is an issue with az cli and python 3.14, so for now we need to pin it # once the issue is resolved it should be able to be re-floated From 6ec08ce6b88dd17bb4532e7b2f8aa242bbfce1e0 Mon Sep 17 00:00:00 2001 From: John Simons Date: Sun, 9 Aug 2026 08:50:41 +1000 Subject: [PATCH 07/12] Delay Azure resource provisioning until after build Moves the Azure Service Bus and Storage Queues setup steps to occur only after the build is successful. This prevents the creation of real cloud resources if the build fails, reducing unnecessary provisioning and potential orphaned resources. --- .github/workflows/ci.yml | 47 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 271959c838..40d85d42cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: # environment variables on both runners. - name: Setup WSL if: startsWith(matrix.test-category, 'RabbitMQ') || contains(fromJSON('["SqlServer", "SqlServerPersistence", "PostgreSql", "PostgreSqlPersistence", "IBMMQ"]'), matrix.test-category) - uses: Particular/setup-wsl-action@v1.0.0 + uses: Particular/setup-wsl-action@v1.1.0 with: # The action defaults to 4GB. The runner has 16GB and the build runs concurrently with the # container starting up, so give the VM real headroom. @@ -86,13 +86,6 @@ jobs: python -m pip install --user "azure-cli==2.64.0" echo "$HOME/.local/bin" >> "$GITHUB_PATH" - # Only the Azure Service Bus and Azure Storage Queues actions provision cloud resources. RabbitMQ, - # PostgreSQL and SQL Server all run as containers now, so they no longer need an Azure session. - - name: Azure login - uses: azure/login@v3.0.1 - if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' - with: - creds: ${{ secrets.AZURE_ACI_CREDENTIALS }} - name: Setup SQL Server uses: Particular/install-sql-server-action@v3.0.0 if: matrix.test-category == 'SqlServer' @@ -127,20 +120,6 @@ jobs: connection-string-name: ServiceControl_TransportTests_RabbitMQ_ConnectionString registry-username: ${{ secrets.DOCKERHUB_USERNAME }} registry-password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Setup Azure Service Bus - uses: Particular/setup-azureservicebus-action@v2.5.0 - if: matrix.test-category == 'AzureServiceBus' - with: - connection-string-name: ServiceControl_TransportTests_ASBS_ConnectionString - azure-credentials: ${{ secrets.AZURE_ACI_CREDENTIALS }} - tag: ServiceControl - - name: Setup Azure Storage Queues - uses: Particular/setup-azurestorage-action@v2.0.0 - if: matrix.test-category == 'AzureStorageQueues' - with: - connection-string-name: ServiceControl_TransportTests_ASQ_ConnectionString - azure-credentials: ${{ secrets.AZURE_ACI_CREDENTIALS }} - tag: ServiceControl - name: Setup IBM MQ uses: ./.github/actions/setup-ibmmq if: matrix.test-category == 'IBMMQ' @@ -156,8 +135,32 @@ jobs: # Cleanup of queues starting with `GHA-` handled by https://github.com/Particular/NServiceBus.AmazonSQS/blob/master/.github/workflows/tests-cleanup.yml $connectString = "AccessKeyId=${{ secrets.AWS_ACCESS_KEY_ID }};SecretAccessKey=${{ secrets.AWS_SECRET_ACCESS_KEY }};Region=${{ secrets.AWS_REGION }};QueueNamePrefix=GHA-${{ github.run_id }}" echo "ServiceControl_TransportTests_SQS_ConnectionString=$connectString" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append + # Everything above provisions local containers, so it overlaps the build freely. The Azure steps + # below create real cloud resources, so they sit behind the wait: a background step only fails the + # job at the next wait that includes it, so without this a commit that cannot compile would still + # create a Service Bus namespace or storage account and then rely on teardown to remove it. If the + # runner is killed that teardown never runs, so nothing is created until the build is known good. - name: Wait for build wait: build + - name: Azure login + uses: azure/login@v3.0.1 + if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' + with: + creds: ${{ secrets.AZURE_ACI_CREDENTIALS }} + - name: Setup Azure Service Bus + uses: Particular/setup-azureservicebus-action@v2.5.0 + if: matrix.test-category == 'AzureServiceBus' + with: + connection-string-name: ServiceControl_TransportTests_ASBS_ConnectionString + azure-credentials: ${{ secrets.AZURE_ACI_CREDENTIALS }} + tag: ServiceControl + - name: Setup Azure Storage Queues + uses: Particular/setup-azurestorage-action@v2.0.0 + if: matrix.test-category == 'AzureStorageQueues' + with: + connection-string-name: ServiceControl_TransportTests_ASQ_ConnectionString + azure-credentials: ${{ secrets.AZURE_ACI_CREDENTIALS }} + tag: ServiceControl - name: Run tests run: ./tools/run-tests.ps1 -Projects $Env:TEST_PROJECTS env: From 39510170b3c31e0fb10ce4289bff4f4dba058d74 Mon Sep 17 00:00:00 2001 From: John Simons Date: Mon, 10 Aug 2026 09:21:31 +1000 Subject: [PATCH 08/12] Centralize test category declaration Removes redundant `TestsFilter.cs` files and specialized `IncludeIn...TestsAttribute.cs` attributes. Test categories are now declared once via the `` MSBuild property in each test project, which `Directory.Build.props` uses to automatically generate the corresponding `IncludeInTestCategoryAttribute`. This ensures consistency between CI test filtering, which uses the MSBuild property, and local test runs filtered by `ServiceControl_TESTS_FILTER`, which relies on the assembly attribute. It also simplifies test project configuration by removing boilerplate files. --- src/Directory.Build.props | 12 ++++++++++++ .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - ...rviceControl.Audit.AcceptanceTests.RavenDB.csproj | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - ...iceControl.Audit.Persistence.Tests.RavenDB.csproj | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - src/ServiceControl.Audit.UnitTests/TestsFilter.cs | 1 - src/ServiceControl.Config.Tests/TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../ServiceControl.Transports.ASBS.Tests.csproj | 1 - .../TestsFilter.cs | 1 - .../ServiceControl.Transports.ASQ.Tests.csproj | 1 - .../TestsFilter.cs | 1 - .../ServiceControl.Transports.IBMMQ.Tests.csproj | 1 - .../TestsFilter.cs | 1 - .../ServiceControl.Transports.Msmq.Tests.csproj | 1 - .../TestsFilter.cs | 1 - ...ServiceControl.Transports.PostgreSql.Tests.csproj | 1 - .../TestsFilter.cs | 1 - ...ts.RabbitMQClassicConventionalRoutingTests.csproj | 1 - .../TestsFilter.cs | 1 - ...nsports.RabbitMQClassicDirectRouting.Tests.csproj | 1 - .../TestsFilter.cs | 1 - ...ts.RabbitMQQuorumConventionalRouting.Tests.csproj | 1 - .../TestsFilter.cs | 1 - ...ansports.RabbitMQQuorumDirectRouting.Tests.csproj | 1 - .../TestsFilter.cs | 1 - .../ServiceControl.Transports.SQS.Tests.csproj | 1 - .../TestsFilter.cs | 1 - .../ServiceControl.Transports.SqlServer.Tests.csproj | 1 - .../TestsFilter.cs | 1 - src/ServiceControl.Transports.Tests/TestsFilter.cs | 1 - src/ServiceControl.UnitTests/TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - .../TestsFilter.cs | 1 - src/TestHelper/IncludeInAmazonSqsTestsAttribute.cs | 4 ---- .../IncludeInAzureServiceBusTestsAttribute.cs | 4 ---- .../IncludeInAzureStorageQueuesTestsAttribute.cs | 4 ---- src/TestHelper/IncludeInIBMMQTestsAttribute.cs | 4 ---- src/TestHelper/IncludeInMsmqTestsAttribute.cs | 4 ---- src/TestHelper/IncludeInPostgreSqlTestsAttribute.cs | 4 ---- src/TestHelper/IncludeInSqlServerTestsAttribute.cs | 4 ---- 52 files changed, 12 insertions(+), 72 deletions(-) delete mode 100644 src/Particular.LicensingComponent.UnitTests/TestsFilter.cs delete mode 100644 src/ServiceControl.AcceptanceTests.RavenDB/TestsFilter.cs delete mode 100644 src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs delete mode 100644 src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs delete mode 100644 src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs delete mode 100644 src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Audit.UnitTests/TestsFilter.cs delete mode 100644 src/ServiceControl.Config.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Infrastructure.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs delete mode 100644 src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs delete mode 100644 src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs delete mode 100644 src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs delete mode 100644 src/ServiceControl.Persistence.Tests.PostgreSql/TestsFilter.cs delete mode 100644 src/ServiceControl.Persistence.Tests.RavenDB/TestsFilter.cs delete mode 100644 src/ServiceControl.Persistence.Tests.SqlServer/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.ASBS.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.ASQ.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.IBMMQ.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.Msmq.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.PostgreSql.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.SQS.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.SqlServer.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.Transports.Tests/TestsFilter.cs delete mode 100644 src/ServiceControl.UnitTests/TestsFilter.cs delete mode 100644 src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs delete mode 100644 src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs delete mode 100644 src/TestHelper/IncludeInAmazonSqsTestsAttribute.cs delete mode 100644 src/TestHelper/IncludeInAzureServiceBusTestsAttribute.cs delete mode 100644 src/TestHelper/IncludeInAzureStorageQueuesTestsAttribute.cs delete mode 100644 src/TestHelper/IncludeInIBMMQTestsAttribute.cs delete mode 100644 src/TestHelper/IncludeInMsmqTestsAttribute.cs delete mode 100644 src/TestHelper/IncludeInPostgreSqlTestsAttribute.cs delete mode 100644 src/TestHelper/IncludeInSqlServerTestsAttribute.cs diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 3291fe2654..c175513774 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -31,6 +31,18 @@ + + + + <_Parameter1>$(TestCategory) + + + diff --git a/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs b/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/TestsFilter.cs b/src/ServiceControl.AcceptanceTests.RavenDB/TestsFilter.cs deleted file mode 100644 index d5e5dfd11e..0000000000 --- a/src/ServiceControl.AcceptanceTests.RavenDB/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("PrimaryRavenAcceptance")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj index f3834a4a81..a71d5defe6 100644 --- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj +++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj @@ -27,7 +27,6 @@ - diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs deleted file mode 100644 index 653413cf8e..0000000000 --- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs deleted file mode 100644 index 653413cf8e..0000000000 --- a/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj index 3dd94be5ea..26a098607b 100644 --- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj +++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj @@ -25,7 +25,6 @@ - diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs deleted file mode 100644 index 653413cf8e..0000000000 --- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs b/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs deleted file mode 100644 index 653413cf8e..0000000000 --- a/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.UnitTests/TestsFilter.cs b/src/ServiceControl.Audit.UnitTests/TestsFilter.cs deleted file mode 100644 index 653413cf8e..0000000000 --- a/src/ServiceControl.Audit.UnitTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Config.Tests/TestsFilter.cs b/src/ServiceControl.Config.Tests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControl.Config.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs b/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs deleted file mode 100644 index 35e9af11b9..0000000000 --- a/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultMonitoring")] \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs b/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs deleted file mode 100644 index 35e9af11b9..0000000000 --- a/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultMonitoring")] \ No newline at end of file diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs b/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Persistence.Tests.PostgreSql/TestsFilter.cs b/src/ServiceControl.Persistence.Tests.PostgreSql/TestsFilter.cs deleted file mode 100644 index c2971486f6..0000000000 --- a/src/ServiceControl.Persistence.Tests.PostgreSql/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("PostgreSqlPersistence")] diff --git a/src/ServiceControl.Persistence.Tests.RavenDB/TestsFilter.cs b/src/ServiceControl.Persistence.Tests.RavenDB/TestsFilter.cs deleted file mode 100644 index 2d1b7a5ce1..0000000000 --- a/src/ServiceControl.Persistence.Tests.RavenDB/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("PrimaryRavenPersistence")] \ No newline at end of file diff --git a/src/ServiceControl.Persistence.Tests.SqlServer/TestsFilter.cs b/src/ServiceControl.Persistence.Tests.SqlServer/TestsFilter.cs deleted file mode 100644 index d37d903ffb..0000000000 --- a/src/ServiceControl.Persistence.Tests.SqlServer/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("SqlServerPersistence")] diff --git a/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj b/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj index fb053d09be..14ce97e5ce 100644 --- a/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj +++ b/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj @@ -30,7 +30,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.ASBS.Tests/TestsFilter.cs b/src/ServiceControl.Transports.ASBS.Tests/TestsFilter.cs deleted file mode 100644 index d7eead1f29..0000000000 --- a/src/ServiceControl.Transports.ASBS.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInAzureServiceBusTests()] \ No newline at end of file diff --git a/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj b/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj index dc1f08141d..6af65de105 100644 --- a/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj +++ b/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj @@ -26,7 +26,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.ASQ.Tests/TestsFilter.cs b/src/ServiceControl.Transports.ASQ.Tests/TestsFilter.cs deleted file mode 100644 index 9af29ba3a2..0000000000 --- a/src/ServiceControl.Transports.ASQ.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInAzureStorageQueuesTests()] \ No newline at end of file diff --git a/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj b/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj index c22367753e..e3635c34b1 100644 --- a/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj +++ b/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj @@ -28,7 +28,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.IBMMQ.Tests/TestsFilter.cs b/src/ServiceControl.Transports.IBMMQ.Tests/TestsFilter.cs deleted file mode 100644 index 76ebf15442..0000000000 --- a/src/ServiceControl.Transports.IBMMQ.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInIBMMQTests()] \ No newline at end of file diff --git a/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj b/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj index 356a3c96fc..9921927a33 100644 --- a/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj +++ b/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj @@ -27,7 +27,6 @@ - diff --git a/src/ServiceControl.Transports.Msmq.Tests/TestsFilter.cs b/src/ServiceControl.Transports.Msmq.Tests/TestsFilter.cs deleted file mode 100644 index dd67f84a24..0000000000 --- a/src/ServiceControl.Transports.Msmq.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInMsmqTests()] \ No newline at end of file diff --git a/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj b/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj index 2e16270353..cb7a91727d 100644 --- a/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj +++ b/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj @@ -28,7 +28,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.PostgreSql.Tests/TestsFilter.cs b/src/ServiceControl.Transports.PostgreSql.Tests/TestsFilter.cs deleted file mode 100644 index aaa9ec7659..0000000000 --- a/src/ServiceControl.Transports.PostgreSql.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInPostgreSqlTests()] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj index ae91141a0e..6fd23a7ba6 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj @@ -28,7 +28,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs deleted file mode 100644 index b8b0d9f08b..0000000000 --- a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("RabbitMQClassicConventional")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj index c8b65116c8..2164ec1d26 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj @@ -27,7 +27,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs deleted file mode 100644 index 605a7b298b..0000000000 --- a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("RabbitMQClassicDirect")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj index 0e5b9c9b3a..be4eea22f0 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj @@ -27,7 +27,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs deleted file mode 100644 index 4587c56b95..0000000000 --- a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("RabbitMQQuorumConventional")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj index c9cf087156..5f5b275050 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj @@ -28,7 +28,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs deleted file mode 100644 index 9977bd7ab6..0000000000 --- a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("RabbitMQQuorumDirect")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj b/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj index 8057265d47..c6039e8713 100644 --- a/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj +++ b/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj @@ -30,7 +30,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.SQS.Tests/TestsFilter.cs b/src/ServiceControl.Transports.SQS.Tests/TestsFilter.cs deleted file mode 100644 index 1e15cac6b6..0000000000 --- a/src/ServiceControl.Transports.SQS.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInAmazonSqsTests()] \ No newline at end of file diff --git a/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj b/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj index a19182e7f4..3eed2f8333 100644 --- a/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj +++ b/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj @@ -29,7 +29,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Transports.SqlServer.Tests/TestsFilter.cs b/src/ServiceControl.Transports.SqlServer.Tests/TestsFilter.cs deleted file mode 100644 index cf47333a84..0000000000 --- a/src/ServiceControl.Transports.SqlServer.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInSqlServerTests()] \ No newline at end of file diff --git a/src/ServiceControl.Transports.Tests/TestsFilter.cs b/src/ServiceControl.Transports.Tests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControl.Transports.Tests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.UnitTests/TestsFilter.cs b/src/ServiceControl.UnitTests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControl.UnitTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs b/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs b/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs deleted file mode 100644 index aab6eb2319..0000000000 --- a/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/TestHelper/IncludeInAmazonSqsTestsAttribute.cs b/src/TestHelper/IncludeInAmazonSqsTestsAttribute.cs deleted file mode 100644 index c1d8825d57..0000000000 --- a/src/TestHelper/IncludeInAmazonSqsTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInAmazonSqsTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "SQS"; -} diff --git a/src/TestHelper/IncludeInAzureServiceBusTestsAttribute.cs b/src/TestHelper/IncludeInAzureServiceBusTestsAttribute.cs deleted file mode 100644 index ca89724c59..0000000000 --- a/src/TestHelper/IncludeInAzureServiceBusTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInAzureServiceBusTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "AzureServiceBus"; -} diff --git a/src/TestHelper/IncludeInAzureStorageQueuesTestsAttribute.cs b/src/TestHelper/IncludeInAzureStorageQueuesTestsAttribute.cs deleted file mode 100644 index 80488efc96..0000000000 --- a/src/TestHelper/IncludeInAzureStorageQueuesTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInAzureStorageQueuesTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "AzureStorageQueues"; -} diff --git a/src/TestHelper/IncludeInIBMMQTestsAttribute.cs b/src/TestHelper/IncludeInIBMMQTestsAttribute.cs deleted file mode 100644 index 2bb18aeb93..0000000000 --- a/src/TestHelper/IncludeInIBMMQTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInIBMMQTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "IBMMQ"; -} diff --git a/src/TestHelper/IncludeInMsmqTestsAttribute.cs b/src/TestHelper/IncludeInMsmqTestsAttribute.cs deleted file mode 100644 index a4946e59d3..0000000000 --- a/src/TestHelper/IncludeInMsmqTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInMsmqTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "MSMQ"; -} diff --git a/src/TestHelper/IncludeInPostgreSqlTestsAttribute.cs b/src/TestHelper/IncludeInPostgreSqlTestsAttribute.cs deleted file mode 100644 index 4691a26412..0000000000 --- a/src/TestHelper/IncludeInPostgreSqlTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInPostgreSqlTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "PostgreSql"; -} diff --git a/src/TestHelper/IncludeInSqlServerTestsAttribute.cs b/src/TestHelper/IncludeInSqlServerTestsAttribute.cs deleted file mode 100644 index 4240d8ea0a..0000000000 --- a/src/TestHelper/IncludeInSqlServerTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInSqlServerTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "SqlServer"; -} From 8052b34c9ff4b41b08ffa7069e0909c720610667 Mon Sep 17 00:00:00 2001 From: John Simons Date: Mon, 10 Aug 2026 09:38:02 +1000 Subject: [PATCH 09/12] Add validation for test categories and improve documentation Introduces a build-time check in `select-test-projects.ps1` to prevent test projects from being silently ignored by CI if they reference the test SDK but do not declare a ``. Updates `README.md` with enhanced documentation on adding test projects and categories, utilizing GitHub Admonitions for improved clarity and visibility of important configuration details. --- README.md | 26 ++++++++++++++++++++++++-- tools/select-test-projects.ps1 | 15 ++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c0bde0c286..8db5a58c80 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,31 @@ Persisters: - `PrimaryRavenPersistence` - `SqlServerPersistence` -NOTE: If no variable is defined all tests will be executed. +> [!NOTE] +> If no variable is defined all tests will be executed. + +Each category is declared once, by the `` property in the test project. CI reads that property to build and run only the projects belonging to the category under test, and the build generates the assembly-level `IncludeInTestCategory` attribute from it, which is what `ServiceControl_TESTS_FILTER` matches against at run time. + +### Adding a test project + +> [!IMPORTANT] +> Every test project must set `` in its `.csproj`, alongside `TargetFramework`: +> +> ```xml +> +> net10.0 +> DefaultCore +> +> ``` +> +> CI selects projects by that property alone, so a test project without it is never built and never run. The tests would simply not exist as far as CI is concerned, and nothing would go red. `tools/select-test-projects.ps1` fails the build if a project references `Microsoft.NET.Test.Sdk` without declaring a category, so this cannot be forgotten silently. + +Use an existing category from the list above where the tests fit. + +> [!WARNING] +> Introducing a *new* category also means adding it to the `test-category` matrix in [ci.yml](.github/workflows/ci.yml), otherwise no job ever selects it and the tests never run. -Each category is declared by the `` property in the test project and by the matching assembly-level `IncludeInTestCategory` attribute. CI uses the property to build and run only that category's projects; the attribute is the runtime safety net. Run `./tools/select-test-projects.ps1 -List` to see every category and the projects it selects. +Run `./tools/select-test-projects.ps1 -List` to see every category and the projects it selects. ## Security Configuration diff --git a/tools/select-test-projects.ps1 b/tools/select-test-projects.ps1 index f6c50c099a..d1f5704f6f 100644 --- a/tools/select-test-projects.ps1 +++ b/tools/select-test-projects.ps1 @@ -1,6 +1,6 @@ # Selects the test projects belonging to a test category, so that CI can build and run only those # rather than the whole solution. The category is declared by the property in each test -# project, which must match the Filter of the assembly-level IncludeIn*Tests attribute in that project. +# project. # # Writes the selected project paths to $GITHUB_OUTPUT as 'test-projects', and generates an MSBuild # traversal project so that `dotnet build` can build the whole selection in one graph. @@ -22,6 +22,7 @@ param( $ErrorActionPreference = 'Stop' $projectsByCategory = @{} +$uncategorized = New-Object Collections.Generic.List[String] # -Filter rather than -Include: the provider applies it during traversal, which is dramatically faster # on a tree that already contains bin/obj output. @@ -29,6 +30,12 @@ Get-ChildItem -Path src -Filter '*.csproj' -Recurse -File | ForEach-Object { $testCategory = (Select-Xml -Path $_.FullName -XPath "/Project/PropertyGroup/TestCategory").Node.InnerText if ([string]::IsNullOrWhiteSpace($testCategory)) { + # A project referencing the test SDK but declaring no category would never be selected by any + # job, so its tests would silently not run rather than fail. Catch that here instead. + $isTestProject = $null -ne (Select-Xml -Path $_.FullName -XPath "/Project/ItemGroup/PackageReference[@Include='Microsoft.NET.Test.Sdk']") + if ($isTestProject) { + $uncategorized.Add((Resolve-Path -Relative $_.FullName)) + } return } @@ -39,6 +46,12 @@ Get-ChildItem -Path src -Filter '*.csproj' -Recurse -File | ForEach-Object { $projectsByCategory[$testCategory].Add((Resolve-Path -Relative $_.FullName)) } +if ($uncategorized.Count -gt 0) { + # Not named $list: PowerShell variables are case-insensitive, so that would overwrite the [switch]$List parameter. + $formatted = ($uncategorized | Sort-Object | ForEach-Object { " $_" }) -join [Environment]::NewLine + throw "These test projects declare no , so CI would never build or run them:$([Environment]::NewLine)$formatted" +} + if ($List) { foreach ($key in $projectsByCategory.Keys | Sort-Object) { Write-Output "$key" From 73c2d31182da41bf36c8fcf2b1838bb3d55ea4b1 Mon Sep 17 00:00:00 2001 From: John Simons Date: Mon, 10 Aug 2026 10:36:31 +1000 Subject: [PATCH 10/12] Removed unnecessary comments --- .github/workflows/ci.yml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40d85d42cc..4984f5fb40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,7 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest] - # Categories are declared by the property in each test project. Default and - # RabbitMQ are split because each was a single job running several slow assemblies back to back. + # Categories are declared by the property in each test project. test-category: [ DefaultCore, DefaultAudit, DefaultMonitoring, SqlServer, SqlServerPersistence, AzureServiceBus, RabbitMQClassicConventional, RabbitMQClassicDirect, RabbitMQQuorumConventional, RabbitMQQuorumDirect, AzureStorageQueues, MSMQ, SQS, PrimaryRavenAcceptance, PrimaryRavenPersistence, PostgreSql, PostgreSqlPersistence, IBMMQ ] include: - os: windows-latest @@ -46,15 +45,9 @@ jobs: - name: Select test projects id: select run: ./tools/select-test-projects.ps1 -Category ${{ matrix.test-category }} - # ServiceControlInstaller.Packaging is only in the DefaultCore category's build closure, and its - # zip targets fail without a self-contained server in deploy/. Tests never read it: - # RavenDB.Embedded supplies its own server into each test project's output directory. - name: Download RavenDB Server if: matrix.test-category == 'DefaultCore' run: ./tools/download-ravendb-server.ps1 - # Backgrounded so that it overlaps the infrastructure steps below. Those steps are an ordered - # chain (Setup WSL provisions the Docker host the database actions rely on), so they stay in the - # foreground and the job joins the build at the `wait` before running tests. - name: Build id: build background: true @@ -72,7 +65,8 @@ jobs: # there is an issue with az cli and python 3.14, so for now we need to pin it # once the issue is resolved it should be able to be re-floated - # https://github.com/Azure/azure-cli/issues/32980 + # https://github.com/Azure/azure-cli/issues/32980. + # This can be removed once https://github.com/Azure/azure-cli/issues/32869 is supported. - name: Set Python 3.13 (Linux) if: matrix.os-name == 'Linux' && matrix.test-category == 'AzureServiceBus' uses: actions/setup-python@v5 @@ -136,10 +130,7 @@ jobs: $connectString = "AccessKeyId=${{ secrets.AWS_ACCESS_KEY_ID }};SecretAccessKey=${{ secrets.AWS_SECRET_ACCESS_KEY }};Region=${{ secrets.AWS_REGION }};QueueNamePrefix=GHA-${{ github.run_id }}" echo "ServiceControl_TransportTests_SQS_ConnectionString=$connectString" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append # Everything above provisions local containers, so it overlaps the build freely. The Azure steps - # below create real cloud resources, so they sit behind the wait: a background step only fails the - # job at the next wait that includes it, so without this a commit that cannot compile would still - # create a Service Bus namespace or storage account and then rely on teardown to remove it. If the - # runner is killed that teardown never runs, so nothing is created until the build is known good. + # below create real cloud resources, so they sit behind the wait too. - name: Wait for build wait: build - name: Azure login @@ -169,9 +160,6 @@ jobs: PARTICULARSOFTWARE_LICENSE: ${{ secrets.LICENSETEXT }} AZURE_ACI_CREDENTIALS: ${{ secrets.AZURE_ACI_CREDENTIALS }} - # The test matrix only builds each category's own dependency closure, so this job is what keeps - # projects outside every closure (HealthCheckApp, Particular.PlatformSample.ServiceControl, - # LegacyArtifacts) compiling. It runs alongside the matrix and is not on its critical path. compile: name: ${{ matrix.os-name }}-Compile runs-on: ${{ matrix.os }} From 72c6bc864c3bbce195aa4572a82b9a3df619b123 Mon Sep 17 00:00:00 2001 From: John Simons Date: Tue, 11 Aug 2026 08:01:36 +1000 Subject: [PATCH 11/12] Remove Setup IBM MQ GitHub Action This dedicated action is no longer required, indicating a consolidation or replacement of messaging service setup within CI workflows. --- .github/actions/setup-ibmmq/action.yml | 93 -------------------------- .github/workflows/ci.yml | 4 +- 2 files changed, 2 insertions(+), 95 deletions(-) delete mode 100644 .github/actions/setup-ibmmq/action.yml diff --git a/.github/actions/setup-ibmmq/action.yml b/.github/actions/setup-ibmmq/action.yml deleted file mode 100644 index 7f581ad722..0000000000 --- a/.github/actions/setup-ibmmq/action.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Setup IBM MQ -description: > - Runs IBM MQ in a Linux container for the duration of the job. On Linux runners the container runs - directly through Docker. On Windows runners it runs inside the WSL2 Docker host provisioned by - Particular/setup-wsl-action, which must run first, because the IBM MQ image is Linux-only and Windows - runners can otherwise only run Windows containers. - -inputs: - connection-string-name: - description: The name of the environment variable to fill with the IBM MQ connection string. - required: true - queue-manager: - description: The name of the queue manager to create. - required: false - default: QM1 - admin-password: - description: The password for the admin user. - required: false - default: passw0rd - image: - description: The IBM MQ container image to run. - required: false - default: icr.io/ibm-messaging/mq:latest - -runs: - using: composite - steps: - - name: Run IBM MQ container - shell: pwsh - env: - CONNECTION_STRING_NAME: ${{ inputs.connection-string-name }} - QUEUE_MANAGER: ${{ inputs.queue-manager }} - ADMIN_PASSWORD: ${{ inputs.admin-password }} - IMAGE: ${{ inputs.image }} - run: | - $ErrorActionPreference = 'Stop' - - $onWindows = $Env:RUNNER_OS -eq 'Windows' - - if ($onWindows) { - if (-not $Env:WSL_TOOLS_MODULE_PATH) { - throw "WSL_TOOLS_MODULE_PATH is not set. Particular/setup-wsl-action must run before this action on Windows." - } - Import-Module $Env:WSL_TOOLS_MODULE_PATH - } - - function Invoke-Docker([string]$Arguments) { - if ($onWindows) { Invoke-Wsl -CheckExitCode -Command "docker $Arguments" } - else { Invoke-Expression "docker $Arguments" } - } - - $containerName = 'ibmmq' - - Write-Output "Starting $($Env:IMAGE) as $containerName" - Invoke-Docker ("run --name $containerName --detach --publish 1414:1414 --publish 9443:9443 " + - "--health-cmd dspmq --health-interval 10s --health-timeout 5s --health-retries 10 --health-start-period 30s " + - "-e LICENSE=accept -e MQ_QMGR_NAME=$($Env:QUEUE_MANAGER) -e MQ_ADMIN_PASSWORD=$($Env:ADMIN_PASSWORD) " + - $Env:IMAGE) - - # On Windows the queue manager listens inside the WSL VM, reachable on its gateway address. - $mqHost = if ($onWindows) { $Env:WSL_IP } else { 'localhost' } - - # Docker's health check runs dspmq inside the container, which reports the queue manager - # process rather than whether the listener is reachable through the WSL port forward. Probe - # the endpoint the tests actually connect to instead, the way setup-postgres-action does. - function Test-Endpoint([string]$TargetHost, [int]$Port) { - $client = [System.Net.Sockets.TcpClient]::new() - try { return $client.ConnectAsync($TargetHost, $Port).Wait(5000) -and $client.Connected } - catch { return $false } - finally { $client.Dispose() } - } - - Write-Output "::group::Waiting for IBM MQ to accept connections on ${mqHost}:1414" - - $deadline = (Get-Date).AddMinutes(5) - while (-not (Test-Endpoint $mqHost 1414)) { - if ((Get-Date) -gt $deadline) { - # Out-String because the WSL and native paths differ in whether they hand back a string - # or a collection of output lines. - $status = (Invoke-Docker "inspect --format '{{.State.Status}} {{.State.Health.Status}}' $containerName" | Out-String).Trim() - Write-Output "Container state: $status" - Invoke-Docker "ps --all --filter name=$containerName" - Invoke-Docker "logs $containerName" - throw "IBM MQ did not accept connections on ${mqHost}:1414 within 5 minutes." - } - Start-Sleep -Seconds 2 - } - - Write-Output "::endgroup::" - $connectionString = "mq://admin:$($Env:ADMIN_PASSWORD)@${mqHost}:1414/$($Env:QUEUE_MANAGER)?channel=DEV.ADMIN.SVRCONN&topicprefix=DEV" - - Write-Output "IBM MQ is healthy on $mqHost" - Write-Output "$($Env:CONNECTION_STRING_NAME)=$connectionString" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4984f5fb40..39eb314637 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: # environment variables on both runners. - name: Setup WSL if: startsWith(matrix.test-category, 'RabbitMQ') || contains(fromJSON('["SqlServer", "SqlServerPersistence", "PostgreSql", "PostgreSqlPersistence", "IBMMQ"]'), matrix.test-category) - uses: Particular/setup-wsl-action@v1.1.0 + uses: Particular/setup-wsl-action@v1.2.0 with: # The action defaults to 4GB. The runner has 16GB and the build runs concurrently with the # container starting up, so give the VM real headroom. @@ -115,7 +115,7 @@ jobs: registry-username: ${{ secrets.DOCKERHUB_USERNAME }} registry-password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Setup IBM MQ - uses: ./.github/actions/setup-ibmmq + uses: Particular/setup-ibmmq-action@75b6087748fcdaef474574ca1fbe2b440141f07f if: matrix.test-category == 'IBMMQ' with: connection-string-name: ServiceControl_TransportTests_IBMMQ_ConnectionString From bb6f0a1ba0c68ff2d1192707f9fc9293c4134a7f Mon Sep 17 00:00:00 2001 From: John Simons Date: Tue, 11 Aug 2026 08:57:47 +1000 Subject: [PATCH 12/12] Centralize FakeTimeProvider and adjust timestamp precision Moves the `FakeTimeProvider` to a common test context and introduces a `StorableUtcNow` method. This ensures the `FakeTimeProvider` is initialized with a timestamp that respects database precision limits (e.g., PostgreSQL's microsecond precision), preventing mismatches and flakiness in persistence tests when comparing stored timestamps. --- .../PersistenceTestsContext.cs | 3 --- .../PersistenceTestsContext.cs | 3 --- .../EFCore/PersistenceTestsContext.cs | 13 +++++++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/ServiceControl.Persistence.Tests.PostgreSql/PersistenceTestsContext.cs b/src/ServiceControl.Persistence.Tests.PostgreSql/PersistenceTestsContext.cs index b99a86b59b..30bc5ae750 100644 --- a/src/ServiceControl.Persistence.Tests.PostgreSql/PersistenceTestsContext.cs +++ b/src/ServiceControl.Persistence.Tests.PostgreSql/PersistenceTestsContext.cs @@ -11,7 +11,6 @@ namespace ServiceControl.Persistence.Tests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Time.Testing; using Npgsql; using ServiceControl.Persistence.EFCore.Abstractions; using ServiceControl.Persistence.EFCore.Infrastructure; @@ -22,8 +21,6 @@ public partial class PersistenceTestsContext : IPersistenceTestsContext string databaseName; string bodyStoragePath; - public FakeTimeProvider FakeTime { get; } = new(DateTimeOffset.UtcNow); - public async Task Setup(IHostApplicationBuilder hostBuilder) { databaseName = $"sc_test_{Guid.NewGuid():n}"; diff --git a/src/ServiceControl.Persistence.Tests.SqlServer/PersistenceTestsContext.cs b/src/ServiceControl.Persistence.Tests.SqlServer/PersistenceTestsContext.cs index 412ff6364a..5ebccb8177 100644 --- a/src/ServiceControl.Persistence.Tests.SqlServer/PersistenceTestsContext.cs +++ b/src/ServiceControl.Persistence.Tests.SqlServer/PersistenceTestsContext.cs @@ -11,7 +11,6 @@ namespace ServiceControl.Persistence.Tests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Time.Testing; using ServiceControl.Persistence.EFCore.Abstractions; using ServiceControl.Persistence.EFCore.Infrastructure; @@ -21,8 +20,6 @@ public partial class PersistenceTestsContext : IPersistenceTestsContext string databaseName; string bodyStoragePath; - public FakeTimeProvider FakeTime { get; } = new(DateTimeOffset.UtcNow); - public async Task Setup(IHostApplicationBuilder hostBuilder) { databaseName = $"sc_test_{Guid.NewGuid():n}"; diff --git a/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs b/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs index f7b0de3b11..5744d6ec3b 100644 --- a/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs +++ b/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs @@ -11,11 +11,24 @@ namespace ServiceControl.Persistence.Tests; using EFCore.Infrastructure; using MessageFailures; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Time.Testing; using NServiceBus; using Operations; public partial class PersistenceTestsContext { + public FakeTimeProvider FakeTime { get; } = new(StorableUtcNow()); + + // PostgreSQL timestamps only keep microseconds, so a clock seeded straight from + // DateTimeOffset.UtcNow carries a 100ns tick that no longer compares equal once a + // timestamp stamped from it has been through the database. + static DateTimeOffset StorableUtcNow() + { + var now = DateTimeOffset.UtcNow; + + return now.AddTicks(-(now.Ticks % TimeSpan.TicksPerMillisecond)); + } + static async Task InsertFailedMessagesDirect(IServiceProvider serviceProvider, FailedMessage[] messages) { await using var scope = serviceProvider.CreateAsyncScope();