diff --git a/build/ci-build.yml b/build/ci-build.yml
index 48cd58c8..16491336 100644
--- a/build/ci-build.yml
+++ b/build/ci-build.yml
@@ -21,6 +21,10 @@ parameters:
- name: 'Package.Version.ManualTrigger'
type: string
default: 'preview'
+ - name: azureServiceConnection
+ displayName: 'Azure service connection'
+ type: string
+ default: 'Azure Codit-Arcus Service Principal'
resources:
repositories:
@@ -30,8 +34,6 @@ resources:
endpoint: arcus-azure
variables:
- - group: 'Arcus Messaging - Integration Testing'
- - group: 'Arcus - GitHub Package Registry'
- group: 'Build Configuration'
- template: ./variables/build.yml
- template: ./variables/test.yml
@@ -93,35 +95,24 @@ stages:
dotnetSdkVersion: '$(DotNet.Sdk.Version)'
projectName: '$(Project).Tests.Unit'
- - stage: SelfContainingIntegrationTests
- displayName: Self-Containing Integration Tests
+ - stage: IntegrationTests
+ displayName: Integration Tests
dependsOn: Build
condition: succeeded()
- variables:
- - name: 'Arcus.Health.Port.Queue'
- value: '42063'
jobs:
- - job: SelfContainingIntegrationTests
- displayName: 'Run self-containing integration tests'
+ - job: RunIntegrationTests
+ displayName: 'Run integration tests'
pool:
vmImage: '$(Vm.Image)'
steps:
- - task: UseDotNet@2
- displayName: 'Import .NET Core SDK ($(DotNet.Sdk.PreviousVersion))'
- inputs:
- packageType: 'sdk'
- version: '$(DotNet.Sdk.PreviousVersion)'
- - template: test/run-integration-tests.yml@templates
+ - template: templates/integration-tests.yml
parameters:
- dotnetSdkVersion: '$(DotNet.Sdk.Version)'
- includePreviewVersions: $(DotNet.Sdk.IncludePreviewVersions)
- projectName: '$(Project).Tests.Integration'
- category: 'Integration'
+ azureServiceConnection: ${{ parameters.azureServiceConnection }}
- - stage: ReleaseToMyget
+ - stage: ReleaseToMyGet
displayName: 'Release to MyGet'
dependsOn:
- [SelfContainingIntegrationTests, UnitTests]
+ [IntegrationTests, UnitTests]
condition: succeeded()
jobs:
- job: PushToMyGet
diff --git a/build/deploy-test-resources.yml b/build/deploy-test-resources.yml
new file mode 100644
index 00000000..eb948b14
--- /dev/null
+++ b/build/deploy-test-resources.yml
@@ -0,0 +1,85 @@
+name: Arcus Messaging - Deploy test resources
+
+trigger: none
+pr: none
+
+parameters:
+ - name: azureServiceConnection
+ displayName: 'Azure service connection'
+ type: string
+ default: 'Azure Codit-Arcus Service Principal'
+ - name: resourceGroupName
+ displayName: 'Resource group name'
+ default: arcus-messaging-dev-we-rg
+ - name: keyVaultName
+ displayName: 'Key vault name'
+ default: 'arcus-messaging-kv'
+
+variables:
+ - template: ./variables/build.yml
+ - template: ./variables/test.yml
+
+resources:
+ repositories:
+ - repository: templates
+ type: github
+ name: arcus-azure/azure-devops-templates
+ endpoint: arcus-azure
+
+stages:
+ - stage: Deploy
+ jobs:
+ - job: DeployBicep
+ displayName: 'Deploy test resources'
+ pool:
+ vmImage: $(Vm.Image)
+ steps:
+ - task: AzureCLI@2
+ inputs:
+ azureSubscription: '${{ parameters.azureServiceConnection }}'
+ addSpnToEnvironment: true
+ scriptType: 'pscore'
+ scriptLocation: 'inlineScript'
+ inlineScript: |
+ az deployment sub create `
+ --location westeurope `
+ --template-file ./build/templates/resource-group.bicep `
+ --parameters resourceGroupName=$env:ARCUS_MESSAGING_RESOURCEGROUP_NAME `
+ --parameters location=westeurope
+
+ $objectId = (az ad sp show --id $env:servicePrincipalId | ConvertFrom-Json).id
+
+ az deployment group create `
+ --resource-group $env:ARCUS_MESSAGING_RESOURCEGROUP_NAME `
+ --template-file ./build/templates/test-resources.bicep `
+ --parameters serviceBusNamespace=$env:ARCUS_MESSAGING_SERVICEBUS_NAMESPACE `
+ --parameters eventHubsNamespace=$env:ARCUS_MESSAGING_EVENTHUBS_NAMESPACE `
+ --parameters storageAccountName=$env:ARCUS_MESSAGING_STORAGEACCOUNT_NAME `
+ --parameters keyVaultName=$env:ARCUS_MESSAGING_KEYVAULT_NAME `
+ --parameters servicePrincipal_objectId=$objectId
+
+ $accountKey = (az storage account keys list --account-name $env:ARCUS_MESSAGING_STORAGEACCOUNT_NAME | ConvertFrom-Json)[0].value
+ az keyvault secret set `
+ --name $env:ARCUS_MESSAGING_STORAGEACCOUNT_KEY_SECRETNAME `
+ --value $accountKey `
+ --vault-name ${{ parameters.keyVaultName }}
+
+ $serviceBusKeys = az servicebus namespace authorization-rule keys list `
+ --resource-group $env:ARCUS_MESSAGING_RESOURCEGROUP_NAME `
+ --namespace-name $env:ARCUS_MESSAGING_SERVICEBUS_NAMESPACE `
+ --authorization-rule-name 'RootManageSharedAccessKey' `
+ | ConvertFrom-Json
+ az keyvault secret set `
+ --name $env:ARCUS_MESSAGING_SERVICEBUS_CONNECTIONSTRING_SECRETNAME `
+ --value $serviceBusKeys.primaryConnectionString `
+ --vault-name ${{ parameters.keyVaultName }}
+
+ $eventHubsKeys = az eventhubs namespace authorization-rule keys list `
+ --resource-group $env:ARCUS_MESSAGING_RESOURCEGROUP_NAME `
+ --namespace-name $env:ARCUS_MESSAGING_EVENTHUBS_NAMESPACE `
+ --authorization-rule-name 'RootManageSharedAccessKey' `
+ | ConvertFrom-Json
+ az keyvault secret set `
+ --name $env:ARCUS_MESSAGING_EVENTHUBS_CONNECTIONSTRING_SECRETNAME `
+ --value $eventHubsKeys.primaryConnectionString `
+ --vault-name ${{ parameters.keyVaultName }}
\ No newline at end of file
diff --git a/build/nuget-release.yml b/build/nuget-release.yml
index 37bc5b0f..157d51b4 100644
--- a/build/nuget-release.yml
+++ b/build/nuget-release.yml
@@ -6,6 +6,10 @@ pr: none
parameters:
- name: 'Package.Version'
type: 'string'
+ - name: azureServiceConnection
+ displayName: 'Azure service connection'
+ type: string
+ default: 'Azure Codit-Arcus Service Principal'
resources:
repositories:
@@ -15,9 +19,6 @@ resources:
endpoint: arcus-azure
variables:
- - group: 'Arcus Messaging - Integration Testing'
- - group: 'Arcus Security - Integration Testing'
- - group: 'Arcus - GitHub Package Registry'
- group: 'Build Configuration'
- template: ./variables/build.yml
- template: ./variables/test.yml
@@ -79,35 +80,24 @@ stages:
dotnetSdkVersion: '$(DotNet.Sdk.Version)'
projectName: '$(Project).Tests.Unit'
- - stage: SelfContainingIntegrationTests
- displayName: Self-Containing Integration Tests
+ - stage: IntegrationTests
+ displayName: Integration Tests
dependsOn: Build
condition: succeeded()
- variables:
- - name: 'Arcus.Health.Port.Queue'
- value: '42063'
jobs:
- - job: SelfContainingIntegrationTests
- displayName: 'Run self-containing integration tests'
+ - job: RunIntegrationTests
+ displayName: 'Run integration tests'
pool:
vmImage: '$(Vm.Image)'
steps:
- - task: UseDotNet@2
- displayName: 'Import .NET Core SDK ($(DotNet.Sdk.PreviousVersion))'
- inputs:
- packageType: 'sdk'
- version: '$(DotNet.Sdk.PreviousVersion)'
- includePreviewVersions: $(DotNet.Sdk.IncludePreviewVersions)
- - template: test/run-integration-tests.yml@templates
+ - template: templates/integration-tests.yml
parameters:
- dotnetSdkVersion: '$(DotNet.Sdk.Version)'
- projectName: '$(Project).Tests.Integration'
- category: 'Integration'
+ azureServiceConnection: ${{ parameters.azureServiceConnection }}
- stage: Release
displayName: 'Release to NuGet.org'
dependsOn:
- [SelfContainingIntegrationTests , UnitTests]
+ [IntegrationTests , UnitTests]
condition: succeeded()
jobs:
- job: PushToNuGet
diff --git a/build/templates/build-and-run-az-func-container.yml b/build/templates/build-and-run-az-func-container.yml
deleted file mode 100644
index 2bb1261d..00000000
--- a/build/templates/build-and-run-az-func-container.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-parameters:
- containerName: ''
- projectName: ''
- imageName: ''
- imageTag: ''
- port: ''
- envVars: {}
-
-steps:
-- bash: |
- if [ -z "$IMAGE_NAME" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"imageName\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "$IMAGE_NAME" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"projectName\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "$CONTAINER_NAME" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"containerName\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "$BUILD_VERSION" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"imageTag\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "$PORT" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"port\""
- echo "##vso[task.complete result=Failed;]"
- fi
- env:
- CONTAINER_NAME: ${{ parameters.containerName }}
- PROJECT_NAME: ${{ parameters.projectName }}
- IMAGE_NAME: ${{ parameters.imageName }}
- BUILD_VERSION: ${{ parameters.imageTag }}
- PORT: ${{ parameters.port }}
- displayName: Check for required parameters in YAML template
-- task: qetza.replacetokens.replacetokens-task.replacetokens@3
- displayName: 'Replace integration test tokens in local.settings.json'
- inputs:
- rootDirectory: 'src/${{ parameters.projectName }}/'
- targetFiles: 'local.settings.json'
- encoding: 'auto'
- verbosity: 'detailed'
- writeBOM: true
- actionOnMissing: 'fail'
- keepToken: false
- tokenPrefix: '#{'
- tokenSuffix: '}#'
-- task: Docker@1
- displayName: 'Build Docker image ''${{ parameters.imageName }}'' for ''${{ parameters.projectName }}'''
- inputs:
- dockerFile: src/${{ parameters.projectName }}/Dockerfile
- imageName: '${{ parameters.imageName }}:${{ parameters.imageTag }}'
- useDefaultContext: false
- buildContext: src
-- task: Docker@1
- displayName: 'Run ''${{ parameters.imageName }}'' Docker image'
- inputs:
- command: 'Run an image'
- imageName: '${{ parameters.imageName }}:${{ parameters.imageTag }}'
- containerName: '${{ parameters.containerName }}'
- ports: '${{ parameters.port }}:${{ parameters.port }}'
- envVars: ${{ parameters.envVars }}
\ No newline at end of file
diff --git a/build/templates/build-and-run-worker-container.yml b/build/templates/build-and-run-worker-container.yml
deleted file mode 100644
index 6c8c6c67..00000000
--- a/build/templates/build-and-run-worker-container.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-parameters:
- containerName: ''
- projectName: ''
- imageName: ''
- imageTag: ''
- port: ''
- envVars: {}
-
-steps:
-- bash: |
- if [ -z "$IMAGE_NAME" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"imageName\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "$IMAGE_NAME" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"projectName\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "$CONTAINER_NAME" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"containerName\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "$BUILD_VERSION" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"imageTag\""
- echo "##vso[task.complete result=Failed;]"
- fi
- if [ -z "PORT" ]; then
- echo "##vso[task.logissue type=error;]Missing template parameter \"port\""
- echo "##vso[task.complete result=Failed;]"
- fi
- env:
- CONTAINER_NAME: ${{ parameters.containerName }}
- PROJECT_NAME: ${{ parameters.projectName }}
- IMAGE_NAME: ${{ parameters.imageName }}
- BUILD_VERSION: ${{ parameters.imageTag }}
- PORT: ${{ parameters.port }}
- displayName: Check for required parameters in YAML template
-- task: Docker@1
- displayName: 'Build Docker image ''${{ parameters.imageName }}'' for ''${{ parameters.projectName }}'''
- inputs:
- dockerFile: src/${{ parameters.projectName }}/Dockerfile
- imageName: '${{ parameters.imageName }}:${{ parameters.imageTag }}'
- useDefaultContext: false
- buildContext: src
-- task: Docker@1
- displayName: 'Run ''${{ parameters.imageName }}'' Docker image'
- inputs:
- command: 'Run an image'
- imageName: '${{ parameters.imageName }}:${{ parameters.imageTag }}'
- containerName: '${{ parameters.containerName }}'
- ports: '${{ parameters.port }}:${{ parameters.port }}'
- envVars: ${{ parameters.envVars }}
\ No newline at end of file
diff --git a/build/templates/integration-tests.yml b/build/templates/integration-tests.yml
new file mode 100644
index 00000000..4118ea84
--- /dev/null
+++ b/build/templates/integration-tests.yml
@@ -0,0 +1,44 @@
+parameters:
+ azureServiceConnection: ''
+
+steps:
+ - task: UseDotNet@2
+ displayName: 'Import .NET Core SDK ($(DotNet.Sdk.PreviousVersion))'
+ inputs:
+ packageType: 'sdk'
+ version: '$(DotNet.Sdk.PreviousVersion)'
+
+ - task: AzureCLI@2
+ displayName: 'Import secrets from Azure Key Vault'
+ inputs:
+ azureSubscription: '${{ parameters.azureServiceConnection }}'
+ addSpnToEnvironment: true
+ scriptType: 'pscore'
+ scriptLocation: 'inlineScript'
+ inlineScript: |
+ Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
+ Install-Module -Name Arcus.Scripting.DevOps -AllowClobber -MinimumVersion 1.3.0
+
+ $subscriptionId = (az account show | ConvertFrom-Json).id
+ $objectId = (az ad sp show --id $env:servicePrincipalId | ConvertFrom-Json).id
+ Set-AzDevOpsVariable -Name 'Arcus.Infra.SubscriptionId' -Value $subscriptionId
+ Set-AzDevOpsVariable -Name 'Arcus.Infra.TenantId' -Value $env:tenantId -AsSecret
+ Set-AzDevOpsVariable -Name 'Arcus.Infra.ServicePrincipal.ObjectId' -Value $objectId
+ Set-AzDevOpsVariable -Name 'Arcus.Infra.ServicePrincipal.ClientId' -Value $env:servicePrincipalId -AsSecret
+ Set-AzDevOpsVariable -Name 'Arcus.Infra.ServicePrincipal.ClientSecret' -Value $env:servicePrincipalKey -AsSecret
+
+ $accountKey = az keyvault secret show --name $env:ARCUS_MESSAGING_STORAGEACCOUNT_KEY_SECRETNAME --vault-name $env:ARCUS_MESSAGING_KEYVAULT_NAME | ConvertFrom-Json
+ Set-AzDevOpsVariable -Name 'Arcus.Messaging.StorageAccount.Key' -Value $accountKey.value -AsSecret
+
+ $serviceBusConnectionString = az keyvault secret show --name $env:ARCUS_MESSAGING_SERVICEBUS_CONNECTIONSTRING_SECRETNAME --vault-name $env:ARCUS_MESSAGING_KEYVAULT_NAME | ConvertFrom-Json
+ Set-AzDevOpsVariable -Name 'Arcus.Messaging.ServiceBus.ConnectionString' -Value $serviceBusConnectionString.value -AsSecret
+
+ $eventHubsConnectionString = az keyvault secret show --name $env:ARCUS_MESSAGING_EVENTHUBS_CONNECTIONSTRING_SECRETNAME --vault-name $env:ARCUS_MESSAGING_KEYVAULT_NAME | ConvertFrom-Json
+ Set-AzDevOpsVariable -Name 'Arcus.Messaging.EventHubs.ConnectionString' -Value $eventHubsConnectionString.value -AsSecret
+
+ - template: test/run-integration-tests.yml@templates
+ parameters:
+ dotnetSdkVersion: '$(DotNet.Sdk.Version)'
+ includePreviewVersions: $(DotNet.Sdk.IncludePreviewVersions)
+ projectName: '$(Project).Tests.Integration'
+ category: 'Integration'
\ No newline at end of file
diff --git a/build/templates/resource-group.bicep b/build/templates/resource-group.bicep
new file mode 100644
index 00000000..f21d0090
--- /dev/null
+++ b/build/templates/resource-group.bicep
@@ -0,0 +1,15 @@
+// Define the name of the resource group.
+param resourceGroupName string
+
+// Define the location for the deployment of the components.
+param location string
+
+targetScope='subscription'
+
+module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' = {
+ name: 'resourceGroupDeployment'
+ params: {
+ name: resourceGroupName
+ location: location
+ }
+}
diff --git a/build/templates/run-docker-integration-tests.yml b/build/templates/run-docker-integration-tests.yml
deleted file mode 100644
index cc913a5e..00000000
--- a/build/templates/run-docker-integration-tests.yml
+++ /dev/null
@@ -1,145 +0,0 @@
-steps:
-- task: DownloadPipelineArtifact@2
- displayName: 'Download build artifacts'
- inputs:
- artifact: 'Build'
- path: '$(Build.SourcesDirectory)'
-- template: build-and-run-worker-container.yml
- parameters:
- projectName: 'Arcus.Messaging.Tests.Workers.ServiceBus.Queue'
- containerName: '$(Images.ServiceBus.Queue)'
- imageName: '$(Images.ServiceBus.Queue)'
- imageTag: $(Build.BuildId)
- port: '$(Arcus.Health.Port.Queue)'
- envVars: |
- ARCUS_HEALTH_PORT=$(Arcus.Health.Port.Queue)
- EVENTGRID_TOPIC_URI=$(Arcus.TestInfra.EventGrid.Topic.Uri)
- EVENTGRID_AUTH_KEY=$(Arcus.TestInfra.EventGrid.Auth.Key)
- ARCUS_SERVICEBUS_CONNECTIONSTRING=$(Arcus.ServiceBus.Docker.ConnectionStringWithQueue)
-- template: build-and-run-worker-container.yml
- parameters:
- projectName: 'Arcus.Messaging.Tests.Workers.ServiceBus.Topic'
- containerName: '$(Images.ServiceBus.Topic)'
- imageName: '$(Images.ServiceBus.Topic)'
- imageTag: $(Build.BuildId)
- port: '$(Arcus.Health.Port.Topic)'
- envVars: |
- ARCUS_HEALTH_PORT=$(Arcus.Health.Port.Topic)
- EVENTGRID_TOPIC_URI=$(Arcus.TestInfra.EventGrid.Topic.Uri)
- EVENTGRID_AUTH_KEY=$(Arcus.TestInfra.EventGrid.Auth.Key)
- ARCUS_SERVICEBUS_CONNECTIONSTRING=$(Arcus.ServiceBus.Docker.ConnectionStringWithTopic)
-# .NET 8 not available yet for Azure Functions in-process
-# - template: build-and-run-az-func-container.yml
-# parameters:
-# projectName: 'Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Queue'
-# containerName: '$(Images.AzureFunction.ServiceBus.Queue)'
-# imageName: '$(Images.AzureFunction.ServiceBus.Queue)'
-# imageTag: '$(Build.BuildId)'
-# port: '$(Arcus.AzureFunctions.Queue.Port)'
-# envVars: |
-# ARCUS_EVENTGRID_TOPIC_URI=$(Arcus.TestInfra.EventGrid.Topic.Uri)
-# ARCUS_EVENTGRID_AUTH_KEY=$(Arcus.TestInfra.EventGrid.Auth.Key)
-# ARCUS_SERVICEBUS_CONNECTIONSTRING=$(Arcus.ServiceBus.Docker.AzureFunctions.NamespaceConnectionString)
-- template: build-and-run-az-func-container.yml
- parameters:
- projectName: 'Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Topic'
- containerName: '$(Images.AzureFunction.ServiceBus.Topic)'
- imageName: '$(Images.AzureFunction.ServiceBus.Topic)'
- imageTag: '$(Build.BuildId)'
- port: '$(Arcus.AzureFunctions.Topic.Port)'
- envVars: |
- ARCUS_EVENTGRID_TOPIC_URI=$(Arcus.TestInfra.EventGrid.Topic.Uri)
- ARCUS_EVENTGRID_AUTH_KEY=$(Arcus.TestInfra.EventGrid.Auth.Key)
- ARCUS_SERVICEBUS_CONNECTIONSTRING=$(Arcus.ServiceBus.Docker.AzureFunctions.ConnectionStringWithTopic)
- FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
-- template: build-and-run-az-func-container.yml
- parameters:
- projectName: 'Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs'
- containerName: '$(Images.AzureFunction.Isolated.EventHubs)'
- imageName: '$(Images.AzureFunction.Isolated.EventHubs)'
- imageTag: '$(Build.BuildId)'
- port: '$(Arcus.AzureFunctions.Isolated.EventHubs.Port)'
- envVars: |
- EVENTGRID_TOPIC_URI=$(Arcus.TestInfra.EventGrid.Topic.Uri)
- EVENTGRID_AUTH_KEY=$(Arcus.TestInfra.EventGrid.Auth.Key)
- EventHubsConnectionString=$(Arcus.EventHubs.ConnectionString)
- AzureWebJobsStorage=$(Arcus.EventHubs.BlobStorage.StorageAccountConnectionString)
- FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
-# .NET 8 not available yet for Azure Functions in-process
-# - template: build-and-run-az-func-container.yml
-# parameters:
-# projectName: 'Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs.InProcess'
-# containerName: '$(Images.AzureFunction.InProcess.EventHubs)'
-# imageName: '$(Images.AzureFunction.InProcess.EventHubs)'
-# imageTag: '$(Build.BuildId)'
-# port: '$(Arcus.AzureFunctions.InProcess.EventHubs.Port)'
-# envVars: |
-# EVENTGRID_TOPIC_URI=$(Arcus.TestInfra.EventGrid.Topic.Uri)
-# EVENTGRID_AUTH_KEY=$(Arcus.TestInfra.EventGrid.Auth.Key)
-# EventHubsConnectionString=$(Arcus.EventHubs.ConnectionString)
-# AzureWebJobsStorage=$(Arcus.EventHubs.BlobStorage.StorageAccountConnectionString)
-- template: build-and-run-worker-container.yml
- parameters:
- projectName: 'Arcus.Messaging.Tests.Workers.EventHubs'
- containerName: '$(Images.EventHubs)'
- imageName: '$(Images.EventHubs)'
- imageTag: $(Build.BuildId)
- port: '$(Arcus.Health.Port.EventHubs)'
- envVars: |
- ARCUS_HEALTH_PORT=$(Arcus.Health.Port.EventHubs)
- EVENTGRID_TOPIC_URI=$(Arcus.TestInfra.EventGrid.Topic.Uri)
- EVENTGRID_AUTH_KEY=$(Arcus.TestInfra.EventGrid.Auth.Key)
- EVENTHUBS_NAME=$(Arcus.EventHubs.Docker.EventHubsName)
- EVENTHUBS_CONNECIONSTRING=$(Arcus.EventHubs.ConnectionString)
- BLOBSTORAGE_CONTAINERNAME=$(Arcus.EventHubs.Docker.BlobStorage.ContainerName)
- STORAGEACCOUNT_CONNECTIONSTRING=$(Arcus.EventHubs.BlobStorage.StorageAccountConnectionString)
-- template: test/run-integration-tests.yml@templates
- parameters:
- dotnetSdkVersion: '$(DotNet.Sdk.Version)'
- includePreviewVersions: $(DotNet.Sdk.IncludePreviewVersions)
- projectName: '$(Project).Tests.Integration'
- category: 'Docker'
-- task: PowerShell@2
- displayName: 'Get Docker container logs for Service Bus Queue worker project'
- inputs:
- targetType: 'inline'
- script: 'docker logs $(Images.ServiceBus.Queue)'
- condition: failed()
-- task: PowerShell@2
- displayName: 'Get Docker container logs for Service Bus Topic worker project'
- inputs:
- targetType: 'inline'
- script: 'docker logs $(Images.ServiceBus.Topic)'
- condition: failed()
-# .NET 8 not available yet for Azure Functions in-process
-# - task: PowerShell@2
-# displayName: 'Get Docker container logs for Azure Functions Service Bus Queue project'
-# inputs:
-# targetType: 'inline'
-# script: 'docker logs $(Images.AzureFunction.ServiceBus.Queue)'
-# condition: failed()
-- task: PowerShell@2
- displayName: 'Get Docker container logs for Azure Functions Service Bus Topic project'
- inputs:
- targetType: 'inline'
- script: 'docker logs $(Images.AzureFunction.ServiceBus.Topic)'
- condition: failed()
-# .NET 8 not available yet for Azure Functions in-process
-# - task: PowerShell@2
-# displayName: 'Get Docker container logs for Azure Functions EventHubs project'
-# inputs:
-# targetType: 'inline'
-# script: 'docker logs $(Images.AzureFunction.InProcess.EventHubs)'
-# condition: failed()
-- task: PowerShell@2
- displayName: 'Get Docker container logs for Azure Functions EventHubs (isolated) project'
- inputs:
- targetType: 'inline'
- script: 'docker logs $(Images.AzureFunction.Isolated.EventHubs)'
- condition: failed()
-- task: PowerShell@2
- displayName: 'Get Docker container logs for Azure EventHubs worker project'
- inputs:
- targetType: 'inline'
- script: 'docker logs $(Images.EventHubs)'
- condition: failed()
\ No newline at end of file
diff --git a/build/templates/test-resources.bicep b/build/templates/test-resources.bicep
new file mode 100644
index 00000000..5b84144c
--- /dev/null
+++ b/build/templates/test-resources.bicep
@@ -0,0 +1,89 @@
+// Define the location for the deployment of the components.
+param location string = resourceGroup().location
+
+// Define the name of the single Azure Service bus namespace.
+param serviceBusNamespace string
+
+// Define the name of the single Azure EventHubs namespace.
+param eventHubsNamespace string
+
+// Define the name of the storage account that will be created.
+param storageAccountName string
+
+// Define the name of the Key Vault.
+param keyVaultName string
+
+// Define the Service Principal ID that needs access full access to the deployed resource group.
+param servicePrincipal_objectId string
+
+module serviceBus 'br/public:avm/res/service-bus/namespace:0.8.0' = {
+ name: 'serviceBusDeployment'
+ params: {
+ name: serviceBusNamespace
+ location: location
+ skuObject: {
+ name: 'Standard'
+ }
+ disableLocalAuth: false
+ roleAssignments: [
+ {
+ principalId: servicePrincipal_objectId
+ roleDefinitionIdOrName: 'Azure Service Bus Data Owner'
+ }
+ ]
+ }
+}
+
+module hubs 'br/public:avm/res/event-hub/namespace:0.7.0' = {
+ name: 'eventHubsDeployment'
+ params: {
+ name: eventHubsNamespace
+ location: location
+ skuName: 'Basic'
+ disableLocalAuth: false
+ roleAssignments: [
+ {
+ principalId: servicePrincipal_objectId
+ roleDefinitionIdOrName: 'Azure Event Hubs Data Owner'
+ }
+ ]
+ }
+}
+
+module storageAccount 'br/public:avm/res/storage/storage-account:0.9.1' = {
+ name: 'storageAccountDeployment'
+ params: {
+ name: storageAccountName
+ location: location
+ allowBlobPublicAccess: true
+ publicNetworkAccess: 'Enabled'
+ networkAcls: {
+ bypass: 'AzureServices'
+ defaultAction: 'Allow'
+ ipRules: []
+ virtualNetworkRules: []
+ }
+ roleAssignments: [
+ {
+ principalId: servicePrincipal_objectId
+ roleDefinitionIdOrName: 'Storage Blob Data Contributor'
+ }
+ ]
+ }
+}
+
+module vault 'br/public:avm/res/key-vault/vault:0.6.1' = {
+ name: 'vaultDeployment'
+ params: {
+ name: keyVaultName
+ location: location
+ roleAssignments: [
+ {
+ principalId: servicePrincipal_objectId
+ roleDefinitionIdOrName: 'Key Vault Secrets officer'
+ }
+ ]
+ secrets: [
+ ]
+ }
+}
diff --git a/build/variables/test.yml b/build/variables/test.yml
index 5f4d819c..4ac2a076 100644
--- a/build/variables/test.yml
+++ b/build/variables/test.yml
@@ -1,20 +1,12 @@
variables:
- Images.ServiceBus.Queue: 'workers-service-bus-queue'
- Images.ServiceBus.Topic: 'workers-service-bus-topic'
- Images.AzureFunction.ServiceBus.Queue: 'runtimes-az-func-service-bus-queue'
- Images.AzureFunction.ServiceBus.Topic: 'runtimes-az-func-service-bus-topic'
- Images.AzureFunction.Isolated.EventHubs: 'runtimes-az-func-eventhubs-isolated'
- Images.AzureFunction.InProcess.EventHubs: 'runtimes-az-func-eventhubs-inprocess'
- Images.EventHubs: 'workers-eventhubs'
- Arcus.Health.Port.Queue: '42063'
- Arcus.Health.Port.Topic: '42064'
- Arcus.AzureFunctions.Queue.Port: '42065'
- Arcus.AzureFunctions.Topic.Port: '42068'
- Arcus.AzureFunctions.Isolated.EventHubs.Port: '42067'
- Arcus.AzureFunctions.InProcess.EventHubs.Port: '42069'
- Arcus.Health.Port.EventHubs: '42066'
- Arcus.EventHubs.SelfContained.EventHubsName: 'orders'
- Arcus.EventHubs.Docker.EventHubsName: 'orders-docker'
- Arcus.EventHubs.Docker.AzureFunctions.Isolated.EventHubsName: 'orders-az-func-docker'
- Arcus.EventHubs.Docker.AzureFunctions.InProcess.EventHubsName: 'orders-az-func-inprocess-docker'
- Arcus.EventHubs.Docker.BlobStorage.ContainerName: 'eventhubs-docker'
+ Arcus.Messaging.ResourceGroup.Name: 'arcus-messaging-dev-we-rg'
+ Arcus.Messaging.KeyVault.Name: 'arcus-messaging-kv'
+
+ Arcus.Messaging.StorageAccount.Name: 'arcusmessagingstorage'
+ Arcus.Messaging.StorageAccount.Key.SecretName: 'Arcus-Messaging-StorageAccount-Key'
+
+ Arcus.Messaging.ServiceBus.Namespace: 'arcus-messaging-servicebus'
+ Arcus.Messaging.ServiceBus.ConnectionString.SecretName: 'Arcus-Messaging-ServiceBus-ConnectionString'
+
+ Arcus.Messaging.EventHubs.Namespace: 'arcus-messaging-eventhubs'
+ Arcus.Messaging.EventHubs.ConnectionString.SecretName: 'Arcus-Messaging-EventHubs-ConnectionString'
\ No newline at end of file
diff --git a/src/Arcus.Messaging.Pumps.Abstractions/MessagePump.cs b/src/Arcus.Messaging.Pumps.Abstractions/MessagePump.cs
index c2353dff..b1e471f5 100644
--- a/src/Arcus.Messaging.Pumps.Abstractions/MessagePump.cs
+++ b/src/Arcus.Messaging.Pumps.Abstractions/MessagePump.cs
@@ -82,7 +82,7 @@ protected MessagePump(IConfiguration configuration, IServiceProvider serviceProv
/// Exception that occurred
protected virtual Task HandleReceiveExceptionAsync(Exception receiveException)
{
- Logger.LogCritical(receiveException, "Unable to process message from {EntityPath} with client {ClientId}", EntityPath, ClientId);
+ Logger.LogCritical(receiveException, "Unable to process message from {EntityPath} with client {ClientId}: {Message}", EntityPath, ClientId, receiveException.Message);
return Task.CompletedTask;
}
diff --git a/src/Arcus.Messaging.Pumps.EventHubs/AzureEventHubsMessagePump.cs b/src/Arcus.Messaging.Pumps.EventHubs/AzureEventHubsMessagePump.cs
index 172ed9ea..446a60d9 100644
--- a/src/Arcus.Messaging.Pumps.EventHubs/AzureEventHubsMessagePump.cs
+++ b/src/Arcus.Messaging.Pumps.EventHubs/AzureEventHubsMessagePump.cs
@@ -200,9 +200,14 @@ public override async Task StopProcessingMessagesAsync(CancellationToken cancell
try
{
Logger.LogTrace("Stopping Azure EventHubs message pump '{JobId}' on '{ConsumerGroup}/{EventHubsName}' in '{Namespace}'", JobId, ConsumerGroup, EventHubName , Namespace);
- await _eventProcessor.StopProcessingAsync(cancellationToken);
- _eventProcessor.ProcessEventAsync -= ProcessMessageAsync;
- _eventProcessor.ProcessErrorAsync -= ProcessErrorAsync;
+
+ if (_eventProcessor != null)
+ {
+ await _eventProcessor.StopProcessingAsync(cancellationToken);
+ _eventProcessor.ProcessEventAsync -= ProcessMessageAsync;
+ _eventProcessor.ProcessErrorAsync -= ProcessErrorAsync;
+ }
+
Logger.LogInformation("Azure EventHubs message pump '{JobId}' on '{ConsumerGroup}/{EventHubsName}' in '{Namespace}' stopped: {Time}", JobId, ConsumerGroup, EventHubName , Namespace, DateTimeOffset.UtcNow);
}
catch (Exception exception)
diff --git a/src/Arcus.Messaging.Tests.Core/Arcus.Messaging.Tests.Core.csproj b/src/Arcus.Messaging.Tests.Core/Arcus.Messaging.Tests.Core.csproj
index e7280c09..86479dc3 100644
--- a/src/Arcus.Messaging.Tests.Core/Arcus.Messaging.Tests.Core.csproj
+++ b/src/Arcus.Messaging.Tests.Core/Arcus.Messaging.Tests.Core.csproj
@@ -14,8 +14,6 @@
-
-
diff --git a/src/Arcus.Messaging.Tests.Core/Events/v1/OrderCreatedEvent.cs b/src/Arcus.Messaging.Tests.Core/Events/v1/OrderCreatedEvent.cs
deleted file mode 100644
index a7fca240..00000000
--- a/src/Arcus.Messaging.Tests.Core/Events/v1/OrderCreatedEvent.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Arcus.EventGrid.Contracts;
-using Arcus.Messaging.Abstractions;
-using Newtonsoft.Json;
-
-namespace Arcus.Messaging.Tests.Core.Events.v1
-{
- public class OrderCreatedEvent : EventGridEvent
- {
- private const string DefaultDataVersion = "1";
- private const string DefaultEventType = "Arcus.Samples.Orders.OrderCreated";
-
- public OrderCreatedEvent(string eventId, string orderId, int amount, string articleNumber, string customerName, MessageCorrelationInfo correlationInfo)
- : base(eventId, $"customer/{customerName}",
- new OrderCreatedEventData(orderId, amount, articleNumber, customerName, correlationInfo), DefaultDataVersion,
- DefaultEventType)
- {
- }
-
- [JsonConstructor]
- private OrderCreatedEvent()
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/Arcus.Messaging.Tests.Integration/Arcus.Messaging.Tests.Integration.csproj b/src/Arcus.Messaging.Tests.Integration/Arcus.Messaging.Tests.Integration.csproj
index 8767a6c8..0ffc6f30 100644
--- a/src/Arcus.Messaging.Tests.Integration/Arcus.Messaging.Tests.Integration.csproj
+++ b/src/Arcus.Messaging.Tests.Integration/Arcus.Messaging.Tests.Integration.csproj
@@ -6,25 +6,22 @@
-
-
-
-
-
+
+
+
-
+
+
+
-
-
-
diff --git a/src/Arcus.Messaging.Tests.Integration/AzureFunctions/EventHubs/AzureFunctionsEventHubsTriggerDockerTests.cs b/src/Arcus.Messaging.Tests.Integration/AzureFunctions/EventHubs/AzureFunctionsEventHubsTriggerDockerTests.cs
deleted file mode 100644
index f2c111c9..00000000
--- a/src/Arcus.Messaging.Tests.Integration/AzureFunctions/EventHubs/AzureFunctionsEventHubsTriggerDockerTests.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Arcus.Messaging.Tests.Core.Correlation;
-using Arcus.Messaging.Tests.Core.Events.v1;
-using Arcus.Messaging.Tests.Core.Generators;
-using Arcus.Messaging.Tests.Core.Messages.v1;
-using Arcus.Messaging.Tests.Integration.Fixture;
-using Arcus.Messaging.Tests.Integration.MessagePump.EventHubs;
-using Arcus.Messaging.Tests.Integration.MessagePump.Fixture;
-using Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus;
-using Azure.Messaging.EventHubs;
-using Newtonsoft.Json;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Arcus.Messaging.Tests.Integration.AzureFunctions.EventHubs
-{
- [Collection("Docker")]
- [Trait("Category", "Docker")]
- public class AzureFunctionsEventHubsTriggerDockerTests : DockerServiceBusIntegrationTest
- {
- private readonly TestConfig _config;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public AzureFunctionsEventHubsTriggerDockerTests(ITestOutputHelper outputWriter) : base(outputWriter)
- {
- _config = TestConfig.Create();
- }
-
- [Theory]
- [InlineData(IntegrationTestType.DockerAzureFunctionsInProcess, Skip = ".NET 8 is not supported yet for in-process Azure Functions")]
- [InlineData(IntegrationTestType.DockerAzureFunctionsIsolated)]
- public async Task EventHubsMessageFunction_PublishesEventDataMessage_MessageSuccessfullyProcessed(IntegrationTestType integrationType)
- {
- // Arrange
- var traceParent = TraceParent.Generate();
- Order order = OrderGenerator.Generate();
- EventData expected = new EventData(JsonConvert.SerializeObject(order)).WithDiagnosticId(traceParent);
-
- EventHubsConfig eventHubs = _config.GetEventHubsConfig();
- string eventHubsName = eventHubs.GetEventHubsName(integrationType);
- var producer = new TestEventHubsMessageProducer(eventHubs.EventHubsConnectionString, eventHubsName);
-
- await using (var consumer = await TestServiceBusMessageEventConsumer.StartNewAsync(_config, Logger))
- {
- // Act
- await producer.ProduceAsync(expected);
-
- // Assert
- OrderCreatedEventData orderCreatedEventData = consumer.ConsumeOrderEventForW3C(traceParent.TransactionId);
- Assert.NotNull(orderCreatedEventData);
- Assert.NotNull(orderCreatedEventData.CorrelationInfo);
- Assert.Equal(order.Id, orderCreatedEventData.Id);
- Assert.Equal(order.Amount, orderCreatedEventData.Amount);
- Assert.Equal(order.ArticleNumber, orderCreatedEventData.ArticleNumber);
- Assert.Equal(traceParent.TransactionId, orderCreatedEventData.CorrelationInfo.TransactionId);
- Assert.Equal(traceParent.OperationParentId, orderCreatedEventData.CorrelationInfo.OperationParentId);
- Assert.NotNull(orderCreatedEventData.CorrelationInfo.OperationId);
- }
- }
- }
-}
diff --git a/src/Arcus.Messaging.Tests.Integration/AzureFunctions/ServiceBus/AzureFunctionServiceBusTriggerDockerTests.cs b/src/Arcus.Messaging.Tests.Integration/AzureFunctions/ServiceBus/AzureFunctionServiceBusTriggerDockerTests.cs
deleted file mode 100644
index 1461ff5a..00000000
--- a/src/Arcus.Messaging.Tests.Integration/AzureFunctions/ServiceBus/AzureFunctionServiceBusTriggerDockerTests.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Arcus.Messaging.Tests.Core.Correlation;
-using Arcus.Messaging.Tests.Core.Events.v1;
-using Arcus.Messaging.Tests.Core.Generators;
-using Arcus.Messaging.Tests.Core.Messages.v1;
-using Arcus.Messaging.Tests.Integration.Fixture;
-using Arcus.Messaging.Tests.Integration.MessagePump.Fixture;
-using Azure.Messaging.ServiceBus;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Arcus.Messaging.Tests.Integration.AzureFunctions.ServiceBus
-{
- [Collection("Docker")]
- [Trait("Category", "Docker")]
- public class AzureFunctionServiceBusTriggerDockerTests : DockerServiceBusIntegrationTest
- {
- ///
- /// Initializes a new instance of the class.
- ///
- public AzureFunctionServiceBusTriggerDockerTests(ITestOutputHelper outputWriter) : base(outputWriter)
- {
- }
-
- [Theory]
- [InlineData("Arcus:ServiceBus:Docker:AzureFunctions:ConnectionStringWithQueue", Skip = ".NET 8 is not supported yet for in-process Azure Functions")]
- [InlineData("Arcus:ServiceBus:Docker:AzureFunctions:ConnectionStringWithTopic")]
- public async Task ServiceBusTrigger_PublishServiceBusMessage_MessageSuccessfullyProcessed(string connectionStringKey)
- {
- // Arrange
- var traceParent = TraceParent.Generate();
- Order order = OrderGenerator.Generate();
- var orderMessage = new ServiceBusMessage(BinaryData.FromObjectAsJson(order)).WithDiagnosticId(traceParent);
-
- // Act
- await SenderOrderToServiceBusAsync(orderMessage, connectionStringKey);
-
- // Assert
- OrderCreatedEventData orderEventData = ReceiveOrderFromEventGrid(traceParent.TransactionId);
- Assert.NotNull(orderEventData.CorrelationInfo);
- Assert.Equal(order.Id, orderEventData.Id);
- Assert.Equal(order.Amount, orderEventData.Amount);
- Assert.Equal(order.ArticleNumber, orderEventData.ArticleNumber);
- Assert.Equal(traceParent.TransactionId, orderEventData.CorrelationInfo.TransactionId);
- Assert.Equal(traceParent.OperationParentId, orderEventData.CorrelationInfo.OperationParentId);
- Assert.NotNull(orderEventData.CorrelationInfo.OperationId);
- }
- }
-}
diff --git a/src/Arcus.Messaging.Tests.Integration/EventHubs/AzureClientFactoryBuilderExtensionsTests.cs b/src/Arcus.Messaging.Tests.Integration/EventHubs/AzureClientFactoryBuilderExtensionsTests.cs
index 9236a986..4a1ca5da 100644
--- a/src/Arcus.Messaging.Tests.Integration/EventHubs/AzureClientFactoryBuilderExtensionsTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/EventHubs/AzureClientFactoryBuilderExtensionsTests.cs
@@ -4,12 +4,10 @@
using Arcus.Messaging.Tests.Core.Generators;
using Arcus.Messaging.Tests.Core.Messages.v1;
using Arcus.Messaging.Tests.Integration.Fixture;
-using Arcus.Messaging.Tests.Integration.MessagePump.EventHubs;
-using Arcus.Testing.Logging;
+using Arcus.Messaging.Tests.Integration.MessagePump;
+using Arcus.Testing;
using Azure.Messaging.EventHubs;
using Azure.Messaging.EventHubs.Producer;
-using Azure.Storage.Blobs;
-using Microsoft.Azure.Management.ServiceBus.Models;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -21,25 +19,28 @@
namespace Arcus.Messaging.Tests.Integration.EventHubs
{
- public class AzureClientFactoryBuilderExtensionsTests : IAsyncLifetime
+ public class AzureClientFactoryBuilderExtensionsTests : IClassFixture, IAsyncLifetime
{
private readonly TestConfig _config;
private readonly EventHubsConfig _eventHubsConfig;
private readonly ILogger _logger;
- private TemporaryBlobStorageContainer _blobStorageContainer;
+ private TemporaryManagedIdentityConnection _connection;
+ private TemporaryBlobContainer _blobStorageContainer;
///
/// Initializes a new instance of the class.
///
- public AzureClientFactoryBuilderExtensionsTests(ITestOutputHelper outputWriter)
+ public AzureClientFactoryBuilderExtensionsTests(EventHubsEntityFixture fixture, ITestOutputHelper outputWriter)
{
_config = TestConfig.Create();
_logger = new XunitTestLogger(outputWriter);
- _eventHubsConfig = _config.GetEventHubsConfig();
+ _eventHubsConfig = _config.GetEventHubs();
+
+ EventHubsName = fixture.HubName;
}
- private string EventHubsName => _eventHubsConfig.GetEventHubsName(IntegrationTestType.SelfContained);
+ private string EventHubsName { get; }
[Fact]
public async Task AddEventHubProducerClientWithNamespace_SendEvent_Succeeds()
@@ -47,7 +48,7 @@ public async Task AddEventHubProducerClientWithNamespace_SendEvent_Succeeds()
// Arrange
var services = new ServiceCollection();
var connectionStringSecretName = "MyConnectionString";
- EventHubsConfig eventHubsConfig = _config.GetEventHubsConfig();
+ EventHubsConfig eventHubsConfig = _config.GetEventHubs();
services.AddSecretStore(stores => stores.AddInMemory(connectionStringSecretName, eventHubsConfig.EventHubsConnectionString));
// Act
@@ -120,9 +121,7 @@ private async Task RetryAssertUntilServiceBusMessageIsAvailableAsync(Action, IAsyncLifetime
{
private const string DependencyIdPattern = @"with ID [a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}";
@@ -29,19 +28,22 @@ public class EventHubProducerClientExtensionsTests : IAsyncLifetime
private readonly EventHubsConfig _eventHubsConfig;
private readonly ILogger _logger;
- private TemporaryBlobStorageContainer _blobStorageContainer;
+ private TemporaryManagedIdentityConnection _connection;
+ private TemporaryBlobContainer _blobStorageContainer;
///
/// Initializes a new instance of the class.
///
- public EventHubProducerClientExtensionsTests(ITestOutputHelper outputWriter)
+ public EventHubProducerClientExtensionsTests(EventHubsEntityFixture fixture, ITestOutputHelper outputWriter)
{
_config = TestConfig.Create();
- _eventHubsConfig = _config.GetEventHubsConfig();
+ _eventHubsConfig = _config.GetEventHubs();
_logger = new XunitTestLogger(outputWriter);
+
+ EventHubsName = fixture.HubName;
}
- private string EventHubsName => _eventHubsConfig.GetEventHubsName(IntegrationTestType.SelfContained);
+ private string EventHubsName { get; }
[Fact]
public async Task SendMessage_WithMessageCorrelation_TracksMessage()
@@ -51,7 +53,8 @@ public async Task SendMessage_WithMessageCorrelation_TracksMessage()
MessageCorrelationInfo correlation = GenerateMessageCorrelationInfo();
var logger = new InMemoryLogger();
- await using (var client = new EventHubProducerClient(_eventHubsConfig.EventHubsConnectionString, EventHubsName))
+
+ await using (EventHubProducerClient client = _eventHubsConfig.GetProducerClient(EventHubsName))
{
await client.SendAsync(new [] { order }, correlation, logger);
}
@@ -82,7 +85,7 @@ public async Task SendMessage_WithCustomOptions_TracksMessage()
string key1 = Guid.NewGuid().ToString(), value1 = Guid.NewGuid().ToString();
string key2 = Guid.NewGuid().ToString(), value2 = Guid.NewGuid().ToString();
- await using (var client = new EventHubProducerClient(_eventHubsConfig.EventHubsConnectionString, EventHubsName))
+ await using (EventHubProducerClient client = _eventHubsConfig.GetProducerClient(EventHubsName))
{
await client.SendAsync(new [] { order }, correlation, logger, options =>
{
@@ -171,9 +174,7 @@ private async Task RetryAssertUntilServiceBusMessageIsAvailableAsync(Action
+ {
+ opt.TopicSubscription = TopicSubscription.Automatic;
+ opt.AutoComplete = true;
+ });
+ }
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/DockerServiceBusIntegrationTest.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/DockerServiceBusIntegrationTest.cs
deleted file mode 100644
index 7539e677..00000000
--- a/src/Arcus.Messaging.Tests.Integration/Fixture/DockerServiceBusIntegrationTest.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Arcus.EventGrid;
-using Arcus.EventGrid.Contracts;
-using Arcus.EventGrid.Parsers;
-using Arcus.EventGrid.Testing.Infrastructure.Hosts.ServiceBus;
-using Arcus.Messaging.Tests.Core.Events.v1;
-using Arcus.Messaging.Tests.Core.Messages.v1;
-using Arcus.Messaging.Tests.Workers.ServiceBus.Fixture;
-using Azure.Messaging.ServiceBus;
-using CloudNative.CloudEvents;
-using GuardNet;
-using Microsoft.Extensions.Configuration;
-using Newtonsoft.Json;
-using Xunit;
-using Xunit.Abstractions;
-using Xunit.Sdk;
-
-namespace Arcus.Messaging.Tests.Integration.Fixture
-{
- ///
- /// Represents the general setup an teardown of an integration test, using an external running Docker container to interact with.
- ///
- [Trait("Category", "Docker")]
- public abstract class DockerServiceBusIntegrationTest : IntegrationTest, IAsyncLifetime
- {
- private ServiceBusEventConsumerHost _serviceBusEventConsumerHost;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The logger instance to write diagnostic messages during the interaction with Azure Service Bus instances.
- protected DockerServiceBusIntegrationTest(ITestOutputHelper outputWriter) : base(outputWriter)
- {
- }
-
- ///
- /// Called immediately after the class has been created, before it is used.
- ///
- public async Task InitializeAsync()
- {
- var connectionString = Configuration.GetValue("Arcus:Infra:ServiceBus:ConnectionString");
- var topicName = Configuration.GetValue("Arcus:Infra:ServiceBus:TopicName");
-
- var serviceBusEventConsumerHostOptions = new ServiceBusEventConsumerHostOptions(topicName, connectionString);
- _serviceBusEventConsumerHost = await ServiceBusEventConsumerHost.StartAsync(serviceBusEventConsumerHostOptions, Logger);
- }
-
- ///
- /// Sends an message to an Azure Service Bus instance, located at the given .
- ///
- /// The Service Bus message representation of an .
- /// The connection string key where the should be send to.
- /// Thrown when the is null.
- /// Thrown when the is blank.
- public async Task SenderOrderToServiceBusAsync(ServiceBusMessage message, string connectionStringKey)
- {
- Guard.NotNull(message, nameof(message), "Requires an Azure Service Bus message representation of an 'Order' to send it to an Azure Service Bus instance");
- Guard.NotNullOrWhitespace(connectionStringKey, nameof(connectionStringKey), "Requires an Azure Service Bus connection string to send the 'Order' message to");
-
- var connectionString = Configuration.GetValue(connectionStringKey);
- ServiceBusConnectionStringProperties serviceBusConnectionString = ServiceBusConnectionStringProperties.Parse(connectionString);
-
- await using (var client = new ServiceBusClient(connectionString))
- await using (ServiceBusSender messageSender = client.CreateSender(serviceBusConnectionString.EntityPath))
- {
- await messageSender.SendMessageAsync(message);
- }
- }
-
- ///
- /// Receives the previously send message as an published event on Azure Event Grid.
- ///
- /// The transaction ID to identity the correct published .
- /// Thrown when the is blank.
- ///
- /// Thrown when the received message event doesn't conform with the expected structure of an published event.
- ///
- public OrderCreatedEventData ReceiveOrderFromEventGrid(string transactionId)
- {
- Guard.NotNullOrWhitespace(transactionId, nameof(transactionId), "Requires an operation ID to uniquely identity the published 'Order' message");
-
- CloudEvent cloudEvent = _serviceBusEventConsumerHost.GetReceivedEvent((CloudEvent ev) =>
- {
- string json = ev.Data.ToString();
- var eventData = JsonConvert.DeserializeObject(json, new MessageCorrelationInfoJsonConverter());
-
- return eventData.CorrelationInfo.TransactionId == transactionId;
- }, timeout: TimeSpan.FromMinutes(1));
-
- var json = cloudEvent.Data.ToString();
- Assert.NotNull(json);
- var orderCreatedEventData = JsonConvert.DeserializeObject(json, new MessageCorrelationInfoJsonConverter());
- Assert.NotNull(orderCreatedEventData);
-
- return orderCreatedEventData;
- }
-
- ///
- /// Called when an object is no longer needed. Called just before
- /// if the class also implements that.
- ///
- public async Task DisposeAsync()
- {
- await _serviceBusEventConsumerHost.StopAsync();
- }
- }
-}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/EventHubsConfig.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/EventHubsConfig.cs
index 5a9fa59a..99f56c3c 100644
--- a/src/Arcus.Messaging.Tests.Integration/Fixture/EventHubsConfig.cs
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/EventHubsConfig.cs
@@ -1,5 +1,9 @@
-using System;
-using GuardNet;
+using Arcus.Testing;
+using Azure.Core;
+using Azure.Messaging.EventHubs;
+using Azure.Messaging.EventHubs.Producer;
+using Azure.ResourceManager.EventHubs;
+using Azure.Storage.Blobs;
namespace Arcus.Messaging.Tests.Integration.Fixture
{
@@ -8,72 +12,53 @@ namespace Arcus.Messaging.Tests.Integration.Fixture
///
public class EventHubsConfig
{
- private readonly string _selfContainedEventHubsName, _dockerEventHubsName, _dockerAzureFunctionsIsolatedEventHubsName, _dockerAzureFunctionsInProcessEventHubsName;
-
///
/// Initializes a new instance of the class.
///
- /// The name of the Azure EventHubs instance used in the slf-contained integration tests.
- /// The name of the Azure EventHubs instance used in the docker integration tests.
- /// The name of the Azure EventHubs instance used in the docker Azure Functions isolated integration tests.
- /// The name of the Azure EventHubs instance used in the docker Azure Functions in-process integration tests.
- /// The connection string to connect to the on Azure.
- ///
- /// The connection string used to connect to the related Azure Blob storage instance where checkpoints are stored and load balancing done.
- ///
- ///
- /// Thrown when the , , or the is blank.
- ///
public EventHubsConfig(
- string selfContainedEventHubsName,
- string dockerEventHubsName,
- string dockerAzureFunctionsIsolatedEventHubsName,
- string dockerAzureFunctionsInProcessEventHubsName,
- string connectionString,
- string storageConnectionString)
+ ServicePrincipal servicePrincipal,
+ string subscriptionId,
+ string resourceGroupName,
+ string eventHubsNamespace,
+ string connectionString,
+ StorageAccountConfig storageAccount)
{
- Guard.NotNullOrWhitespace(selfContainedEventHubsName, nameof(selfContainedEventHubsName), "Requires a non-blank name for the Azure EventHubs instance used in the self-contained integration tests");
- Guard.NotNullOrWhitespace(dockerEventHubsName, nameof(dockerEventHubsName), "Requires a non-blank name for the Azure EventHubs instance used in the docker integration tests");
- Guard.NotNullOrWhitespace(dockerAzureFunctionsIsolatedEventHubsName, nameof(dockerAzureFunctionsIsolatedEventHubsName), "Requires a non-blank name for the Azure EventHubs instance used in the docker Azure Functions integration tests");
- Guard.NotNullOrWhitespace(dockerAzureFunctionsInProcessEventHubsName, nameof(dockerAzureFunctionsInProcessEventHubsName), "Requires a non-blank name for the Azure EventHubs instance used in the docker Azure Functions integration tests");
- Guard.NotNullOrWhitespace(connectionString, nameof(connectionString), "Requires a non-blank connection string to connect to the Azure EventHubs instance");
- Guard.NotNullOrWhitespace(storageConnectionString, nameof(storageConnectionString), "Requires a non-blank connection string to connect to the related Azure Blob storage instance for Azure EventHubs");
-
- _selfContainedEventHubsName = selfContainedEventHubsName;
- _dockerEventHubsName = dockerEventHubsName;
- _dockerAzureFunctionsIsolatedEventHubsName = dockerAzureFunctionsIsolatedEventHubsName;
- _dockerAzureFunctionsInProcessEventHubsName = dockerAzureFunctionsInProcessEventHubsName;
+ ServicePrincipal = servicePrincipal;
+ ResourceId = EventHubsNamespaceResource.CreateResourceIdentifier(subscriptionId, resourceGroupName, eventHubsNamespace);
+ HostName = $"{eventHubsNamespace}.servicebus.windows.net";
+ Storage = storageAccount;
EventHubsConnectionString = connectionString;
- StorageConnectionString = storageConnectionString;
}
- ///
- /// Gets the connection string to connect to the Azure EventHubs instance.
- ///
+ public ServicePrincipal ServicePrincipal { get; }
+ public ResourceIdentifier ResourceId { get; }
+ public string HostName { get; }
+ public StorageAccountConfig Storage { get; }
public string EventHubsConnectionString { get; }
- ///
- /// Gets the connection string to connect to the related Azure Blob storage instance that is prepared for the integration tests.
- ///
- public string StorageConnectionString { get; }
+ public EventHubProducerClient GetProducerClient(string name)
+ {
+ return new EventHubProducerClient(HostName, name, ServicePrincipal.GetCredential());
+ }
- ///
- /// Gets the configured Azure EventHubs name used during the integration tests.
- ///
- /// The type of test that request the name of the Azure EventHubs instance.
- /// When the is outside the bounds of the enumeration.
- public string GetEventHubsName(IntegrationTestType type)
+ public EventProcessorClient GetProcessorClient(string name, BlobContainerClient checkpointStore)
+ {
+ return new EventProcessorClient(checkpointStore, "$Default", HostName, name, ServicePrincipal.GetCredential());
+ }
+ }
+
+ public static class EventHubsTestConfigExtensions
+ {
+ public static EventHubsConfig GetEventHubs(this TestConfig config)
{
- switch (type)
- {
- case IntegrationTestType.SelfContained: return _selfContainedEventHubsName;
- case IntegrationTestType.DockerWorker: return _dockerEventHubsName;
- case IntegrationTestType.DockerAzureFunctionsIsolated: return _dockerAzureFunctionsIsolatedEventHubsName;
- case IntegrationTestType.DockerAzureFunctionsInProcess: return _dockerAzureFunctionsInProcessEventHubsName;
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown integration test type");
- }
+ return new EventHubsConfig(
+ config.GetServicePrincipal(),
+ config.GetSubscriptionId(),
+ config.GetResourceGroupName(),
+ config["Arcus:EventHubs:Namespace"],
+ config["Arcus:EventHubs:ConnectionString"],
+ config.GetStorageAccount());
}
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/IntegrationTestType.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/IntegrationTestType.cs
index 00d2ec35..26a01288 100644
--- a/src/Arcus.Messaging.Tests.Integration/Fixture/IntegrationTestType.cs
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/IntegrationTestType.cs
@@ -3,8 +3,5 @@
public enum IntegrationTestType
{
SelfContained,
- DockerWorker,
- DockerAzureFunctionsIsolated,
- DockerAzureFunctionsInProcess
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/KeyVaultConfig.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/KeyVaultConfig.cs
index ac855cd5..bb655db9 100644
--- a/src/Arcus.Messaging.Tests.Integration/Fixture/KeyVaultConfig.cs
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/KeyVaultConfig.cs
@@ -1,4 +1,6 @@
using System;
+using Arcus.Testing;
+using Azure.Security.KeyVault.Secrets;
using GuardNet;
namespace Arcus.Messaging.Tests.Integration.Fixture
@@ -8,6 +10,8 @@ namespace Arcus.Messaging.Tests.Integration.Fixture
///
public class KeyVaultConfig
{
+ private readonly ServicePrincipal _servicePrincipal;
+
///
/// Initializes a new instance of the class.
///
@@ -27,6 +31,15 @@ public KeyVaultConfig(string vaultUri, string secretName, KeyVaultEventEndpoint
SecretNewVersionCreated = secretNewVersionCreated;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public KeyVaultConfig(ServicePrincipal servicePrincipal, string vaultName)
+ {
+ _servicePrincipal = servicePrincipal;
+ VaultUri = $"https://{vaultName}.vault.azure.net";
+ }
+
///
/// Gets the URI referencing the Azure Key Vault instance.
///
@@ -41,5 +54,20 @@ public KeyVaultConfig(string vaultUri, string secretName, KeyVaultEventEndpoint
/// Gets the endpoint where Azure Key Vault events will be available, including 'Secret new version created' event.
///
public KeyVaultEventEndpoint SecretNewVersionCreated { get; }
+
+ public SecretClient GetClient()
+ {
+ return new SecretClient(new Uri(VaultUri), _servicePrincipal.GetCredential());
+ }
+ }
+
+ public static class KeyVaultConfigExtensions
+ {
+ public static KeyVaultConfig GetKeyVault(this TestConfig config)
+ {
+ return new KeyVaultConfig(
+ config.GetServicePrincipal(),
+ config["Arcus:KeyVault:Name"]);
+ }
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/ServiceBusConfig.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/ServiceBusConfig.cs
new file mode 100644
index 00000000..fa7a2ed7
--- /dev/null
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/ServiceBusConfig.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Arcus.Testing;
+using Azure.Core;
+using Azure.Messaging.ServiceBus;
+using Azure.Messaging.ServiceBus.Administration;
+using Azure.ResourceManager.ServiceBus;
+
+namespace Arcus.Messaging.Tests.Integration.Fixture
+{
+ public class ServiceBusConfig
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ServiceBusConfig(
+ ServicePrincipal servicePrincipal,
+ string subscriptionId,
+ string resourceGroupName,
+ string @namespace,
+ string namespaceConnectionString = null)
+ {
+ ServicePrincipal = servicePrincipal;
+ NamespaceConnectionString = namespaceConnectionString;
+ HostName = $"{@namespace}.servicebus.windows.net";
+ ResourceId = ServiceBusNamespaceResource.CreateResourceIdentifier(subscriptionId, resourceGroupName, @namespace);
+ }
+
+ public string HostName { get; }
+ public ResourceIdentifier ResourceId { get; }
+ public ServicePrincipal ServicePrincipal { get; }
+ public string NamespaceConnectionString { get; }
+
+ public ServiceBusClient GetClient()
+ {
+ return new ServiceBusClient(HostName, ServicePrincipal.GetCredential());
+ }
+
+ public ServiceBusAdministrationClient GetAdminClient()
+ {
+ return new ServiceBusAdministrationClient(HostName, ServicePrincipal.GetCredential());
+ }
+ }
+
+ public static class ServiceBusConfigExtensions
+ {
+ public static ServiceBusConfig GetServiceBus(this TestConfig config)
+ {
+ return new ServiceBusConfig(
+ config.GetServicePrincipal(),
+ config.GetSubscriptionId(),
+ config.GetResourceGroupName(),
+ config["Arcus:ServiceBus:Namespace"],
+ config["Arcus:ServiceBus:ConnectionString"]);
+ }
+ }
+}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/ServicePrincipal.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/ServicePrincipal.cs
index a57b0971..626a6d08 100644
--- a/src/Arcus.Messaging.Tests.Integration/Fixture/ServicePrincipal.cs
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/ServicePrincipal.cs
@@ -1,5 +1,7 @@
using System;
-using GuardNet;
+using Arcus.Testing;
+using Azure.Core;
+using Azure.Identity;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace Arcus.Messaging.Tests.Integration.Fixture
@@ -12,57 +14,63 @@ public class ServicePrincipal
///
/// Initializes a new instance of the class.
///
- /// The ID of the client application.
- /// The secret of the client application.
- /// Thrown when the or is blank.
- public ServicePrincipal(string clientId, string clientSecret)
+ public ServicePrincipal(string tenantId, string objectId, string clientId, string clientSecret)
{
- Guard.NotNullOrWhitespace(clientId, nameof(clientId), "Requires a non-blank Azure service principal client ID");
- Guard.NotNullOrWhitespace(clientSecret, nameof(clientSecret), "Requires a non-blank Azure service principal client secret");
-
+ TenantId = tenantId;
+ ObjectId = Guid.Parse(objectId);
ClientId = clientId;
ClientSecret = clientSecret;
}
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The ID of the client application.
- /// The secret of the client application.
- /// The key to the secret of the client application.
- /// Thrown when the , or is blank.
- public ServicePrincipal(string clientId, string clientSecret, string clientSecretKey)
- {
- Guard.NotNullOrWhitespace(clientId, nameof(clientId), "Requires a non-blank Azure service principal client ID");
- Guard.NotNullOrWhitespace(clientSecret, nameof(clientSecret), "Requires a non-blank Azure service principal client secret");
- Guard.NotNullOrWhitespace(clientSecretKey, nameof(clientSecretKey), "Requires a non-blank secret Azure Key Vault key where the Azure service principal client secret is located");
- ClientId = clientId;
- ClientSecret = clientSecret;
- ClientSecretKey = clientSecretKey;
- }
+ public string TenantId { get; }
///
/// Gets the ID of the client application.
///
public string ClientId { get; }
+ public Guid ObjectId { get; }
+
///
/// Gets the secret of the client application.
///
public string ClientSecret { get; }
///
- /// Gets the key to the client secret of the client application.
+ /// Creates an instance that combines the service principal information into an instance.
+ ///
+ public TokenCredential GetCredential()
+ {
+ return new ClientSecretCredential(TenantId, ClientId, ClientSecret);
+ }
+ }
+
+ public static class ServicePrincipalConfigExtensions
+ {
+ ///
+ /// Gets the service principal that can authenticate with the Azure resources used in these integration tests.
///
- public string ClientSecretKey { get; }
+ ///
+ public static ServicePrincipal GetServicePrincipal(this TestConfig config)
+ {
+ var servicePrincipal = new ServicePrincipal(
+ tenantId: GetTenantId(config),
+ objectId: config["Arcus:Infra:ServicePrincipal:ObjectId"],
+ clientId: config["Arcus:Infra:ServicePrincipal:ClientId"],
+ clientSecret: config["Arcus:Infra:ServicePrincipal:ClientSecret"]);
+
+ return servicePrincipal;
+ }
///
- /// Creates an instance that combines the service principal information into an instance.
+ /// Gets the ID of the current tenant where the Azure resources used in these integration tests are located.
///
- public ClientCredential CreateCredentials()
+ public static string GetTenantId(this TestConfig config)
{
- return new ClientCredential(ClientId, ClientSecret);
+ return config["Arcus:Infra:TenantId"];
}
+
+ public static string GetSubscriptionId(this TestConfig config) => config["Arcus:Infra:SubscriptionId"];
+ public static string GetResourceGroupName(this TestConfig config) => config["Arcus:Infra:ResourceGroup:Name"];
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/StorageAccountConfig.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/StorageAccountConfig.cs
new file mode 100644
index 00000000..5a128652
--- /dev/null
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/StorageAccountConfig.cs
@@ -0,0 +1,30 @@
+using Arcus.Testing;
+
+namespace Arcus.Messaging.Tests.Integration.Fixture
+{
+ public class StorageAccountConfig
+ {
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public StorageAccountConfig(string name, string key)
+ {
+ Name = name;
+ ConnectionString = $"DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};EndpointSuffix=core.windows.net";
+ }
+
+ public string Name { get; }
+ public string ConnectionString { get; }
+ }
+
+ public static class StorageAccountConfigExtensions
+ {
+ public static StorageAccountConfig GetStorageAccount(this TestConfig config)
+ {
+ return new StorageAccountConfig(
+ config["Arcus:StorageAccount:Name"],
+ config["Arcus:StorageAccount:Key"]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryEventHubEntity.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryEventHubEntity.cs
new file mode 100644
index 00000000..ddf4c34b
--- /dev/null
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryEventHubEntity.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Threading.Tasks;
+using Azure;
+using Azure.ResourceManager;
+using Azure.ResourceManager.EventHubs;
+using Azure.ResourceManager.EventHubs.Models;
+using Microsoft.Extensions.Logging;
+
+namespace Arcus.Messaging.Tests.Integration.Fixture
+{
+ ///
+ /// Represents a temporary hub on an Azure EventHubs namespace that is gets deleted when the instance gets disposed.
+ ///
+ public class TemporaryEventHubEntity : IAsyncDisposable
+ {
+ private readonly EventHubsNamespaceResource _eventHubsNamespace;
+ private readonly ILogger _logger;
+
+ private TemporaryEventHubEntity(string name, EventHubsNamespaceResource eventHubsNamespace, ILogger logger)
+ {
+ Name = name;
+ _eventHubsNamespace = eventHubsNamespace;
+ _logger = logger;
+ }
+
+ public string Name { get; }
+
+ public static async Task CreateAsync(string name, EventHubsConfig config, ILogger logger)
+ {
+ var client = new ArmClient(config.ServicePrincipal.GetCredential());
+
+ EventHubsNamespaceResource eventHubsNamespace = client.GetEventHubsNamespaceResource(config.ResourceId);
+
+ logger.LogTrace("[Test] create EventHub '{HubName}'", name);
+ await eventHubsNamespace.GetEventHubs()
+ .CreateOrUpdateAsync(WaitUntil.Completed, name, new EventHubData
+ {
+ PartitionCount = 1,
+ RetentionDescription = new RetentionDescription
+ {
+ CleanupPolicy = CleanupPolicyRetentionDescription.Delete,
+ RetentionTimeInHours = 1,
+ }
+ });
+
+ return new TemporaryEventHubEntity(name, eventHubsNamespace, logger);
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously.
+ ///
+ /// A task that represents the asynchronous dispose operation.
+ public async ValueTask DisposeAsync()
+ {
+ _logger.LogTrace("[Test] delete EventHub '{HubName}'", Name);
+ EventHubResource hub = await _eventHubsNamespace.GetEventHubAsync(Name);
+ await hub.DeleteAsync(WaitUntil.Started);
+
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryKeyVaultSecret.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryKeyVaultSecret.cs
new file mode 100644
index 00000000..f7b3fa9c
--- /dev/null
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryKeyVaultSecret.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Threading.Tasks;
+using Azure.Security.KeyVault.Secrets;
+using Microsoft.Extensions.Logging;
+
+namespace Arcus.Messaging.Tests.Integration.Fixture
+{
+ public class TemporaryKeyVaultSecret : IAsyncDisposable
+ {
+ private readonly string _secretName;
+ private readonly SecretClient _client;
+ private readonly ILogger _logger;
+
+ private TemporaryKeyVaultSecret(string secretName, SecretClient client, ILogger logger)
+ {
+ _secretName = secretName;
+ _client = client;
+ _logger = logger;
+ }
+
+ public string Name => _secretName;
+
+ public static async Task CreateAsync(string secretName, string secretValue, KeyVaultConfig config, ILogger logger)
+ {
+ SecretClient secretClient = config.GetClient();
+
+ logger.LogTrace("[Test] create Key vault secret '{SecretName}'", secretName);
+ await secretClient.SetSecretAsync(secretName, secretValue);
+
+ return new TemporaryKeyVaultSecret(secretName, secretClient, logger);
+ }
+
+ public async Task UpdateSecretAsync(string secretValue)
+ {
+ _logger.LogTrace("[Test] update Key vault secret '{SecretName}'", _secretName);
+ await _client.SetSecretAsync(_secretName, secretValue);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ _logger.LogTrace("[Test] delete Key vault secret '{SecretName}'", _secretName);
+ await _client.StartDeleteSecretAsync(_secretName);
+ }
+ }
+}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryManagedIdentityConnection.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryManagedIdentityConnection.cs
index ceac7493..35a43a99 100644
--- a/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryManagedIdentityConnection.cs
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryManagedIdentityConnection.cs
@@ -1,4 +1,5 @@
using System;
+using Arcus.Testing;
using GuardNet;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryServiceBusEntity.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryServiceBusEntity.cs
new file mode 100644
index 00000000..d26942e4
--- /dev/null
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryServiceBusEntity.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Azure;
+using Azure.Identity;
+using Azure.Messaging.ServiceBus;
+using Azure.ResourceManager;
+using Azure.ResourceManager.ServiceBus;
+using Azure.ResourceManager.ServiceBus.Models;
+using Microsoft.Azure.EventGrid.Models;
+using Microsoft.Extensions.Logging;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Arcus.Messaging.Tests.Integration.Fixture
+{
+ public class TemporaryServiceBusEntity : IAsyncDisposable
+ {
+ private readonly ServiceBusEntityType _entityType;
+ private readonly ServiceBusNamespaceResource _ns;
+ private readonly ILogger _logger;
+
+ private TemporaryServiceBusEntity(ServiceBusEntityType entityType, string entityName, ServiceBusNamespaceResource @namespace, ILogger logger)
+ {
+ _entityType = entityType;
+ _ns = @namespace;
+ _logger = logger;
+
+ EntityName = entityName;
+ }
+
+ public string EntityName { get; }
+
+ public static async Task CreateAsync(ServiceBusEntityType entityType, string entityName, ServiceBusConfig serviceBus, ILogger logger)
+ {
+ var armClient = new ArmClient(serviceBus.ServicePrincipal.GetCredential());
+ ServiceBusNamespaceResource serviceBusNamespace = armClient.GetServiceBusNamespaceResource(serviceBus.ResourceId);
+
+ switch (entityType)
+ {
+ case ServiceBusEntityType.Queue:
+ logger.LogTrace("[Test] create Service bus queue '{EntityName}'", entityName);
+ await serviceBusNamespace.GetServiceBusQueues()
+ .CreateOrUpdateAsync(WaitUntil.Completed, entityName, new ServiceBusQueueData());
+ break;
+
+ case ServiceBusEntityType.Topic:
+ logger.LogTrace("[Test] create Service bus topic '{EntityName}'", entityName);
+ await serviceBusNamespace.GetServiceBusTopics()
+ .CreateOrUpdateAsync(WaitUntil.Completed, entityName, new ServiceBusTopicData());
+ break;
+
+ case ServiceBusEntityType.Unknown:
+ default:
+ throw new ArgumentOutOfRangeException(nameof(entityType), entityType, "Unknown Service bus entity type");
+ }
+
+ return new TemporaryServiceBusEntity(entityType, entityName, serviceBusNamespace, logger);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ switch (_entityType)
+ {
+ case ServiceBusEntityType.Queue:
+ _logger.LogTrace("[Test] delete Service bus queue '{EntityName}''", EntityName);
+ ServiceBusQueueResource queue = await _ns.GetServiceBusQueueAsync(EntityName);
+ await queue.DeleteAsync(WaitUntil.Started);
+ break;
+
+ case ServiceBusEntityType.Topic:
+ _logger.LogTrace("[Test] delete Service bus topic '{EntityName}'", EntityName);
+ ServiceBusTopicResource topic = await _ns.GetServiceBusTopicAsync(EntityName);
+ await topic.DeleteAsync(WaitUntil.Started);
+ break;
+
+ case ServiceBusEntityType.Unknown:
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryServiceBusNamespace.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryServiceBusNamespace.cs
new file mode 100644
index 00000000..7dce5e4f
--- /dev/null
+++ b/src/Arcus.Messaging.Tests.Integration/Fixture/TemporaryServiceBusNamespace.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Azure;
+using Azure.Core;
+using Azure.Messaging.ServiceBus.Administration;
+using Azure.ResourceManager;
+using Azure.ResourceManager.Authorization;
+using Azure.ResourceManager.Authorization.Models;
+using Azure.ResourceManager.Resources;
+using Azure.ResourceManager.ServiceBus;
+using Azure.ResourceManager.ServiceBus.Models;
+using Microsoft.Azure.Management.ServiceBus.Models;
+using Microsoft.Extensions.Logging;
+
+namespace Arcus.Messaging.Tests.Integration.Fixture
+{
+ internal class TemporaryServiceBusNamespace : IAsyncDisposable
+ {
+ private const string DefaultRuleName = "RootManageSharedAccessKey";
+
+ private readonly ServiceBusNamespaceResource _namespace;
+ private readonly ILogger _logger;
+
+ private TemporaryServiceBusNamespace(
+ ServicePrincipal servicePrincipal,
+ ServiceBusNamespaceResource serviceBusNamespace,
+ ILogger logger)
+ {
+ _namespace = serviceBusNamespace;
+ _logger = logger;
+
+ Config = new ServiceBusConfig(servicePrincipal, _namespace.Id.SubscriptionId, _namespace.Id.ResourceGroupName, _namespace.Id.Name);
+ }
+
+ public ServiceBusConfig Config { get; }
+
+ public static async Task CreateBasicAsync(
+ string subscriptionId,
+ string resourceGroupName,
+ ServicePrincipal servicePrincipal,
+ ILogger logger)
+ {
+ var client = new ArmClient(servicePrincipal.GetCredential());
+
+ string @namespace = $"arcus-message-servicebus-{Guid.NewGuid().ToString()[..10]}";
+ logger.LogTrace("[Test] create Service bus namespace '{Namespace}'", @namespace);
+
+ ResourceGroupResource resourceGroup =
+ await client.GetResourceGroupResource(ResourceGroupResource.CreateResourceIdentifier(subscriptionId, resourceGroupName)).GetAsync();
+
+ var options = new ServiceBusNamespaceData(resourceGroup.Data.Location)
+ {
+ Sku = new ServiceBusSku(ServiceBusSkuName.Basic),
+ DisableLocalAuth = false
+ };
+ ArmOperation serviceBusNamespace =
+ await resourceGroup.GetServiceBusNamespaces()
+ .CreateOrUpdateAsync(WaitUntil.Completed, @namespace, options);
+
+ ResourceIdentifier serviceBusOwner =
+ RoleAssignmentResource.CreateResourceIdentifier(
+ scope: serviceBusNamespace.Value.Id.ToString(),
+ "090c5cfd-751d-490a-894a-3ce6f1109419");
+
+ var content = new RoleAssignmentCreateOrUpdateContent(serviceBusOwner, servicePrincipal.ObjectId);
+ await client.GetRoleAssignments(serviceBusNamespace.Value.Id)
+ .CreateOrUpdateAsync(WaitUntil.Completed, roleAssignmentName: Guid.NewGuid().ToString(), content);
+
+ return new TemporaryServiceBusNamespace(servicePrincipal, serviceBusNamespace.Value, logger);
+ }
+
+ public async Task GetAccessKeysAsync()
+ {
+ ServiceBusNamespaceAuthorizationRuleResource rule = GetDefaultAuthRule();
+
+ ServiceBusAccessKeys keys = await rule.GetKeysAsync();
+ return keys;
+ }
+
+ public async Task RotateAccessKeysAsync(ServiceBusAccessKeyType keyType)
+ {
+ _logger.LogTrace("[Test] rotate {KeyType} Service bus namespace '{Namespace}' connection string", keyType, _namespace.Id.Name);
+ ServiceBusNamespaceAuthorizationRuleResource rule = GetDefaultAuthRule();
+
+ ServiceBusAccessKeys newKeys =
+ await rule.RegenerateKeysAsync(new ServiceBusRegenerateAccessKeyContent(keyType));
+
+ return newKeys;
+ }
+
+ private ServiceBusNamespaceAuthorizationRuleResource GetDefaultAuthRule()
+ {
+ return _namespace.GetServiceBusNamespaceAuthorizationRule(DefaultRuleName);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ _logger.LogTrace("[Test] delete Service bus namespace '{Namespace}'", _namespace.Id.Name);
+ await _namespace.DeleteAsync(WaitUntil.Started);
+ }
+ }
+}
diff --git a/src/Arcus.Messaging.Tests.Integration/Fixture/TestConfig.cs b/src/Arcus.Messaging.Tests.Integration/Fixture/TestConfig.cs
deleted file mode 100644
index 40cccd5a..00000000
--- a/src/Arcus.Messaging.Tests.Integration/Fixture/TestConfig.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-using System;
-using System.Collections.Generic;
-using GuardNet;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Primitives;
-
-namespace Arcus.Messaging.Tests.Integration.Fixture
-{
- ///
- /// Represents a test configuration model with application key/value properties specific to this integration test suite.
- ///
- public class TestConfig : IConfigurationRoot
- {
- private readonly IConfigurationRoot _config;
-
- ///
- /// Initializes a new instance of the class.
- ///
- private TestConfig(IConfigurationRoot config)
- {
- Guard.NotNull(config, nameof(config));
- _config = config;
- }
-
- ///
- /// Creates the test configuration model for this integration test suite.
- ///
- ///
- public static TestConfig Create()
- {
- var config =
- new ConfigurationBuilder()
- .AddJsonFile(path: "appsettings.json")
- .AddJsonFile(path: "appsettings.local.json", optional: true)
- .AddEnvironmentVariables()
- .Build();
-
- return new TestConfig(config);
- }
-
- public string GetServiceBusTopicConnectionString()
- {
- return GetServiceBusConnectionString(ServiceBusEntityType.Topic);
- }
-
- public string GetServiceBusQueueConnectionString()
- {
- return GetServiceBusConnectionString(ServiceBusEntityType.Queue);
- }
-
- ///
- /// Gets the Service Bus connection string based on the given .
- ///
- /// The type of the Service Bus entity.
- public string GetServiceBusConnectionString(ServiceBusEntityType entity)
- {
- switch (entity)
- {
- case ServiceBusEntityType.Queue: return _config["Arcus:ServiceBus:SelfContained:ConnectionStringWithQueue"];
- case ServiceBusEntityType.Topic: return _config["Arcus:ServiceBus:SelfContained:ConnectionStringWithTopic"];
- default:
- throw new ArgumentOutOfRangeException(nameof(entity), entity, "Unknown Service Bus entity");
- }
- }
-
- ///
- /// Gets the service principal that can authenticate with the Azure resources used in these integration tests.
- ///
- ///
- public ServicePrincipal GetServicePrincipal()
- {
- var servicePrincipal = new ServicePrincipal(
- clientId: _config.GetValue("Arcus:Infra:ServicePrincipal:ClientId"),
- clientSecret: _config.GetValue("Arcus:Infra:ServicePrincipal:ClientSecret"));
-
- return servicePrincipal;
- }
-
- ///
- /// Gets the ID of the current tenant where the Azure resources used in these integration tests are located.
- ///
- public string GetTenantId()
- {
- const string tenantIdKey = "Arcus:Infra:TenantId";
- var tenantId = _config.GetValue(tenantIdKey);
- Guard.For(() => string.IsNullOrWhiteSpace(tenantId), $"Requires a non-blank 'TenantId' at '{tenantIdKey}'");
-
- return tenantId;
- }
-
- ///
- /// Gets all the configuration to run a complete key rotation integration test.
- ///
- public KeyRotationConfig GetKeyRotationConfig()
- {
- var azureEnv = new ServiceBusNamespace(
- tenantId: _config.GetValue("Arcus:KeyRotation:ServiceBus:TenantId"),
- azureSubscriptionId: _config.GetValue("Arcus:KeyRotation:ServiceBus:SubscriptionId"),
- resourceGroup: _config.GetValue("Arcus:KeyRotation:ServiceBus:ResourceGroupName"),
- @namespace: _config.GetValue("Arcus:KeyRotation:ServiceBus:Namespace"),
- queueName: _config.GetValue("Arcus:KeyRotation:ServiceBus:QueueName"),
- topicName: _config.GetValue("Arcus:KeyRotation:ServiceBus:TopicName"),
- authorizationRuleName: _config.GetValue("Arcus:KeyRotation:ServiceBus:AuthorizationRuleName"));
-
- var servicePrincipal = new ServicePrincipal(
- clientId: _config.GetValue("Arcus:KeyRotation:ServicePrincipal:ClientId"),
- clientSecret: _config.GetValue("Arcus:KeyRotation:ServicePrincipal:ClientSecret"),
- clientSecretKey: _config.GetValue("Arcus:KeyRotation:ServicePrincipal:ClientSecretKey"));
-
- var secret = new KeyVaultConfig(
- vaultUri: _config.GetValue("Arcus:KeyRotation:KeyVault:VaultUri"),
- secretName: _config.GetValue("Arcus:KeyRotation:KeyVault:ConnectionStringSecretName"),
- secretNewVersionCreated: new KeyVaultEventEndpoint(
- _config.GetValue("Arcus:KeyRotation:KeyVault:SecretNewVersionCreated:ServiceBusConnectionStringWithTopicEndpoint")));
-
- return new KeyRotationConfig(secret, servicePrincipal, azureEnv);
- }
-
- ///
- /// Gets all the configuration to run the Azure EventHubs integration tests.
- ///
- public EventHubsConfig GetEventHubsConfig()
- {
- return new EventHubsConfig(
- _config.GetValue("Arcus:EventHubs:SelfContained:EventHubsName"),
- _config.GetValue("Arcus:EventHubs:Docker:EventHubsName"),
- _config.GetValue("Arcus:EventHubs:Docker:AzureFunctions:Isolated:EventHubsName"),
- _config.GetValue("Arcus:EventHubs:Docker:AzureFunctions:InProcess:EventHubsName"),
- _config.GetValue("Arcus:EventHubs:ConnectionString"),
- _config.GetValue("Arcus:EventHubs:BlobStorage:StorageAccountConnectionString"));
- }
-
- ///
- /// Gets a configuration sub-section with the specified key.
- ///
- /// The key of the configuration section.
- /// The .
- ///
- /// This method will never return null. If no matching sub-section is found with the specified key,
- /// an empty will be returned.
- ///
- public IConfigurationSection GetSection(string key)
- {
- return _config.GetSection(key);
- }
-
- ///
- /// Gets the immediate descendant configuration sub-sections.
- ///
- /// The configuration sub-sections.
- public IEnumerable GetChildren()
- {
- return _config.GetChildren();
- }
-
- ///
- /// Returns a that can be used to observe when this configuration is reloaded.
- ///
- /// A .
- public IChangeToken GetReloadToken()
- {
- return _config.GetReloadToken();
- }
-
- /// Gets or sets a configuration value.
- /// The configuration key.
- /// The configuration value.
- public string this[string key]
- {
- get => _config[key];
- set => _config[key] = value;
- }
-
- ///
- /// Force the configuration values to be reloaded from the underlying s.
- ///
- public void Reload()
- {
- _config.Reload();
- }
-
- ///
- /// The s for this configuration.
- ///
- public IEnumerable Providers => _config.Providers;
- }
-}
diff --git a/src/Arcus.Messaging.Tests.Integration/Health/TcpHealthListenerDockerTests.cs b/src/Arcus.Messaging.Tests.Integration/Health/TcpHealthListenerDockerTests.cs
deleted file mode 100644
index 6ae6f716..00000000
--- a/src/Arcus.Messaging.Tests.Integration/Health/TcpHealthListenerDockerTests.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Diagnostics.HealthChecks;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Arcus.Messaging.Tests.Integration.Health
-{
- [Trait("Category", "Docker")]
- public class TcpHealthListenerDockerTests : IntegrationTest
- {
- private readonly int _healthTcpPort;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public TcpHealthListenerDockerTests(ITestOutputHelper testOutput) : base(testOutput)
- {
- _healthTcpPort = Configuration.GetValue("Arcus:Health:Port");
- }
-
- [Fact]
- public async Task TcpHealthListener_ProbeForHealthReport_ResponseHealthy()
- {
- // Arrange
- var service = new TcpHealthService(_healthTcpPort, Logger);
-
- // Act
- HealthReport report = await service.GetHealthReportAsync();
-
- // Assert
- Assert.NotNull(report);
- Assert.Equal(HealthStatus.Healthy, report.Status);
- (string entryName, HealthReportEntry entry) = Assert.Single(report.Entries);
- Assert.Equal("sample", entryName);
- Assert.Equal(HealthStatus.Healthy, entry.Status);
- }
- }
-}
diff --git a/src/Arcus.Messaging.Tests.Integration/Health/TcpHealthListenerTests.cs b/src/Arcus.Messaging.Tests.Integration/Health/TcpHealthListenerTests.cs
index 0aa647ce..0dafaca8 100644
--- a/src/Arcus.Messaging.Tests.Integration/Health/TcpHealthListenerTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/Health/TcpHealthListenerTests.cs
@@ -4,7 +4,7 @@
using System.Net.Sockets;
using System.Threading.Tasks;
using Arcus.Messaging.Tests.Integration.Fixture;
-using Arcus.Testing.Logging;
+using Arcus.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
diff --git a/src/Arcus.Messaging.Tests.Integration/IntegrationTest.cs b/src/Arcus.Messaging.Tests.Integration/IntegrationTest.cs
index c89beda3..250aa93a 100644
--- a/src/Arcus.Messaging.Tests.Integration/IntegrationTest.cs
+++ b/src/Arcus.Messaging.Tests.Integration/IntegrationTest.cs
@@ -1,4 +1,4 @@
-using Arcus.Testing.Logging;
+using Arcus.Testing;
using Microsoft.Extensions.Configuration;
using Xunit.Abstractions;
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubs/TemporaryBlobStorageContainer.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubs/TemporaryBlobStorageContainer.cs
deleted file mode 100644
index 12155e4c..00000000
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubs/TemporaryBlobStorageContainer.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Azure.Storage.Blobs;
-using Azure.Storage.Blobs.Models;
-using GuardNet;
-using Microsoft.Extensions.Logging;
-
-namespace Arcus.Messaging.Tests.Integration.MessagePump.EventHubs
-{
- ///
- /// Represents a temporary disposable fixture that sets up an Azure Blob storage container.
- ///
- public class TemporaryBlobStorageContainer : IAsyncDisposable
- {
- private readonly BlobServiceClient _client;
- private readonly ILogger _logger;
-
- private TemporaryBlobStorageContainer(BlobServiceClient client, string containerName, ILogger logger)
- {
- Guard.NotNull(client, nameof(client), "Requires an Azure Blob storage client to create an temporary container");
- Guard.NotNullOrWhitespace(containerName, nameof(containerName), "Requires a non-blank Azure Blob storage container name");
- Guard.NotNull(logger, nameof(logger), "Requires a logger instance to write diagnostic trace messages during the lifetime of the temporary Azure Blob storage container");
-
- _client = client;
- _logger = logger;
-
- ContainerName = containerName;
- ContainerUri = new Uri(client.Uri, new Uri(containerName, UriKind.Relative)).OriginalString;
- }
-
- ///
- /// Gets the container name used during this temporary Azure Blob storage container.
- ///
- public string ContainerName { get; }
-
- ///
- /// Gets the container primary endpoint used during this temporary Azure Blob storage container.
- ///
- public string ContainerUri { get; }
-
- ///
- /// Creates an instance that creates an Azure Blob container on the Azure storage account
- /// that is accessed via the given .
- ///
- /// The Azure storage account connection string on which a temporary Azure Blob storage container should be created.
- /// The logger instance to write diagnostic trace messages during the lifetime of the temporary Azure blob storage container.
- /// Thrown when the is blank.
- /// Thrown when the is null.
- public static async Task CreateAsync(string storageAccountConnectionString, ILogger logger)
- {
- Guard.NotNullOrWhitespace(storageAccountConnectionString, nameof(storageAccountConnectionString), "Requires a non-blank connection string to access the Azure storage account");
- Guard.NotNull(logger, nameof(logger), "Requires a logger instance to write diagnostic trace messages during the lifetime of the temporary Azure Blob storage container");
-
- string containerName = $"eventhubs-{Guid.NewGuid()}";
- return await CreateAsync(storageAccountConnectionString, containerName, logger);
- }
-
- ///
- /// Creates an instance that creates an Azure Blob container on the Azure storage account
- /// that is accessed via the given .
- ///
- /// The Azure storage account connection string on which a temporary Azure Blob storage container should be created.
- /// The logger instance to write diagnostic trace messages during the lifetime of the temporary Azure blob storage container.
- /// Thrown when the is blank.
- /// Thrown when the is null.
- public static async Task CreateAsync(string storageAccountConnectionString, string containerName, ILogger logger)
- {
- Guard.NotNullOrWhitespace(storageAccountConnectionString, nameof(storageAccountConnectionString), "Requires a non-blank connection string to access the Azure storage account");
- Guard.NotNull(logger, nameof(logger), "Requires a logger instance to write diagnostic trace messages during the lifetime of the temporary Azure Blob storage container");
-
- var blobClient = new BlobServiceClient(storageAccountConnectionString);
-
- logger.LogTrace("Add Azure Blob storage container '{ContainerName}'", containerName);
- await blobClient.CreateBlobContainerAsync(containerName, PublicAccessType.Blob);
-
- return new TemporaryBlobStorageContainer(blobClient, containerName, logger);
- }
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously.
- ///
- /// A task that represents the asynchronous dispose operation.
- public async ValueTask DisposeAsync()
- {
- _logger.LogTrace("Remove Azure Blob storage container '{ContainerName}'", ContainerName);
- await _client.DeleteBlobContainerAsync(ContainerName);
- }
- }
-}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubs/TestEventHubsMessageProducer.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubs/TestEventHubsMessageProducer.cs
index 9a2c84b7..6074f73a 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubs/TestEventHubsMessageProducer.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubs/TestEventHubsMessageProducer.cs
@@ -1,6 +1,7 @@
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
+using Arcus.Messaging.Tests.Integration.Fixture;
using Azure.Messaging.EventHubs;
using Azure.Messaging.EventHubs.Producer;
using GuardNet;
@@ -12,22 +13,16 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump.EventHubs
///
public class TestEventHubsMessageProducer
{
- private readonly string _eventHubsConnectionString;
- private readonly string _eventHubName;
+ private readonly string _name;
+ private readonly EventHubsConfig _config;
///
/// Initializes a new instance of the class.
///
- /// The connection string to access the Azure EventHubs.
- /// The name of the Azure EventHubs where an event message should be placed.
- /// Thrown when the or the is blank.
- public TestEventHubsMessageProducer(string eventHubsConnectionString, string eventHubName)
+ public TestEventHubsMessageProducer(string name, EventHubsConfig config)
{
- Guard.NotNullOrWhitespace(eventHubsConnectionString, nameof(eventHubsConnectionString), "Requires a non-blank connection string to access the Azure EventHubs");
- Guard.NotNullOrWhitespace(eventHubName, nameof(eventHubName), "Requires a non-blank name of the Azure EventHubs");
-
- _eventHubsConnectionString = eventHubsConnectionString;
- _eventHubName = eventHubName;
+ _name = name;
+ _config = config;
}
///
@@ -43,7 +38,7 @@ public async Task ProduceAsync(EventData eventData)
{
Guard.NotNull(eventData, nameof(eventData), "Requires an event data instance to place on the configured Azure EventHubs");
- await using (var client = new EventHubProducerClient(_eventHubsConnectionString, _eventHubName))
+ await using (EventHubProducerClient client = _config.GetProducerClient(_name))
{
await client.SendAsync(new[] { eventData });
}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.ConnectivityTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.ConnectivityTests.cs
index 424a2c05..5621c666 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.ConnectivityTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.ConnectivityTests.cs
@@ -5,7 +5,6 @@
using System.Threading.Tasks;
using Arcus.Messaging.Pumps.Abstractions;
using Arcus.Messaging.Pumps.EventHubs;
-using Arcus.Messaging.Tests.Core.Correlation;
using Arcus.Messaging.Tests.Core.Events.v1;
using Arcus.Messaging.Tests.Core.Messages.v1;
using Arcus.Messaging.Tests.Integration.Fixture;
@@ -22,16 +21,20 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump
public partial class EventHubsMessagePumpTests
{
[Fact]
- public async Task EventHubsMessagePumpUsingManagedIdentity_PublishesMessage_MessageSuccessfullyProcessed()
+ public async Task EventHubsMessagePumpUsingSecrets_PublishesMessage_MessageSuccessfullyProcessed()
{
- using var auth = TemporaryManagedIdentityConnection.Create(_config, _logger);
+ string eventHubsConnectionStringSecretName = "Arcus_EventHubs_ConnectionString",
+ storageAccountConnectionStringSecretName = "Arcus_StorageAccount_ConnectionString";
+
await TestEventHubsMessageHandlingAsync(options =>
{
- options.AddEventHubsMessagePumpUsingManagedIdentity(
- eventHubsName: EventHubsName,
- fullyQualifiedNamespace: FullyQualifiedEventHubsNamespace,
- blobContainerUri: _blobStorageContainer.ContainerUri,
- clientId: auth.ClientId)
+ options.AddSecretStore(stores => stores.AddInMemory(new Dictionary
+ {
+ [eventHubsConnectionStringSecretName] = _eventHubsConfig.EventHubsConnectionString,
+ [storageAccountConnectionStringSecretName] = _eventHubsConfig.Storage.ConnectionString
+ }));
+
+ options.AddEventHubsMessagePump(EventHubsName, eventHubsConnectionStringSecretName, ContainerName, storageAccountConnectionStringSecretName)
.WithEventHubsMessageHandler();
});
}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.TelemetryTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.TelemetryTests.cs
index 3f3cfc80..11b6342a 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.TelemetryTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePump.TelemetryTests.cs
@@ -9,15 +9,16 @@
using Arcus.Messaging.Tests.Integration.Fixture;
using Arcus.Messaging.Tests.Integration.Fixture.Logging;
using Arcus.Messaging.Tests.Integration.MessagePump.EventHubs;
-using Arcus.Messaging.Tests.Integration.MessagePump.Fixture;
using Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus;
using Arcus.Messaging.Tests.Workers.EventHubs.Core.MessageHandlers;
+using Arcus.Testing;
using Azure.Messaging.EventHubs;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Xunit;
+using static Arcus.Messaging.Tests.Integration.MessagePump.Fixture.AssertX;
namespace Arcus.Messaging.Tests.Integration.MessagePump
{
@@ -118,15 +119,28 @@ public async Task EventHubsMessagePump_WithW3CCorrelationFormat_AutomaticallyTra
await producer.ProduceAsync(eventData);
// Assert
- AssertX.RetryAssertUntil(() =>
- {
- RequestTelemetry requestViaArcusEventHubs = AssertX.GetRequestFrom(spySink.Telemetries, r => r.Name == operationName && r.Context.Operation.Id == traceParent.TransactionId);
- DependencyTelemetry dependencyViaArcusKeyVault = AssertX.GetDependencyFrom(spySink.Telemetries, d => d.Type == "Azure key vault" && d.Context.Operation.Id == traceParent.TransactionId);
- DependencyTelemetry dependencyViaMicrosoftSql = AssertX.GetDependencyFrom(spyChannel.Telemetries, d => d.Type == "SQL" && d.Context.Operation.Id == traceParent.TransactionId);
-
- Assert.Equal(requestViaArcusEventHubs.Id, dependencyViaArcusKeyVault.Context.Operation.ParentId);
- Assert.Equal(requestViaArcusEventHubs.Id, dependencyViaMicrosoftSql.Context.Operation.ParentId);
- }, timeout: TimeSpan.FromMinutes(2), _logger);
+ TimeSpan timeout = TimeSpan.FromMinutes(2);
+
+ DependencyTelemetry dependencyViaArcusKeyVault =
+ await Poll.Target(() => GetDependencyFrom(spySink.Telemetries, d => d.Type == "Azure key vault"))
+ .Until(d => d.Context.Operation.Id == traceParent.TransactionId)
+ .Timeout(timeout)
+ .FailWith("missing Key vault dependency telemetry tracking via Arcus with W3C format in spied sink");
+
+ DependencyTelemetry dependencyViaMicrosoftSql =
+ await Poll.Target(() => GetDependencyFrom(spyChannel.Telemetries, d => d.Type == "SQL"))
+ .Until(d => d.Context.Operation.Id == traceParent.TransactionId)
+ .Timeout(timeout)
+ .FailWith("missing SQL dependency telemetry tracking via Microsoft with W3C format in spied channel");
+
+ RequestTelemetry requestViaArcusEventHubs =
+ await Poll.Target(() => GetRequestFrom(spySink.Telemetries, r => r.Name == operationName))
+ .Until(r => r.Context.Operation.Id == traceParent.TransactionId)
+ .Timeout(timeout)
+ .FailWith("missing request telemetry tracking with W3C format in spied sink");
+
+ Assert.Equal(requestViaArcusEventHubs.Id, dependencyViaArcusKeyVault.Context.Operation.ParentId);
+ Assert.Equal(requestViaArcusEventHubs.Id, dependencyViaMicrosoftSql.Context.Operation.ParentId);
}
[Fact]
@@ -154,19 +168,19 @@ public async Task EventHubsMessagePump_WithW3CCorrelationFormatForNewParent_Auto
await producer.ProduceAsync(eventData);
// Assert
- AssertX.RetryAssertUntil(() =>
- {
- IEnumerable dependenciesViaArcusKeyVault = spySink.Telemetries.OfType().Where(d => d.Type == "Azure key vault");
- IEnumerable dependenciesViaMicrosoftSql = spyChannel.Telemetries.OfType().Where(d => d.Type == "SQL");
-
- bool correlationSuccess = spySink.Telemetries.Any(t =>
- {
- return t is RequestTelemetry r && r.Name == operationName
- && dependenciesViaArcusKeyVault.SingleOrDefault(d => d.Context.Operation.ParentId == r.Id) != null
- && dependenciesViaMicrosoftSql.SingleOrDefault(d => d.Context.Operation.ParentId == r.Id) != null;
- });
- Assert.True(correlationSuccess);
- }, timeout: TimeSpan.FromMinutes(1), _logger);
+ RequestTelemetry requestViaArcusEventHubs =
+ await Poll.Target(() => GetRequestFrom(spySink.Telemetries, r => r.Name == operationName))
+ .Timeout(TimeSpan.FromMinutes(2))
+ .FailWith("missing request telemetry with operation name in spied sink");
+
+ await Poll.Target(() => GetDependencyFrom(spySink.Telemetries, d => d.Type == "Azure key vault"))
+ .Until(d => d.Context.Operation.ParentId == requestViaArcusEventHubs.Id)
+ .FailWith("missing Key vault dependency telemetry tracking via Arcus in spied sink");
+
+
+ await Poll.Target(() => GetDependencyFrom(spyChannel.Telemetries, d => d.Type == "SQL"))
+ .Until(d => d.Context.Operation.ParentId == requestViaArcusEventHubs.Id)
+ .FailWith("missing SQL dependency telemetry racking via Microsoft on spied channel");
}
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePumpDockerTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePumpDockerTests.cs
deleted file mode 100644
index 7d5c43b3..00000000
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePumpDockerTests.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Threading.Tasks;
-using Arcus.Messaging.Abstractions;
-using Arcus.Messaging.Tests.Core.Correlation;
-using Arcus.Messaging.Tests.Core.Events.v1;
-using Arcus.Messaging.Tests.Core.Generators;
-using Arcus.Messaging.Tests.Core.Messages.v1;
-using Arcus.Messaging.Tests.Integration.Fixture;
-using Arcus.Messaging.Tests.Integration.MessagePump.EventHubs;
-using Arcus.Messaging.Tests.Integration.MessagePump.Fixture;
-using Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus;
-using Arcus.Testing.Logging;
-using Azure.Messaging.EventHubs;
-using Bogus;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Arcus.Messaging.Tests.Integration.MessagePump
-{
- [Collection("Docker")]
- [Trait("Category", "Docker")]
- public class EventHubsMessagePumpDockerTests : DockerServiceBusIntegrationTest
- {
- private readonly TestConfig _config;
-
- public EventHubsMessagePumpDockerTests(ITestOutputHelper outputWriter) : base(outputWriter)
- {
- _config = TestConfig.Create();
- }
-
- [Fact]
- public async Task EventHubsMessagePump_PublishEventDataMessage_MessageSuccessfullyProcessed()
- {
- // Arrange
- var traceParent = TraceParent.Generate();
- Order order = OrderGenerator.Generate();
- EventData expected = new EventData(JsonConvert.SerializeObject(order)).WithDiagnosticId(traceParent);
-
- EventHubsConfig eventHubs = _config.GetEventHubsConfig();
- string eventHubsName = eventHubs.GetEventHubsName(IntegrationTestType.DockerWorker);
- var producer = new TestEventHubsMessageProducer(eventHubs.EventHubsConnectionString, eventHubsName);
-
- await using (var consumer = await TestServiceBusMessageEventConsumer.StartNewAsync(_config, Logger))
- {
- // Act
- await producer.ProduceAsync(expected);
-
- // Assert
- OrderCreatedEventData orderCreatedEventData = consumer.ConsumeOrderEventForW3C(traceParent.TransactionId);
- Assert.NotNull(orderCreatedEventData);
- Assert.NotNull(orderCreatedEventData.CorrelationInfo);
- Assert.Equal(order.Id, orderCreatedEventData.Id);
- Assert.Equal(order.Amount, orderCreatedEventData.Amount);
- Assert.Equal(order.ArticleNumber, orderCreatedEventData.ArticleNumber);
- Assert.Equal(traceParent.TransactionId, orderCreatedEventData.CorrelationInfo.TransactionId);
- Assert.Equal(traceParent.OperationParentId, orderCreatedEventData.CorrelationInfo.OperationParentId);
- Assert.NotNull(orderCreatedEventData.CorrelationInfo.OperationId);
- }
- }
- }
-}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePumpTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePumpTests.cs
index 65587270..ebf8984d 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePumpTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/EventHubsMessagePumpTests.cs
@@ -1,8 +1,8 @@
using System;
-using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
+using Arcus.EventGrid.Testing.Logging;
using Arcus.Messaging.Abstractions;
using Arcus.Messaging.Abstractions.EventHubs.MessageHandling;
using Arcus.Messaging.Abstractions.MessageHandling;
@@ -15,11 +15,11 @@
using Arcus.Messaging.Tests.Integration.MessagePump.EventHubs;
using Arcus.Messaging.Tests.Integration.MessagePump.Fixture;
using Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus;
-using Arcus.Testing.Logging;
+using Arcus.Testing;
using Azure.Messaging.EventHubs;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using Xunit;
using Xunit.Abstractions;
@@ -28,51 +28,52 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump
{
[Collection("Integration")]
[Trait("Category", "Integration")]
- public partial class EventHubsMessagePumpTests : IAsyncLifetime
+ public partial class EventHubsMessagePumpTests : IClassFixture, IAsyncLifetime
{
private readonly TestConfig _config;
private readonly EventHubsConfig _eventHubsConfig;
private readonly ILogger _logger;
private readonly ITestOutputHelper _outputWriter;
- private TemporaryBlobStorageContainer _blobStorageContainer;
+ private TemporaryBlobContainer _blobStorageContainer;
+ private TemporaryManagedIdentityConnection _connection;
///
/// Initializes a new instance of the class.
///
- public EventHubsMessagePumpTests(ITestOutputHelper outputWriter)
+ public EventHubsMessagePumpTests(EventHubsEntityFixture fixture, ITestOutputHelper outputWriter)
{
_outputWriter = outputWriter;
_logger = new XunitTestLogger(outputWriter);
_config = TestConfig.Create();
- _eventHubsConfig = _config.GetEventHubsConfig();
+ _eventHubsConfig = _config.GetEventHubs();
+
+ EventHubsName = fixture.HubName;
}
- private string EventHubsName => _eventHubsConfig.GetEventHubsName(IntegrationTestType.SelfContained);
- private string FullyQualifiedEventHubsNamespace => EventHubsConnectionStringProperties.Parse(_eventHubsConfig.EventHubsConnectionString).FullyQualifiedNamespace;
- private string ContainerName => _blobStorageContainer.ContainerName;
+ private string EventHubsName { get; }
+ private string FullyQualifiedEventHubsNamespace => _eventHubsConfig.HostName;
+ private string ContainerName => _blobStorageContainer.Name;
///
/// Called immediately after the class has been created, before it is used.
///
public async Task InitializeAsync()
{
- _blobStorageContainer = await TemporaryBlobStorageContainer.CreateAsync(_eventHubsConfig.StorageConnectionString, _logger);
+ _connection = TemporaryManagedIdentityConnection.Create(_config, _logger);
+ _blobStorageContainer = await TemporaryBlobContainer.CreateIfNotExistsAsync(_eventHubsConfig.Storage.Name, $"test-{Guid.NewGuid()}", _logger);
}
private EventHubsMessageHandlerCollection AddEventHubsMessagePump(WorkerOptions options, Action configureOptions = null)
{
- string eventHubsConnectionStringSecretName = "Arcus_EventHubs_ConnectionString",
- storageAccountConnectionStringSecretName = "Arcus_StorageAccount_ConnectionString";
-
return options.AddXunitTestLogging(_outputWriter)
- .AddSecretStore(stores => stores.AddInMemory(new Dictionary
- {
- [eventHubsConnectionStringSecretName] = _eventHubsConfig.EventHubsConnectionString,
- [storageAccountConnectionStringSecretName] = _eventHubsConfig.StorageConnectionString
- }))
- .AddEventHubsMessagePump(EventHubsName, eventHubsConnectionStringSecretName, ContainerName, storageAccountConnectionStringSecretName, configureOptions);
+ .AddEventHubsMessagePumpUsingManagedIdentity(
+ eventHubsName: EventHubsName,
+ fullyQualifiedNamespace: FullyQualifiedEventHubsNamespace,
+ blobContainerUri: _blobStorageContainer.Client.Uri.ToString(),
+ clientId: _connection.ClientId,
+ configureOptions);
}
private async Task TestEventHubsMessageHandlingAsync(
@@ -215,9 +216,7 @@ private static void AssertReceivedSensorEventDataForW3C(
private TestEventHubsMessageProducer CreateEventHubsMessageProducer()
{
- return new TestEventHubsMessageProducer(
- _eventHubsConfig.EventHubsConnectionString,
- EventHubsName);
+ return new TestEventHubsMessageProducer(EventHubsName, _eventHubsConfig);
}
///
@@ -230,6 +229,26 @@ public async Task DisposeAsync()
{
await _blobStorageContainer.DisposeAsync();
}
+
+ _connection?.Dispose();
+ }
+ }
+
+ public class EventHubsEntityFixture : IAsyncLifetime
+ {
+ private TemporaryEventHubEntity _hub;
+
+ public string HubName { get; } = $"hub-{Guid.NewGuid()}";
+
+ public async Task InitializeAsync()
+ {
+ var config = TestConfig.Create();
+ _hub = await TemporaryEventHubEntity.CreateAsync(HubName, config.GetEventHubs(), NullLogger.Instance);
+ }
+
+ public async Task DisposeAsync()
+ {
+ await _hub.DisposeAsync();
}
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/DiskMessageEventConsumer.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/DiskMessageEventConsumer.cs
index 16d60dea..4f7e633b 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/DiskMessageEventConsumer.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/DiskMessageEventConsumer.cs
@@ -11,10 +11,10 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus
{
public static class DiskMessageEventConsumer
{
- public static async Task ConsumeOrderCreatedAsync(string messageId)
+ public static async Task ConsumeOrderCreatedAsync(string messageId, TimeSpan? timeout = null)
{
return await ConsumeEventAsync(messageId,
- $"order created event does not seem to be delivered in time as the file '{messageId}.json' cannot be found on disk");
+ $"order created event does not seem to be delivered in time as the file '{messageId}.json' cannot be found on disk", timeout);
}
public static async Task ConsumeSensorReadAsync(string messageId)
@@ -23,7 +23,7 @@ public static async Task ConsumeSensorReadAsync(string mess
$"sensor read event does not seem to be delivered in time as the file '{messageId}.json' cannot be found on disk");
}
- private static async Task ConsumeEventAsync(string messageId, string errorMessage)
+ private static async Task ConsumeEventAsync(string messageId, string errorMessage, TimeSpan? timeout = null)
{
var dir = new DirectoryInfo(Directory.GetCurrentDirectory());
@@ -31,7 +31,7 @@ private static async Task ConsumeEventAsync(string messageId,
await Poll.Target(() => Assert.Single(dir.GetFiles($"{messageId}.json", SearchOption.AllDirectories)))
.Until(files => files.Length > 0)
.Every(TimeSpan.FromMilliseconds(100))
- .Timeout(TimeSpan.FromSeconds(10))
+ .Timeout(timeout ?? TimeSpan.FromSeconds(10))
.FailWith(errorMessage);
string json = await File.ReadAllTextAsync(file.FullName);
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceBusMessageEventConsumer.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceBusMessageEventConsumer.cs
deleted file mode 100644
index 59bc78c6..00000000
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceBusMessageEventConsumer.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Arcus.EventGrid;
-using Arcus.EventGrid.Contracts;
-using Arcus.EventGrid.Parsers;
-using Arcus.EventGrid.Testing.Infrastructure.Hosts.ServiceBus;
-using Arcus.Messaging.Tests.Core.Events.v1;
-using Arcus.Messaging.Tests.Workers.ServiceBus.Fixture;
-using CloudNative.CloudEvents;
-using GuardNet;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
-using Xunit;
-using TestConfig = Arcus.Messaging.Tests.Integration.Fixture.TestConfig;
-
-namespace Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus
-{
- ///
- /// Represents an event consumer which receives events from an Azure Service Bus.
- ///
- public class TestServiceBusMessageEventConsumer : IAsyncDisposable
- {
- private readonly ServiceBusEventConsumerHost _serviceBusEventConsumerHost;
-
- private TestServiceBusMessageEventConsumer(ServiceBusEventConsumerHost consumerHost)
- {
- Guard.NotNull(consumerHost, nameof(consumerHost), "Requires an Azure Service Bus consumer host instance to consume messages");
- _serviceBusEventConsumerHost = consumerHost;
- }
-
- ///
- /// Starts an new event consumer which receives events from an Azure Service Bus entity.
- ///
- /// The test configuration to retrieve the Azure Service Bus test infrastructure.
- /// The logger to write diagnostic messages during consuming the messages.
- /// Thrown when the is null.
- public static async Task StartNewAsync(TestConfig configuration, ILogger logger)
- {
- Guard.NotNull(configuration, nameof(configuration), "Requires a test configuration to retrieve the Azure Service Bus test infrastructure");
-
- logger = logger ?? NullLogger.Instance;
-
- var topicName = configuration.GetValue("Arcus:Infra:ServiceBus:TopicName");
- var connectionString = configuration.GetValue("Arcus:Infra:ServiceBus:ConnectionString");
- var serviceBusEventConsumerHostOptions = new ServiceBusEventConsumerHostOptions(topicName, connectionString);
-
- var serviceBusEventConsumerHost = await ServiceBusEventConsumerHost.StartAsync(serviceBusEventConsumerHostOptions, logger);
- return new TestServiceBusMessageEventConsumer(serviceBusEventConsumerHost);
- }
-
- ///
- /// Receives an event produced on the Azure Service Bus.
- ///
- /// The ID to identity the produced event.
- /// Thrown when the is blank.
- public OrderCreatedEventData ConsumeOrderEventForHierarchical(string eventId)
- {
- Guard.NotNullOrWhitespace(eventId, nameof(eventId), "Requires a non-blank event ID to identity the produced event on the Azure Service Bus");
-
- string receivedEvent = _serviceBusEventConsumerHost.GetReceivedEvent(eventId, retryCount: 10);
- Assert.NotEmpty(receivedEvent);
-
- EventBatch eventBatch = EventParser.Parse(receivedEvent);
- Assert.NotNull(eventBatch);
- Event @event = Assert.Single(eventBatch.Events);
- Assert.NotNull(@event);
-
- var data = @event.Data.ToString();
- Assert.NotNull(data);
-
- var eventData = JsonConvert.DeserializeObject(data, new MessageCorrelationInfoJsonConverter());
- return eventData;
- }
-
- ///
- /// Receives an event produced on the Azure Service Bus.
- ///
- /// The ID to identity the produced event.
- /// The optional time-out in seconds for the event to be arrived.
- /// Thrown when the is blank.
- public OrderCreatedEventData ConsumeOrderEventForW3C(string transactionId, int timeoutInSeconds = 60)
- {
- Guard.NotNullOrWhitespace(transactionId, nameof(transactionId), "Requires a non-blank transaction ID to identity the produced event on the Azure Service Bus");
- Guard.NotLessThan(timeoutInSeconds, 0, nameof(timeoutInSeconds), "Requires a time-out in seconds of at least 1 second");
-
- // TODO: will be simplified, once all the message handlers are using the same event publishing (https://github.com/arcus-azure/arcus.messaging/issues/343).
- CloudEvent receivedEvent = _serviceBusEventConsumerHost.GetReceivedEvent((CloudEvent ev) =>
- {
- var data = ev.Data.ToString();
- var eventData = JsonConvert.DeserializeObject(data, new MessageCorrelationInfoJsonConverter());
-
- return eventData.CorrelationInfo.TransactionId == transactionId;
- }, timeout: TimeSpan.FromSeconds(timeoutInSeconds));
-
- var data = receivedEvent.Data.ToString();
-
- var eventData = JsonConvert.DeserializeObject(data, new MessageCorrelationInfoJsonConverter());
- return eventData;
- }
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously.
- ///
- /// A task that represents the asynchronous dispose operation.
- public async ValueTask DisposeAsync()
- {
- await _serviceBusEventConsumerHost.StopAsync();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceBusMessageProducer.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceBusMessageProducer.cs
index ddfaaeeb..0f20ad22 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceBusMessageProducer.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceBusMessageProducer.cs
@@ -1,9 +1,9 @@
using System;
using System.Threading.Tasks;
using Arcus.Messaging.Tests.Integration.Fixture;
+using Arcus.Testing;
using Azure.Messaging.ServiceBus;
using GuardNet;
-using Microsoft.Extensions.Logging;
namespace Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus
{
@@ -13,50 +13,34 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus
public class TestServiceBusMessageProducer
{
private readonly string _connectionString;
+ private readonly string _entityName;
+ private readonly ServiceBusConfig _config;
///
/// Initializes a new instance of the class.
///
- /// The Azure Service Bus entity-scoped connection string to send messages to.
- /// Thrown when the is blank.
- public TestServiceBusMessageProducer(string connectionString)
- {
- Guard.NotNullOrWhitespace(connectionString, nameof(connectionString), "Requires a non-blank Azure Service Bus entity-scoped connection string");
- _connectionString = connectionString;
- }
-
- ///
- /// Creates an instance which sends events to an Azure Service Bus topic subscription.
- ///
- /// The test configuration used in this test suite.
- public static TestServiceBusMessageProducer CreateForTopic(TestConfig configuration)
+ public TestServiceBusMessageProducer(string entityName, ServiceBusConfig config)
{
- Guard.NotNull(configuration, nameof(configuration), "Requires a test configuration to retrieve the Azure Service Bus topic entity-scoped connection string");
- return CreateFor(configuration, ServiceBusEntityType.Topic);
+ _entityName = entityName;
+ _config = config ?? throw new ArgumentNullException(nameof(config));
}
///
- /// Creates an instance which sends events to an Azure Service Bus queue.
+ /// Initializes a new instance of the class.
///
- /// The test configuration used in this test suite.
- public static TestServiceBusMessageProducer CreateForQueue(TestConfig configuration)
+ public TestServiceBusMessageProducer(string connectionString)
{
- Guard.NotNull(configuration, nameof(configuration), "Requires a test configuration to retrieve the Azure Service Bus queue entity-scoped connection string");
- return CreateFor(configuration, ServiceBusEntityType.Queue);
+ _connectionString = connectionString;
}
///
/// Creates an instance which sends events to an Azure Service Bus.
///
- /// The test configuration used in this test suite.
- ///
- /// Thrown when the is null.
- public static TestServiceBusMessageProducer CreateFor(TestConfig configuration, ServiceBusEntityType entityType)
+ public static TestServiceBusMessageProducer CreateFor(string entityName, TestConfig configuration)
{
Guard.NotNull(configuration, nameof(configuration), "Requires a test configuration to retrieve the Azure Service Bus entity-scoped connection string");
- string connectionString = configuration.GetServiceBusConnectionString(entityType);
- return new TestServiceBusMessageProducer(connectionString);
+ return new TestServiceBusMessageProducer(entityName, configuration.GetServiceBus());
}
///
@@ -68,20 +52,12 @@ public async Task ProduceAsync(params ServiceBusMessage[] messages)
{
Guard.NotNull(messages, nameof(messages), "Requires an Azure Service Bus message to send");
- var connectionStringProperties = ServiceBusConnectionStringProperties.Parse(_connectionString);
- await using (var client = new ServiceBusClient(_connectionString))
- {
- ServiceBusSender messageSender = client.CreateSender(connectionStringProperties.EntityPath);
-
- try
- {
- await messageSender.SendMessagesAsync(messages);
- }
- finally
- {
- await messageSender.CloseAsync();
- }
- }
+ await using var client = _connectionString is null
+ ? new ServiceBusClient(_config.HostName, _config.ServicePrincipal.GetCredential())
+ : new ServiceBusClient(_connectionString);
+
+ await using ServiceBusSender messageSender = client.CreateSender(_entityName);
+ await messageSender.SendMessagesAsync(messages);
}
}
}
\ No newline at end of file
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceMessageConsumer.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceMessageConsumer.cs
index 302c0cb5..b86c945c 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceMessageConsumer.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBus/TestServiceMessageConsumer.cs
@@ -2,14 +2,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Arcus.Messaging.Tests.Core.Events.v1;
+using Arcus.Messaging.Tests.Integration.Fixture;
using Arcus.Messaging.Tests.Workers.ServiceBus.Fixture;
using Arcus.Testing;
using Azure.Messaging.ServiceBus;
-using GuardNet;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Xunit;
-using TestConfig = Arcus.Messaging.Tests.Integration.Fixture.TestConfig;
namespace Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus
{
@@ -18,31 +17,23 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus
///
public class TestServiceMessageConsumer
{
- private readonly string _connectionString;
+ private readonly string _entityName;
+ private readonly ServiceBusConfig _config;
private readonly ILogger _logger;
- private TestServiceMessageConsumer(string connectionString, ILogger logger)
+ private TestServiceMessageConsumer(string entityName, ServiceBusConfig config, ILogger logger)
{
- Guard.NotNullOrWhitespace(connectionString, nameof(connectionString), "Requires an Azure Service Bus connection string so dead-lettered messages can be consumed");
- Guard.NotNull(logger, nameof(logger), "Requires a logger instance to write informational messages during the dead-lettered message consuming from an Azure Service Bus queue");
-
- _connectionString = connectionString;
+ _entityName = entityName;
+ _config = config;
_logger = logger;
}
///
/// Creates an instance that consumes dead-lettered messages from an Azure Service Bus queue.
///
- /// The integration test configuration where the connection string to the Azure Service Bus queue is present.
- /// The logger instance to write informational messages during the message consuming.
- /// Thrown when the or the is null.
- public static TestServiceMessageConsumer CreateForQueue(TestConfig config, ILogger logger)
+ public static TestServiceMessageConsumer CreateForQueue(string entityName, ServiceBusConfig config, ILogger logger)
{
- Guard.NotNull(config, nameof(config), "Requires an integration test configuration to retrieve the Azure Service Bus queue connection string so dead-lettered messages can be consumed");
- Guard.NotNull(logger, nameof(logger), "Requires a logger instance to write informational messages during the dead-lettered message consuming from an Azure Service Bus queue");
-
- string connectionString = config.GetServiceBusQueueConnectionString();
- return new TestServiceMessageConsumer(connectionString, logger);
+ return new TestServiceMessageConsumer(entityName, config, logger);
}
///
@@ -50,9 +41,8 @@ public static TestServiceMessageConsumer CreateForQueue(TestConfig config, ILogg
///
public async Task AssertCompletedMessageAsync(string messageId)
{
- var properties = ServiceBusConnectionStringProperties.Parse(_connectionString);
- await using var client = new ServiceBusClient(_connectionString);
- await using var receiver = client.CreateReceiver(properties.EntityPath);
+ await using ServiceBusClient client = _config.GetClient();
+ await using ServiceBusReceiver receiver = client.CreateReceiver(_entityName);
IReadOnlyList messages = await receiver.ReceiveMessagesAsync(1, maxWaitTime: TimeSpan.FromSeconds(2));
Assert.DoesNotContain(messages, msg => msg.MessageId == messageId);
@@ -64,9 +54,8 @@ public async Task AssertCompletedMessageAsync(string messageId)
/// Thrown when no abandoned messages can be consumed within the configured time-out.
public async Task AssertAbandonMessageAsync(string messageId)
{
- var properties = ServiceBusConnectionStringProperties.Parse(_connectionString);
- await using var client = new ServiceBusClient(_connectionString);
- await using var receiver = client.CreateReceiver(properties.EntityPath);
+ await using ServiceBusClient client = _config.GetClient();
+ await using ServiceBusReceiver receiver = client.CreateReceiver(_entityName);
ServiceBusReceivedMessage message =
await Poll.Target(() => receiver.ReceiveMessageAsync())
@@ -84,19 +73,18 @@ await Poll.Target(() => receiver.ReceiveMessageAsync())
/// Thrown when no dead-lettered messages can be consumed within the configured time-out.
public async Task AssertDeadLetterMessageAsync(string messageId)
{
- var properties = ServiceBusConnectionStringProperties.Parse(_connectionString);
var options = new ServiceBusReceiverOptions { SubQueue = SubQueue.DeadLetter };
async Task ReceiveMessageAsync()
{
- await using var client = new ServiceBusClient(_connectionString);
- await using var receiver = client.CreateReceiver(properties.EntityPath, options);
+ await using ServiceBusClient client = _config.GetClient();
+ await using ServiceBusReceiver receiver = client.CreateReceiver(_entityName, options);
- _logger.LogTrace("Start looking for dead-lettered message '{MessageId}' for Azure Service bus queue '{QueueName}'", messageId, properties.EntityPath);
+ _logger.LogTrace("Start looking for dead-lettered message '{MessageId}' for Azure Service bus queue '{QueueName}'", messageId, _entityName);
ServiceBusReceivedMessage message = await receiver.ReceiveMessageAsync();
if (message != null)
{
- _logger.LogTrace("Found dead-lettered message '{MessageId}' for Azure Service bus queue '{QueueName}'", messageId, properties.EntityPath);
+ _logger.LogTrace("Found dead-lettered message '{MessageId}' for Azure Service bus queue '{QueueName}'", messageId, _entityName);
await receiver.CompleteMessageAsync(message);
}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ConnectivityTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ConnectivityTests.cs
index bd408b39..c21e1b06 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ConnectivityTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ConnectivityTests.cs
@@ -1,21 +1,14 @@
using System;
using System.Threading.Tasks;
-using Arcus.Messaging.Abstractions.MessageHandling;
-using Arcus.Messaging.Pumps.ServiceBus;
using Arcus.Messaging.Tests.Core.Events.v1;
using Arcus.Messaging.Tests.Core.Messages.v1;
using Arcus.Messaging.Tests.Integration.Fixture;
using Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus;
-using Arcus.Messaging.Tests.Integration.ServiceBus;
using Arcus.Messaging.Tests.Workers.MessageHandlers;
-using Arcus.Security.Core.Caching.Configuration;
-using Azure.Identity;
using Azure.Messaging.ServiceBus;
-using Azure.Security.KeyVault.Secrets;
-using Microsoft.Azure.Management.ServiceBus.Models;
+using Azure.ResourceManager.ServiceBus.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
using Xunit;
using static Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus.DiskMessageEventConsumer;
using static Microsoft.Extensions.Logging.ServiceBusEntityType;
@@ -24,20 +17,6 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump
{
public partial class ServiceBusMessagePumpTests
{
- [Fact]
- public async Task ServiceBusQueueMessagePumpWithEntityScopedConnectionString_PublishServiceBusMessageForHierarchical_MessageSuccessfullyProcessed()
- {
- await TestServiceBusMessageHandlingAsync(Queue, format: MessageCorrelationFormat.Hierarchical, configureOptions: options =>
- {
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt =>
- {
- opt.AutoComplete = true;
- opt.Routing.Correlation.Format = MessageCorrelationFormat.Hierarchical;
- })
- .WithServiceBusMessageHandler();
- });
- }
-
[Fact]
public async Task ServiceBusQueueMessagePumpWithEntityScopedConnectionString_PublishServiceBusMessage_MessageSuccessfullyProcessed()
{
@@ -51,14 +30,9 @@ await TestServiceBusMessageHandlingAsync(Queue, options =>
[Fact]
public async Task ServiceBusQueueMessagePumpWithNamespaceScopedConnectionString_PublishesServiceBusMessage_MessageSuccessfullyProcessed()
{
- // Arrange
- var properties = ServiceBusConnectionStringProperties.Parse(QueueConnectionString);
- string namespaceConnectionString = properties.GetNamespaceConnectionString();
-
- // Act / Assert
await TestServiceBusMessageHandlingAsync(Queue, options =>
{
- options.AddServiceBusQueueMessagePump(properties.EntityPath, _ => namespaceConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePump(QueueName, _ => NamespaceConnectionString, opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler();
});
}
@@ -74,127 +48,71 @@ await TestServiceBusMessageHandlingAsync(Topic, options =>
}
[Fact]
- public async Task ServiceBusTopicMessagePumpWithNamespaceScopedConnectionString_PublishesServiceBusMessage_MessageSuccessfullyProcessed()
- {
- // Arrange
- var properties = ServiceBusConnectionStringProperties.Parse(TopicConnectionString);
- string namespaceConnectionString = properties.GetNamespaceConnectionString();
-
- // Act / Assert
- await TestServiceBusMessageHandlingAsync(Topic, options =>
- {
- options.AddServiceBusTopicMessagePump(
- topicName: properties.EntityPath,
- subscriptionName: Guid.NewGuid().ToString(),
- getConnectionStringFromConfigurationFunc: _ => namespaceConnectionString,
- configureMessagePump: opt =>
- {
- opt.AutoComplete = true;
- opt.TopicSubscription = TopicSubscription.Automatic;
- })
- .WithServiceBusMessageHandler();
- });
- }
-
- [Fact]
- public async Task ServiceBusTopicMessagePumpUsingManagedIdentity_PublishServiceBusMessage_MessageSuccessfullyProcessed()
- {
- // Arrange
- ServiceBusConnectionStringProperties properties = ServiceBusConnectionStringProperties.Parse(TopicConnectionString);
- using var auth = TemporaryManagedIdentityConnection.Create(_config, _logger);
-
- // Act / Assert
- await TestServiceBusMessageHandlingAsync(Topic, options =>
- {
- options.AddServiceBusTopicMessagePumpUsingManagedIdentity(
- topicName: properties.EntityPath,
- subscriptionName: Guid.NewGuid().ToString(),
- serviceBusNamespace: properties.FullyQualifiedNamespace,
- clientId: auth.ClientId,
- configureMessagePump: opt =>
- {
- opt.AutoComplete = true;
- opt.TopicSubscription = TopicSubscription.Automatic;
- })
- .WithServiceBusMessageHandler();
- });
- }
-
- [Fact]
- public async Task ServiceBusQueueMessagePumpUsingManagedIdentity_PublishServiceBusMessage_MessageSuccessfullyProcessed()
- {
- // Arrange
- ServiceBusConnectionStringProperties properties = ServiceBusConnectionStringProperties.Parse(QueueConnectionString);
- using var auth = TemporaryManagedIdentityConnection.Create(_config, _logger);
-
- // Act / Assert
- await TestServiceBusMessageHandlingAsync(Queue, options =>
- {
- options.AddServiceBusQueueMessagePumpUsingManagedIdentity(
- queueName: properties.EntityPath,
- serviceBusNamespace: properties.FullyQualifiedNamespace,
- clientId: auth.ClientId,
- configureMessagePump: opt => opt.AutoComplete = true)
- .WithServiceBusMessageHandler();
- });
- }
-
- [Fact]
public async Task ServiceBusMessagePump_RotateServiceBusConnectionKeys_MessagePumpRestartsThenMessageSuccessfullyProcessed()
{
// Arrange
- string tenantId = _config.GetTenantId();
- KeyRotationConfig keyRotationConfig = _config.GetKeyRotationConfig();
- _logger.LogInformation("Using Service Principal [ClientID: '{ClientId}']", keyRotationConfig.ServicePrincipal.ClientId);
-
- var client = new ServiceBusConfiguration(keyRotationConfig, _logger);
- string freshConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.PrimaryKey);
+ await using TemporaryServiceBusNamespace serviceBus = await CreateServiceBusNamespaceAsync();
+ await using TemporaryServiceBusEntity queue = await CreateServiceBusQueueAsync(serviceBus.Config);
- SecretClient secretClient = CreateSecretClient(tenantId, keyRotationConfig);
- await SetConnectionStringInKeyVaultAsync(secretClient, keyRotationConfig, freshConnectionString);
+ ServiceBusAccessKeys keys = await serviceBus.GetAccessKeysAsync();
+ await using TemporaryKeyVaultSecret secret = await CreateKeyVaultSecretAsync(keys.PrimaryConnectionString);
var options = new WorkerOptions();
- options.AddSecretStore(stores => stores.AddAzureKeyVaultWithServicePrincipal(
- rawVaultUri: keyRotationConfig.KeyVault.VaultUri,
- tenantId: tenantId,
- clientId: keyRotationConfig.ServicePrincipal.ClientId,
- clientKey: keyRotationConfig.ServicePrincipal.ClientSecret,
- cacheConfiguration: CacheConfiguration.Default))
- .AddServiceBusQueueMessagePump(keyRotationConfig.KeyVault.SecretName, opt => opt.AutoComplete = true)
+ options.AddXunitTestLogging(_outputWriter)
+ .AddSecretStore(stores => AddAzureKeyVaultWithServicePrincipal(stores, _serviceBusConfig.ServicePrincipal))
+ .AddServiceBusQueueMessagePump(queue.EntityName, secret.Name)
.WithServiceBusMessageHandler();
- ServiceBusMessage message = CreateOrderServiceBusMessageForW3C();
+ await using var worker = await Worker.StartNewAsync(options);
- await using (var worker = await Worker.StartNewAsync(options))
- {
- string newSecondaryConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.SecondaryKey);
- await SetConnectionStringInKeyVaultAsync(secretClient, keyRotationConfig, newSecondaryConnectionString);
+ // Act
+ ServiceBusAccessKeys newKeys = await serviceBus.RotateAccessKeysAsync(ServiceBusAccessKeyType.PrimaryKey);
- // Act
- string newPrimaryConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.PrimaryKey);
+ // Assert
+ ServiceBusMessage message = CreateOrderServiceBusMessageForW3C();
+ var producer = new TestServiceBusMessageProducer(queue.EntityName, serviceBus.Config);
+ await producer.ProduceAsync(message);
- // Assert
- var producer = new TestServiceBusMessageProducer(newPrimaryConnectionString);
- await producer.ProduceAsync(message);
+ await secret.UpdateSecretAsync(newKeys.PrimaryConnectionString);
+
+ OrderCreatedEventData actual = await ConsumeOrderCreatedAsync(message.MessageId, TimeSpan.FromSeconds(20));
+ AssertReceivedOrderEventDataForW3C(message, actual);
+ }
- OrderCreatedEventData eventData = await ConsumeOrderCreatedAsync(message.MessageId);
- AssertReceivedOrderEventDataForW3C(message, eventData);
- }
+ private async Task CreateServiceBusNamespaceAsync()
+ {
+ return await TemporaryServiceBusNamespace.CreateBasicAsync(
+ _config.GetSubscriptionId(),
+ _config.GetResourceGroupName(),
+ _config.GetServicePrincipal(),
+ _logger);
}
- private static SecretClient CreateSecretClient(string tenantId, KeyRotationConfig keyRotationConfig)
+ private async Task CreateServiceBusQueueAsync(ServiceBusConfig serviceBus)
{
- var clientCredential = new ClientSecretCredential(tenantId,
- keyRotationConfig.ServicePrincipal.ClientId,
- keyRotationConfig.ServicePrincipal.ClientSecret);
-
- var secretClient = new SecretClient(new Uri(keyRotationConfig.KeyVault.VaultUri), clientCredential);
- return secretClient;
+ return await TemporaryServiceBusEntity.CreateAsync(
+ Queue,
+ $"queue-{Guid.NewGuid()}",
+ serviceBus,
+ _logger);
+ }
+
+ private async Task CreateKeyVaultSecretAsync(string secretValue)
+ {
+ return await TemporaryKeyVaultSecret.CreateAsync(
+ $"Queue-ConnectionString-{Guid.NewGuid()}",
+ secretValue,
+ _config.GetKeyVault(),
+ _logger);
}
- private static async Task SetConnectionStringInKeyVaultAsync(SecretClient keyVaultClient, KeyRotationConfig keyRotationConfig, string rotatedConnectionString)
+ private void AddAzureKeyVaultWithServicePrincipal(SecretStoreBuilder stores, ServicePrincipal servicePrincipal)
{
- await keyVaultClient.SetSecretAsync(keyRotationConfig.KeyVault.SecretName, rotatedConnectionString);
+ stores.AddAzureKeyVaultWithServicePrincipal(
+ _config.GetKeyVault().VaultUri,
+ servicePrincipal.TenantId,
+ servicePrincipal.ClientId,
+ servicePrincipal.ClientSecret);
}
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ResiliencyTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ResiliencyTests.cs
index 21819508..46252e74 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ResiliencyTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ResiliencyTests.cs
@@ -38,7 +38,7 @@ public async Task ServiceBusTopicMessagePump_PauseViaCircuitBreaker_RestartsAgai
options.AddXunitTestLogging(_outputWriter)
.AddServiceBusTopicMessagePump(
subscriptionName: "circuit-breaker-" + Guid.NewGuid(),
- _ => _config.GetServiceBusTopicConnectionString(),
+ _ => TopicConnectionString,
opt => opt.TopicSubscription = TopicSubscription.Automatic)
.WithServiceBusMessageHandler(
implementationFactory: provider => new CircuitBreakerAzureServiceBusMessageHandler(
@@ -50,7 +50,7 @@ public async Task ServiceBusTopicMessagePump_PauseViaCircuitBreaker_RestartsAgai
},
provider.GetRequiredService()));
- var producer = TestServiceBusMessageProducer.CreateFor(_config, ServiceBusEntityType.Topic);
+ var producer = TestServiceBusMessageProducer.CreateFor(QueueName, _config);
await using var worker = await Worker.StartNewAsync(options);
// Act
@@ -107,18 +107,18 @@ private static ServiceBusMessage[] GenerateShipmentMessages(int count)
}).ToArray();
}
- [Fact]
+ [Fact(Skip = "TODO: fixed upon circuit breaker changes")]
public async Task ServiceBusMessagePump_PauseViaLifetime_RestartsAgain()
{
// Arrange
- string connectionString = _config.GetServiceBusTopicConnectionString();
string jobId = Guid.NewGuid().ToString();
var options = new WorkerOptions();
options.AddXunitTestLogging(_outputWriter)
- .AddServiceBusTopicMessagePump(
+ .AddServiceBusTopicMessagePumpUsingManagedIdentity(
+ TopicName,
subscriptionName: Guid.NewGuid().ToString(),
- _ => connectionString,
- opt =>
+ HostName,
+ configureMessagePump: opt =>
{
opt.JobId = jobId;
opt.TopicSubscription = TopicSubscription.Automatic;
@@ -128,7 +128,7 @@ public async Task ServiceBusMessagePump_PauseViaLifetime_RestartsAgain()
ServiceBusMessage message = CreateOrderServiceBusMessageForW3C();
- var producer = TestServiceBusMessageProducer.CreateFor(_config, ServiceBusEntityType.Topic);
+ var producer = TestServiceBusMessageProducer.CreateFor(QueueName, _config);
await using var worker = await Worker.StartNewAsync(options);
var lifetime = worker.Services.GetRequiredService();
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.RouterTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.RouterTests.cs
index 6fab5e69..95f1c47d 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.RouterTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.RouterTests.cs
@@ -25,7 +25,7 @@ public async Task ServiceBusTopicMessagePumpWithBodyFiltering_RoutesServiceBusMe
{
await TestServiceBusMessageHandlingAsync(Topic, options =>
{
- options.AddServiceBusTopicMessagePump(TopicConnectionString)
+ options.AddServiceBusTopicMessagePumpUsingManagedIdentity(TopicName, HostName)
.WithServiceBusMessageHandler((Customer body) => body is null)
.WithServiceBusMessageHandler((Order body) => body.Id != null)
.WithMessageHandler((Order _) => false);
@@ -37,7 +37,7 @@ public async Task ServiceBusQueueMessagePumpWithContextAndBodyFilteringWithSeria
{
await TestServiceBusMessageHandlingAsync(Queue, options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler(messageContextFilter: _ => false)
.WithServiceBusMessageHandler(messageBodyFilter: _ => true)
.WithServiceBusMessageHandler(
@@ -56,7 +56,7 @@ public async Task ServiceBusQueueMessagePumpWithContextTypeFiltering_RoutesServi
{
await TestServiceBusMessageHandlingAsync(Queue, options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler()
.WithServiceBusMessageHandler();
});
@@ -67,7 +67,7 @@ public async Task ServiceBusQueueMessagePumpWithContextAndBodyFiltering_RoutesSe
{
// Arrange
var options = new WorkerOptions();
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler(context => context.Properties.ContainsKey("NotExisting"), _ => false)
.WithServiceBusMessageHandler(
context => context.Properties["Topic"].ToString() == "Orders",
@@ -89,7 +89,7 @@ public async Task ServiceBusTopicMessagePumpWithContextFiltering_RoutesServiceBu
{
// Arrange
var options = new WorkerOptions();
- options.AddServiceBusTopicMessagePump(TopicConnectionString)
+ options.AddServiceBusTopicMessagePumpUsingManagedIdentity(TopicName, HostName)
.WithServiceBusMessageHandler(context => context.Properties.TryGetValue("Topic", out object value) && value.ToString() == "Customers")
.WithServiceBusMessageHandler(context => context.Properties.TryGetValue("Topic", out object value) && value.ToString() == "Orders")
.WithMessageHandler((AzureServiceBusMessageContext _) => false);
@@ -110,7 +110,7 @@ public async Task ServiceBusQueueMessagePumpWithBatchedMessages_PublishServiceBu
{
await TestServiceBusMessageHandlingAsync(Queue, options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler(
messageBodySerializerImplementationFactory: serviceProvider =>
{
@@ -125,10 +125,11 @@ public async Task ServiceBusQueueMessagePumpWithIgnoringMissingMembersDeserializ
{
await TestServiceBusMessageHandlingAsync(Queue, options =>
{
- options.AddServiceBusQueueMessagePump(
- _ => QueueConnectionString,
- opt => opt.Routing.Deserialization.AdditionalMembers = AdditionalMemberHandling.Ignore)
- .WithServiceBusMessageHandler();
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt =>
+ {
+ opt.Routing.Deserialization.AdditionalMembers = AdditionalMemberHandling.Ignore;
+
+ }).WithServiceBusMessageHandler();
});
}
@@ -150,7 +151,7 @@ public async Task ServiceBusQueueMessagePump_PublishesEncodedServiceBusMessage_M
{
// Arrange
var options = new WorkerOptions();
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler();
ServiceBusMessage message = CreateOrderServiceBusMessageForW3C(encoding: encoding);
@@ -170,7 +171,7 @@ public async Task ServiceBusTopicMessagePump_PublishesEncodedServiceBusMessage_M
{
// Arrange
var options = new WorkerOptions();
- options.AddServiceBusTopicMessagePump(TopicConnectionString)
+ options.AddServiceBusTopicMessagePumpUsingManagedIdentity(TopicName, HostName)
.WithServiceBusMessageHandler();
ServiceBusMessage message = CreateOrderServiceBusMessageForW3C(encoding: encoding);
@@ -189,7 +190,7 @@ public async Task ServiceBusMessagePumpWithFallback_PublishServiceBusMessage_Mes
{
await TestServiceBusMessageHandlingAsync(Queue, options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler((AzureServiceBusMessageContext _) => false)
.WithFallbackMessageHandler();
});
@@ -200,7 +201,7 @@ public async Task ServiceBusMessagePumpWithServiceBusFallback_PublishServiceBusM
{
await TestServiceBusMessageHandlingAsync(Queue, options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler((AzureServiceBusMessageContext _) => false)
.WithServiceBusFallbackMessageHandler();
});
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ServiceBusTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ServiceBusTests.cs
index 4a70c2c5..afcf52bb 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ServiceBusTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.ServiceBusTests.cs
@@ -10,8 +10,8 @@
using Azure.Messaging.ServiceBus;
using Azure.Messaging.ServiceBus.Administration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
using Xunit;
+using static Microsoft.Extensions.Logging.ServiceBusEntityType;
namespace Arcus.Messaging.Tests.Integration.MessagePump
{
@@ -22,7 +22,7 @@ public async Task ServiceBusQueueMessagePumpWithServiceBusDeadLetter_PublishServ
{
await TestServiceBusQueueDeadLetteredMessageAsync(options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = false)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = false)
.WithServiceBusMessageHandler(context => context.Properties["Topic"].ToString() == "Customers")
.WithServiceBusMessageHandler()
.WithMessageHandler((AzureServiceBusMessageContext _) => false);
@@ -34,7 +34,7 @@ public async Task ServiceBusQueueMessagePumpWithServiceBusDeadLetterOnFallback_P
{
await TestServiceBusQueueDeadLetteredMessageAsync(options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = false)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = false)
.WithServiceBusMessageHandler((AzureServiceBusMessageContext _) => true)
.WithServiceBusFallbackMessageHandler();
});
@@ -48,9 +48,9 @@ private async Task TestServiceBusQueueDeadLetteredMessageAsync(Action
+ await TestServiceBusMessageHandlingAsync(options, Queue, message, async () =>
{
- var consumer = TestServiceMessageConsumer.CreateForQueue(_config, _logger);
+ TestServiceMessageConsumer consumer = CreateQueueConsumer();
await consumer.AssertDeadLetterMessageAsync(message.MessageId);
});
}
@@ -60,7 +60,7 @@ public async Task ServiceBusQueueMessagePumpWithServiceBusAbandon_PublishService
{
await TestServiceBusQueueAbandonMessageAsync(options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName)
.WithServiceBusMessageHandler((AzureServiceBusMessageContext _) => false)
.WithServiceBusMessageHandler((AzureServiceBusMessageContext _) => true);
});
@@ -71,7 +71,7 @@ public async Task ServiceBusQueueMessagePumpWithServiceBusAbandonOnFallback_Publ
{
await TestServiceBusQueueAbandonMessageAsync(options =>
{
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString)
+ options.AddServiceBusQueueMessagePump(QueueName, HostName)
.WithServiceBusMessageHandler()
.WithServiceBusFallbackMessageHandler();
});
@@ -86,10 +86,10 @@ private async Task TestServiceBusQueueAbandonMessageAsync(Action
ServiceBusMessage message = CreateOrderServiceBusMessageForW3C();
// Act
- await TestServiceBusMessageHandlingAsync(options, ServiceBusEntityType.Queue, message, async () =>
+ await TestServiceBusMessageHandlingAsync(options, Queue, message, async () =>
{
// Assert
- var consumer = TestServiceMessageConsumer.CreateForQueue(_config, _logger);
+ TestServiceMessageConsumer consumer = CreateQueueConsumer();
await consumer.AssertAbandonMessageAsync(message.MessageId);
});
}
@@ -99,17 +99,17 @@ public async Task ServiceBusQueueMessagePumpWithCustomCompleteOnFallback_Publish
{
// Arrange
var options = new WorkerOptions();
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = false)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = false)
.WithServiceBusMessageHandler()
.WithServiceBusFallbackMessageHandler();
ServiceBusMessage message = CreateOrderServiceBusMessageForW3C();
// Act
- await TestServiceBusMessageHandlingAsync(options, ServiceBusEntityType.Queue, message, async () =>
+ await TestServiceBusMessageHandlingAsync(options, Queue, message, async () =>
{
// Assert
- var consumer = TestServiceMessageConsumer.CreateForQueue(_config, _logger);
+ TestServiceMessageConsumer consumer = CreateQueueConsumer();
await consumer.AssertCompletedMessageAsync(message.MessageId);
});
}
@@ -119,20 +119,26 @@ public async Task ServiceBusTopicMessagePumpWithCustomComplete_PublishServiceBus
{
// Arrange
var options = new WorkerOptions();
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt => opt.AutoComplete = false)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = false)
.WithServiceBusMessageHandler();
ServiceBusMessage message = CreateOrderServiceBusMessageForW3C();
// Act
- await TestServiceBusMessageHandlingAsync(options, ServiceBusEntityType.Queue, message, async () =>
+ await TestServiceBusMessageHandlingAsync(options, Queue, message, async () =>
{
// Assert
- var consumer = TestServiceMessageConsumer.CreateForQueue(_config, _logger);
+ TestServiceMessageConsumer consumer = CreateQueueConsumer();
await consumer.AssertCompletedMessageAsync(message.MessageId);
});
}
+ private TestServiceMessageConsumer CreateQueueConsumer()
+ {
+ var consumer = TestServiceMessageConsumer.CreateForQueue(QueueName, _serviceBusConfig, _logger);
+ return consumer;
+ }
+
[Theory]
[InlineData(TopicSubscription.None, false)]
[InlineData(TopicSubscription.Automatic, true)]
@@ -141,32 +147,32 @@ public async Task ServiceBusTopicMessagePump_WithNoneTopicSubscription_DoesNotCr
// Arrange
var options = new WorkerOptions();
var subscriptionName = $"Subscription-{Guid.NewGuid():N}";
- options.AddServiceBusTopicMessagePump(
+ options.AddServiceBusTopicMessagePumpUsingManagedIdentity(
+ TopicName,
subscriptionName,
- _ => TopicConnectionString,
- opt => opt.TopicSubscription = topicSubscription)
+ HostName,
+ configureMessagePump: opt => opt.TopicSubscription = topicSubscription)
.WithServiceBusMessageHandler();
// Act
await using var worker = await Worker.StartNewAsync(options);
// Assert
- var client = new ServiceBusAdministrationClient(TopicConnectionString);
- var properties = ServiceBusConnectionStringProperties.Parse(TopicConnectionString);
-
- Response subscriptionExistsResponse = await client.SubscriptionExistsAsync(properties.EntityPath, subscriptionName);
+ ServiceBusAdministrationClient client = _serviceBusConfig.GetAdminClient();
+ Response subscriptionExistsResponse = await client.SubscriptionExistsAsync(TopicName, subscriptionName);
Assert.Equal(doesSubscriptionExists, subscriptionExistsResponse.Value);
}
[Fact]
public async Task ServiceBusTopicMessagePumpWithSubscriptionNameOver50_PublishServiceBusMessage_MessageSuccessfullyProcessed()
{
- await TestServiceBusMessageHandlingAsync(ServiceBusEntityType.Topic, options =>
+ await TestServiceBusMessageHandlingAsync(Topic, options =>
{
- options.AddServiceBusTopicMessagePump(
+ options.AddServiceBusTopicMessagePumpUsingManagedIdentity(
+ TopicName,
subscriptionName: "Test-Receive-All-Topic-Only-with-an-azure-servicebus-topic-subscription-name-over-50-characters",
- _ => TopicConnectionString,
- opt =>
+ HostName,
+ configureMessagePump: opt =>
{
opt.AutoComplete = true;
opt.TopicSubscription = TopicSubscription.Automatic;
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.TelemetryTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.TelemetryTests.cs
index 4e6e0f33..a6f6dd2c 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.TelemetryTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePump.TelemetryTests.cs
@@ -39,10 +39,11 @@ public async Task ServiceBusMessagePump_WithW3CCorrelationFormat_AutomaticallyTr
var options = new WorkerOptions();
string operationName = Guid.NewGuid().ToString();
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt =>
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt =>
{
opt.AutoComplete = true;
opt.Routing.Telemetry.OperationName = operationName;
+
}).WithServiceBusMessageHandler();
var spySink = new InMemoryApplicationInsightsTelemetryConverter();
@@ -73,10 +74,11 @@ public async Task ServiceBusMessagePump_WithW3CCorrelationFormatForNewParent_Aut
var options = new WorkerOptions();
string operationName = Guid.NewGuid().ToString();
- options.AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt =>
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt =>
{
opt.Routing.Telemetry.OperationName = operationName;
opt.AutoComplete = true;
+
}).WithServiceBusMessageHandler();
var spySink = new InMemoryApplicationInsightsTelemetryConverter();
@@ -143,12 +145,12 @@ public async Task ServiceBusMessagePump_FailureDuringMessageHandling_TracksCorre
var spySink = new InMemoryLogSink();
var options = new WorkerOptions();
options.ConfigureSerilog(config => config.WriteTo.Sink(spySink))
- .AddServiceBusQueueMessagePump(_ => QueueConnectionString, opt =>
+ .AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt =>
{
opt.AutoComplete = true;
opt.Routing.Correlation.Format = MessageCorrelationFormat.Hierarchical;
- })
- .WithServiceBusMessageHandler();
+
+ }).WithServiceBusMessageHandler();
string operationId = $"operation-{Guid.NewGuid()}", transactionId = $"transaction-{Guid.NewGuid()}";
Order order = OrderGenerator.Generate();
@@ -180,15 +182,15 @@ public async Task ServiceBusTopicMessagePump_WithCustomTransactionIdProperty_Ret
// Arrange
var customTransactionIdPropertyName = "MyTransactionId";
var options = new WorkerOptions();
- options.AddServiceBusTopicMessagePump($"MySubscription-{Guid.NewGuid():N}", _ => TopicConnectionString,
- opt =>
- {
- opt.AutoComplete = true;
- opt.TopicSubscription = TopicSubscription.Automatic;
- opt.Routing.Correlation.Format = MessageCorrelationFormat.Hierarchical;
- opt.Routing.Correlation.TransactionIdPropertyName = customTransactionIdPropertyName;
- })
- .WithServiceBusMessageHandler();
+ options.AddServiceBusTopicMessagePumpUsingManagedIdentity(TopicName, $"MySubscription-{Guid.NewGuid():N}", HostName,
+ configureMessagePump: opt =>
+ {
+ opt.AutoComplete = true;
+ opt.TopicSubscription = TopicSubscription.Automatic;
+ opt.Routing.Correlation.Format = MessageCorrelationFormat.Hierarchical;
+ opt.Routing.Correlation.TransactionIdPropertyName = customTransactionIdPropertyName;
+
+ }).WithServiceBusMessageHandler();
ServiceBusMessage message = CreateOrderServiceBusMessageForHierarchical(customTransactionIdPropertyName);
@@ -206,20 +208,19 @@ public async Task ServiceBusQueueMessagePump_WithCustomOperationParentIdProperty
// Arrange
var customOperationParentIdPropertyName = "MyOperationParentId";
var options = new WorkerOptions();
- options.AddServiceBusQueueMessagePump(
- _ => QueueConnectionString,
- opt =>
- {
- opt.AutoComplete = true;
- opt.Routing.Correlation.Format = MessageCorrelationFormat.Hierarchical;
- opt.Routing.Correlation.OperationParentIdPropertyName = customOperationParentIdPropertyName;
- })
- .WithServiceBusMessageHandler();
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName,
+ configureMessagePump: opt =>
+ {
+ opt.AutoComplete = true;
+ opt.Routing.Correlation.Format = MessageCorrelationFormat.Hierarchical;
+ opt.Routing.Correlation.OperationParentIdPropertyName = customOperationParentIdPropertyName;
+
+ }).WithServiceBusMessageHandler();
ServiceBusMessage message = CreateOrderServiceBusMessageForHierarchical(operationParentIdPropertyName: customOperationParentIdPropertyName);
// Act / Assert
- await TestServiceBusMessageHandlingAsync(options, Queue, message, async () =>
+ await TestServiceBusMessageHandlingAsync(options, ServiceBusEntityType.Topic, message, async () =>
{
OrderCreatedEventData eventData = await ConsumeOrderCreatedAsync(message.MessageId);
AssertReceivedOrderEventDataForHierarchical(message, eventData, operationParentIdPropertyName: customOperationParentIdPropertyName);
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePumpDockerTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePumpDockerTests.cs
deleted file mode 100644
index e9127c6d..00000000
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePumpDockerTests.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Arcus.Messaging.Tests.Core.Correlation;
-using Arcus.Messaging.Tests.Core.Events.v1;
-using Arcus.Messaging.Tests.Core.Generators;
-using Azure.Messaging.ServiceBus;
-using Arcus.Messaging.Tests.Core.Messages.v1;
-using Arcus.Messaging.Tests.Integration.Fixture;
-using Arcus.Messaging.Tests.Integration.MessagePump.Fixture;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Arcus.Messaging.Tests.Integration.MessagePump
-{
- [Collection("Docker")]
- [Trait("Category", "Docker")]
- public class ServiceBusMessagePumpDockerTests : DockerServiceBusIntegrationTest
- {
- ///
- /// Initializes a new instance of the class.
- ///
- public ServiceBusMessagePumpDockerTests(ITestOutputHelper testOutput) : base(testOutput)
- {
- }
-
- [Theory]
- [InlineData("Arcus:ServiceBus:Docker:ConnectionStringWithQueue")]
- [InlineData("Arcus:ServiceBus:Docker:ConnectionStringWithTopic")]
- public async Task ServiceBusTopicMessagePump_PublishServiceBusMessage_MessageSuccessfullyProcessed(string connectionString)
- {
- // Arrange
- var traceParent = TraceParent.Generate();
- Order order = OrderGenerator.Generate();
-
- var orderMessage = new ServiceBusMessage(BinaryData.FromObjectAsJson(order))
- .WithDiagnosticId(traceParent);
-
- // Act
- await SenderOrderToServiceBusAsync(orderMessage, connectionString);
-
- // Assert
- OrderCreatedEventData orderCreatedEventData = ReceiveOrderFromEventGrid(traceParent.TransactionId);
- Assert.NotNull(orderCreatedEventData);
- Assert.NotNull(orderCreatedEventData.CorrelationInfo);
- Assert.Equal(order.Id, orderCreatedEventData.Id);
- Assert.Equal(order.Amount, orderCreatedEventData.Amount);
- Assert.Equal(order.ArticleNumber, orderCreatedEventData.ArticleNumber);
- Assert.Equal(traceParent.TransactionId, orderCreatedEventData.CorrelationInfo.TransactionId);
- Assert.Equal(traceParent.OperationParentId, orderCreatedEventData.CorrelationInfo.OperationParentId);
- Assert.NotNull(orderCreatedEventData.CorrelationInfo.OperationId);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePumpTests.cs b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePumpTests.cs
index 526dc549..55bf56b2 100644
--- a/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePumpTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/MessagePump/ServiceBusMessagePumpTests.cs
@@ -1,9 +1,11 @@
using System;
+using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Arcus.Messaging.Abstractions;
using Arcus.Messaging.Abstractions.MessageHandling;
+using Arcus.Messaging.Pumps.ServiceBus;
using Arcus.Messaging.Tests.Core.Correlation;
using Arcus.Messaging.Tests.Core.Events.v1;
using Arcus.Messaging.Tests.Core.Generators;
@@ -12,15 +14,15 @@
using Arcus.Messaging.Tests.Integration.MessagePump.Fixture;
using Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus;
using Arcus.Messaging.Tests.Workers.MessageHandlers;
+using Arcus.Testing;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using Xunit;
using Xunit.Abstractions;
-using ArgumentOutOfRangeException = System.ArgumentOutOfRangeException;
-using TestConfig = Arcus.Messaging.Tests.Integration.Fixture.TestConfig;
-using XunitTestLogger = Arcus.Testing.Logging.XunitTestLogger;
using static Microsoft.Extensions.Logging.ServiceBusEntityType;
using static Arcus.Messaging.Tests.Integration.MessagePump.ServiceBus.DiskMessageEventConsumer;
@@ -28,37 +30,45 @@ namespace Arcus.Messaging.Tests.Integration.MessagePump
{
[Collection("Integration")]
[Trait("Category", "Integration")]
- public partial class ServiceBusMessagePumpTests
+ public partial class ServiceBusMessagePumpTests : IClassFixture, IDisposable
{
private readonly TestConfig _config;
+ private readonly ServiceBusConfig _serviceBusConfig;
+ private readonly TemporaryManagedIdentityConnection _connection;
private readonly ILogger _logger;
private readonly ITestOutputHelper _outputWriter;
///
/// Initializes a new instance of the class.
///
- public ServiceBusMessagePumpTests(ITestOutputHelper outputWriter)
+ public ServiceBusMessagePumpTests(ServiceBusEntityFixture entity, ITestOutputHelper outputWriter)
{
_config = TestConfig.Create();
+ _serviceBusConfig = _config.GetServiceBus();
+
_outputWriter = outputWriter;
_logger = new XunitTestLogger(outputWriter);
+ _connection = TemporaryManagedIdentityConnection.Create(_config, _logger);
+
+ QueueName = entity.QueueName;
+ TopicName = entity.TopicName;
}
- private string QueueConnectionString => _config.GetServiceBusQueueConnectionString();
- private string TopicConnectionString => _config.GetServiceBusTopicConnectionString();
+ private string QueueName { get; }
+ private string TopicName { get; }
+ private string HostName => _serviceBusConfig.HostName;
+ private string NamespaceConnectionString => _serviceBusConfig.NamespaceConnectionString;
+ private string QueueConnectionString => $"{_serviceBusConfig.NamespaceConnectionString};EntityPath={QueueName}";
+ private string TopicConnectionString => $"{_serviceBusConfig.NamespaceConnectionString};EntityPath={TopicName}";
[Fact(Skip = ".NET application cannot start multiple blocking background tasks, see https://github.com/dotnet/runtime/issues/36063")]
public async Task ServiceBusMessagePumpWithQueueAndTopic_PublishServiceBusMessage_MessageSuccessfullyProcessed()
{
// Arrange
- string connectionString = _config.GetServiceBusQueueConnectionString();
var options = new WorkerOptions();
- options.AddServiceBusQueueMessagePump(_ => connectionString, opt => opt.AutoComplete = true)
+ options.AddServiceBusQueueMessagePumpUsingManagedIdentity(QueueName, HostName, configureMessagePump: opt => opt.AutoComplete = true)
.WithServiceBusMessageHandler();
- options.AddServiceBusTopicMessagePump(
- subscriptionName: Guid.NewGuid().ToString(),
- _ => _config.GetServiceBusConnectionString(Topic),
- opt => opt.AutoComplete = true)
+ options.AddServiceBusTopicMessagePumpUsingManagedIdentity(TopicName, HostName)
.WithServiceBusMessageHandler();
// Act / Assert
@@ -125,7 +135,14 @@ private async Task TestServiceBusMessageHandlingAsync(
options.AddXunitTestLogging(_outputWriter);
await using var worker = await Worker.StartNewAsync(options, memberName);
- var producer = TestServiceBusMessageProducer.CreateFor(_config, entityType);
+ ServiceBusEntityType registeredEntityType = DetermineRegisteredEntityType(worker);
+
+ var producer = TestServiceBusMessageProducer.CreateFor(registeredEntityType switch
+ {
+ Queue => QueueName,
+ Topic => TopicName,
+ _ => throw new ArgumentOutOfRangeException(nameof(entityType), entityType, "Unknown Service bus entity type")
+ }, _config);
// Act
await producer.ProduceAsync(message);
@@ -134,6 +151,19 @@ private async Task TestServiceBusMessageHandlingAsync(
await assertionAsync();
}
+ private static ServiceBusEntityType DetermineRegisteredEntityType(Worker worker)
+ {
+ ServiceBusEntityType registeredEntityType =
+ Assert.Single(
+ worker.Services.GetServices()
+ .Where(h => h is AzureServiceBusMessagePump)
+ .Cast()
+ .Select(m => m.Settings.ServiceBusEntity)
+ .ToArray());
+
+ return registeredEntityType;
+ }
+
private static ServiceBusMessage CreateOrderServiceBusMessageForHierarchical(
string transactionIdPropertyName = PropertyNames.TransactionId,
string operationParentIdPropertyName = PropertyNames.OperationParentId,
@@ -221,5 +251,35 @@ private static void AssertReceivedOrderEventDataForW3C(
Assert.NotNull(receivedEventData.CorrelationInfo.OperationId);
Assert.Equal(operationParentId, receivedEventData.CorrelationInfo.OperationParentId);
}
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ _connection?.Dispose();
+ }
+ }
+
+ public class ServiceBusEntityFixture : IAsyncLifetime
+ {
+ private TemporaryServiceBusEntity _queue, _topic;
+
+ public string QueueName { get; } = $"queue-{Guid.NewGuid()}";
+ public string TopicName { get; } = $"topic-{Guid.NewGuid()}";
+
+ public async Task InitializeAsync()
+ {
+ var config = TestConfig.Create().GetServiceBus();
+ _topic = await TemporaryServiceBusEntity.CreateAsync(Topic, TopicName, config, NullLogger.Instance);
+ _queue = await TemporaryServiceBusEntity.CreateAsync(Queue, QueueName, config, NullLogger.Instance);
+ }
+
+ public async Task DisposeAsync()
+ {
+ await using var disposables = new DisposableCollection(NullLogger.Instance);
+ disposables.Add(_queue);
+ disposables.Add(_topic);
+ }
}
}
\ No newline at end of file
diff --git a/src/Arcus.Messaging.Tests.Integration/ServiceBus/AzureClientFactoryBuilderExtensionsTests.cs b/src/Arcus.Messaging.Tests.Integration/ServiceBus/AzureClientFactoryBuilderExtensionsTests.cs
index e38d31cd..504074ee 100644
--- a/src/Arcus.Messaging.Tests.Integration/ServiceBus/AzureClientFactoryBuilderExtensionsTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/ServiceBus/AzureClientFactoryBuilderExtensionsTests.cs
@@ -5,26 +5,34 @@
using Arcus.Messaging.Tests.Core.Generators;
using Arcus.Messaging.Tests.Core.Messages.v1;
using Arcus.Messaging.Tests.Integration.Fixture;
+using Arcus.Testing;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using Polly;
using Polly.Wrap;
using Xunit;
+using Xunit.Abstractions;
namespace Arcus.Messaging.Tests.Integration.ServiceBus
{
- public class AzureClientFactoryBuilderExtensionsTests
+ public class AzureClientFactoryBuilderExtensionsTests : IAsyncLifetime
{
private readonly TestConfig _config;
+ private readonly ILogger _logger;
+
+ private TemporaryManagedIdentityConnection _connection;
+ private TemporaryServiceBusEntity _queue;
///
/// Initializes a new instance of the class.
///
- public AzureClientFactoryBuilderExtensionsTests()
+ public AzureClientFactoryBuilderExtensionsTests(ITestOutputHelper outputWriter)
{
_config = TestConfig.Create();
+ _logger = new XunitTestLogger(outputWriter);
}
[Fact]
@@ -33,7 +41,7 @@ public async Task AddServiceBusClient_SendsMessage_Succeeds()
// Arrange
var services = new ServiceCollection();
var connectionStringSecretName = "MyConnectionString";
- string connectionString = _config.GetServiceBusQueueConnectionString();
+ string connectionString = _config.GetServiceBus().NamespaceConnectionString + ";EntityPath=" + _queue.EntityName;
var connectionStringProperties = ServiceBusConnectionStringProperties.Parse(connectionString);
services.AddSecretStore(stores => stores.AddInMemory(connectionStringSecretName, connectionString));
@@ -91,5 +99,17 @@ await policy.ExecuteAsync(async () =>
});
}
}
+
+ public async Task InitializeAsync()
+ {
+ _connection = TemporaryManagedIdentityConnection.Create(_config, _logger);
+ _queue = await TemporaryServiceBusEntity.CreateAsync(ServiceBusEntityType.Queue, $"queue-{Guid.NewGuid()}", _config.GetServiceBus(), _logger);
+ }
+
+ public async Task DisposeAsync()
+ {
+ await _queue.DisposeAsync();
+ _connection.Dispose();
+ }
}
}
diff --git a/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusConfiguration.cs b/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusConfiguration.cs
index da9c03c9..2c303435 100644
--- a/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusConfiguration.cs
+++ b/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusConfiguration.cs
@@ -110,11 +110,12 @@ private async Task CreateServiceManagementClientAsy
string tenantId = _configuration.ServiceBusNamespace.TenantId;
var context = new AuthenticationContext($"https://login.microsoftonline.com/{tenantId}");
- ClientCredential clientCredentials = _configuration.ServicePrincipal.CreateCredentials();
AuthenticationResult result =
await context.AcquireTokenAsync(
- "https://management.azure.com/",
- clientCredentials);
+ "https://management.azure.com/",
+ new ClientCredential(
+ _configuration.ServicePrincipal.ClientId,
+ _configuration.ServicePrincipal.ClientSecret));
var tokenCredentials = new TokenCredentials(result.AccessToken);
string subscriptionId = _configuration.ServiceBusNamespace.SubscriptionId;
diff --git a/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusSenderExtensionsTests.cs b/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusSenderExtensionsTests.cs
index 42543ab2..61a56d56 100644
--- a/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusSenderExtensionsTests.cs
+++ b/src/Arcus.Messaging.Tests.Integration/ServiceBus/ServiceBusSenderExtensionsTests.cs
@@ -6,7 +6,8 @@
using Arcus.Messaging.Tests.Core.Generators;
using Arcus.Messaging.Tests.Core.Messages.v1;
using Arcus.Messaging.Tests.Integration.Fixture;
-using Arcus.Testing.Logging;
+using Arcus.Messaging.Tests.Integration.MessagePump;
+using Arcus.Testing;
using Azure.Messaging.ServiceBus;
using Polly;
using Polly.Wrap;
@@ -14,7 +15,7 @@
namespace Arcus.Messaging.Tests.Integration.ServiceBus
{
- public class ServiceBusSenderExtensionsTests
+ public class ServiceBusSenderExtensionsTests : IClassFixture
{
private const string DependencyIdPattern = @"with ID [a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}";
@@ -23,11 +24,15 @@ public class ServiceBusSenderExtensionsTests
///
/// Initializes a new instance of the class.
///
- public ServiceBusSenderExtensionsTests()
+ public ServiceBusSenderExtensionsTests(ServiceBusEntityFixture fixture)
{
_config = TestConfig.Create();
+
+ QueueName = fixture.QueueName;
}
+ private string QueueName { get; }
+
[Fact]
public async Task SendMessage_WithMessageCorrelation_TracksMessage()
{
@@ -36,31 +41,26 @@ public async Task SendMessage_WithMessageCorrelation_TracksMessage()
MessageCorrelationInfo correlation = GenerateMessageCorrelationInfo();
var logger = new InMemoryLogger();
- string connectionString = _config.GetServiceBusQueueConnectionString();
- var connectionStringProperties = ServiceBusConnectionStringProperties.Parse(connectionString);
-
- await using (var client = new ServiceBusClient(connectionString))
+ await using ServiceBusClient client = _config.GetServiceBus().GetClient();
+ await using (ServiceBusSender sender = client.CreateSender(QueueName))
{
- await using (ServiceBusSender sender = client.CreateSender(connectionStringProperties.EntityPath))
- {
- // Act
- await sender.SendMessageAsync(order, correlation, logger);
- }
-
- // Assert
- await RetryAssertUntilServiceBusMessageIsAvailableAsync(client, connectionStringProperties.EntityPath, message =>
- {
- var actual = message.Body.ToObjectFromJson();
- Assert.Equal(order.Id, actual.Id);
-
- Assert.Equal(message.ApplicationProperties[PropertyNames.TransactionId], correlation.TransactionId);
- Assert.False(string.IsNullOrWhiteSpace(message.ApplicationProperties[PropertyNames.OperationParentId].ToString()));
-
- string logMessage = Assert.Single(logger.Messages);
- Assert.Contains("Dependency", logMessage);
- Assert.Matches(DependencyIdPattern, logMessage);
- });
+ // Act
+ await sender.SendMessageAsync(order, correlation, logger);
}
+
+ // Assert
+ await RetryAssertUntilServiceBusMessageIsAvailableAsync(client, QueueName, message =>
+ {
+ var actual = message.Body.ToObjectFromJson();
+ Assert.Equal(order.Id, actual.Id);
+
+ Assert.Equal(message.ApplicationProperties[PropertyNames.TransactionId], correlation.TransactionId);
+ Assert.False(string.IsNullOrWhiteSpace(message.ApplicationProperties[PropertyNames.OperationParentId].ToString()));
+
+ string logMessage = Assert.Single(logger.Messages);
+ Assert.Contains("Dependency", logMessage);
+ Assert.Matches(DependencyIdPattern, logMessage);
+ });
}
[Fact]
@@ -75,38 +75,33 @@ public async Task SendMessage_WithCustomOptions_TracksMessage()
var telemetryContext = new Dictionary { [key] = value };
var logger = new InMemoryLogger();
- string connectionString = _config.GetServiceBusQueueConnectionString();
- var connectionStringProperties = ServiceBusConnectionStringProperties.Parse(connectionString);
-
- await using (var client = new ServiceBusClient(connectionString))
+ await using ServiceBusClient client = _config.GetServiceBus().GetClient();
+ await using (ServiceBusSender sender = client.CreateSender(QueueName))
{
- await using (ServiceBusSender sender = client.CreateSender(connectionStringProperties.EntityPath))
- {
- // Act
- await sender.SendMessageAsync(order, correlation, logger, options =>
- {
- options.TransactionIdPropertyName = transactionIdPropertyName;
- options.UpstreamServicePropertyName = upstreamServicePropertyName;
- options.GenerateDependencyId = () => dependencyId;
- options.AddTelemetryContext(telemetryContext);
- });
- }
-
- // Assert
- string logMessage = Assert.Single(logger.Messages);
- Assert.Contains("Dependency", logMessage);
- Assert.Matches($"with ID {dependencyId}", logMessage);
- Assert.Contains(key, logMessage);
- Assert.Contains(value, logMessage);
- await RetryAssertUntilServiceBusMessageIsAvailableAsync(client, connectionStringProperties.EntityPath, message =>
+ // Act
+ await sender.SendMessageAsync(order, correlation, logger, options =>
{
- var actual = message.Body.ToObjectFromJson();
- Assert.Equal(order.Id, actual.Id);
-
- Assert.Equal(correlation.TransactionId, message.ApplicationProperties[transactionIdPropertyName]);
- Assert.Equal(dependencyId, message.ApplicationProperties[upstreamServicePropertyName]);
+ options.TransactionIdPropertyName = transactionIdPropertyName;
+ options.UpstreamServicePropertyName = upstreamServicePropertyName;
+ options.GenerateDependencyId = () => dependencyId;
+ options.AddTelemetryContext(telemetryContext);
});
}
+
+ // Assert
+ string logMessage = Assert.Single(logger.Messages);
+ Assert.Contains("Dependency", logMessage);
+ Assert.Matches($"with ID {dependencyId}", logMessage);
+ Assert.Contains(key, logMessage);
+ Assert.Contains(value, logMessage);
+ await RetryAssertUntilServiceBusMessageIsAvailableAsync(client, QueueName, message =>
+ {
+ var actual = message.Body.ToObjectFromJson();
+ Assert.Equal(order.Id, actual.Id);
+
+ Assert.Equal(correlation.TransactionId, message.ApplicationProperties[transactionIdPropertyName]);
+ Assert.Equal(dependencyId, message.ApplicationProperties[upstreamServicePropertyName]);
+ });
}
private static MessageCorrelationInfo GenerateMessageCorrelationInfo()
diff --git a/src/Arcus.Messaging.Tests.Integration/appsettings.json b/src/Arcus.Messaging.Tests.Integration/appsettings.json
index 8fd649fd..6832e6d7 100644
--- a/src/Arcus.Messaging.Tests.Integration/appsettings.json
+++ b/src/Arcus.Messaging.Tests.Integration/appsettings.json
@@ -1,81 +1,35 @@
{
"Arcus": {
- "Health": {
- "Port": "#{Arcus.Health.Port.Queue}#"
- },
"Infra": {
- "ServiceBus": {
- "TopicName": "#{Arcus.TestInfra.ServiceBus.Topic.Name}#",
- "ConnectionString": "#{Arcus.TestInfra.ServiceBus.Topic.ConnectionString}#"
- },
- "EventGrid": {
- "TopicUri": "#{Arcus.TestInfra.EventGrid.Topic.Uri}#",
- "AuthKey": "#{Arcus.TestInfra.EventGrid.Auth.Key}#"
- },
+ "SubscriptionId": "#{Arcus.Infra.SubscriptionId}#",
"TenantId": "#{Arcus.Infra.TenantId}#",
+ "ResourceGroup": {
+ "Name": "#{Arcus.Messaging.ResourceGroup.Name}#"
+ },
"ServicePrincipal": {
"ClientId": "#{Arcus.Infra.ServicePrincipal.ClientId}#",
+ "ObjectId": "#{Arcus.Infra.ServicePrincipal.ObjectId}#",
"ClientSecret": "#{Arcus.Infra.ServicePrincipal.ClientSecret}#"
}
},
+
+ "StorageAccount": {
+ "Name": "#{Arcus.Messaging.StorageAccount.Name}#",
+ "Key": "#{Arcus.Messaging.StorageAccount.Key}#"
+ },
+
+ "KeyVault": {
+ "Name": "#{Arcus.Messaging.KeyVault.Name}#"
+ },
+
"ServiceBus": {
- "Docker": {
- "ConnectionStringWithQueue": "#{Arcus.ServiceBus.Docker.ConnectionStringWithQueue}#",
- "ConnectionStringWithTopic": "#{Arcus.ServiceBus.Docker.ConnectionStringWithTopic}#",
- "NamespaceConnectionString": "#{Arcus.ServiceBus.Docker.AzureFunctions.NamespaceConnectionString}#",
- "AzureFunctions": {
- "ConnectionStringWithQueue": "#{Arcus.ServiceBus.Docker.AzureFunctions.ConnectionStringWithQueue}#",
- "ConnectionStringWithTopic": "#{Arcus.ServiceBus.Docker.AzureFunctions.ConnectionStringWithTopic}#"
- }
- },
- "SelfContained": {
- "ConnectionStringWithQueue": "#{Arcus.ServiceBus.ConnectionStringWithQueue}#",
- "ConnectionStringWithTopic": "#{Arcus.ServiceBus.ConnectionStringWithTopic}#"
- }
+ "Namespace": "#{Arcus.Messaging.ServiceBus.Namespace}#",
+ "ConnectionString": "#{Arcus.Messaging.ServiceBus.ConnectionString}#"
},
+
"EventHubs": {
- "ConnectionString": "#{Arcus.EventHubs.ConnectionString}#",
- "BlobStorage": {
- "StorageAccountConnectionString": "#{Arcus.EventHubs.BlobStorage.StorageAccountConnectionString}#"
- },
- "SelfContained": {
- "EventHubsName": "#{Arcus.EventHubs.SelfContained.EventHubsName}#"
- },
- "Docker": {
- "EventHubsName": "#{Arcus.EventHubs.Docker.EventHubsName}#",
- "AzureFunctions": {
- "Isolated": {
- "EventHubsName": "#{Arcus.EventHubs.Docker.AzureFunctions.Isolated.EventHubsName}#"
- },
- "InProcess": {
- "EventHubsName": "#{Arcus.EventHubs.Docker.AzureFunctions.InProcess.EventHubsName}#"
- }
- }
- }
- },
- "KeyRotation": {
- "ServicePrincipal": {
- "ClientId": "#{Arcus.KeyRotation.ServicePrincipal.ClientId}#",
- "ClientSecret": "#{Arcus.KeyRotation.ServicePrincipal.ClientSecret}#",
- "ClientSecretKey": "#{Arcus.KeyRotation.ServicePrincipal.ClientSecretKey}#"
- },
- "ServiceBus": {
- "ResourceGroupName": "#{Arcus.KeyRotation.ServiceBus.ResourceGroupName}#",
- "TenantId": "#{Arcus.KeyRotation.ServiceBus.TenantId}#",
- "SubscriptionId": "#{Arcus.KeyRotation.ServiceBus.SubscriptionId}#",
- "Namespace": "#{Arcus.KeyRotation.ServiceBus.Namespace}#",
- "QueueName": "#{Arcus.KeyRotation.ServiceBus.QueueName}#",
- "TopicName": "#{Arcus.KeyRotation.ServiceBus.TopicName}#",
- "AuthorizationRuleName": "#{Arcus.KeyRotation.ServiceBus.AuthorizationRuleName}#"
- },
- "KeyVault": {
- "VaultUri": "#{Arcus.KeyRotation.KeyVault.VaultUri}#",
- "ConnectionStringSecretName": "#{Arcus.KeyRotation.KeyVault.ConnectionStringSecretName}#",
- "SecretNewVersionCreated": {
- "ServiceBusConnectionStringWithTopicEndpoint": "#{Arcus.KeyRotation.KeyVault.SecretNewVersionCreated.ServiceBusConnectionStringWithTopicEndpoint}#"
- }
- }
- }
- },
- "Build.SourcesDirectory": "#{Build.SourcesDirectory}#"
+ "Namespace": "#{Arcus.Messaging.EventHubs.Namespace}#",
+ "ConnectionString": "#{Arcus.Messaging.EventHubs.ConnectionString}#"
+ }
+ }
}
diff --git a/src/Arcus.Messaging.Tests.Unit/Arcus.Messaging.Tests.Unit.csproj b/src/Arcus.Messaging.Tests.Unit/Arcus.Messaging.Tests.Unit.csproj
index 24e189d2..e7690bb1 100644
--- a/src/Arcus.Messaging.Tests.Unit/Arcus.Messaging.Tests.Unit.csproj
+++ b/src/Arcus.Messaging.Tests.Unit/Arcus.Messaging.Tests.Unit.csproj
@@ -8,6 +8,7 @@
+
diff --git a/src/Arcus.Messaging.Tests.Unit/MessageHandling/MessageRouterTests.cs b/src/Arcus.Messaging.Tests.Unit/MessageHandling/MessageRouterTests.cs
index 772e3d31..e2158796 100644
--- a/src/Arcus.Messaging.Tests.Unit/MessageHandling/MessageRouterTests.cs
+++ b/src/Arcus.Messaging.Tests.Unit/MessageHandling/MessageRouterTests.cs
@@ -8,15 +8,9 @@
using Arcus.Messaging.Tests.Core.Messages.v1;
using Arcus.Messaging.Tests.Core.Messages.v2;
using Arcus.Messaging.Tests.Unit.Fixture;
-using Arcus.Observability.Telemetry.Core;
-using Arcus.Testing.Logging;
-using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
-using Serilog;
-using Serilog.Events;
using Xunit;
using Order = Arcus.Messaging.Tests.Core.Messages.v1.Order;
diff --git a/src/Arcus.Messaging.sln b/src/Arcus.Messaging.sln
index 16415d78..c79cb62d 100644
--- a/src/Arcus.Messaging.sln
+++ b/src/Arcus.Messaging.sln
@@ -19,12 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.ServiceBus.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Abstractions", "Arcus.Messaging.Abstractions\Arcus.Messaging.Abstractions.csproj", "{B53BF9F5-AC72-4462-87DE-C19CA7C74C9D}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Workers.ServiceBus.Queue", "Arcus.Messaging.Tests.Workers.ServiceBus.Queue\Arcus.Messaging.Tests.Workers.ServiceBus.Queue.csproj", "{799D5D4C-2EED-400B-BF6B-134E8722B628}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Workers", "Arcus.Messaging.Tests.Workers\Arcus.Messaging.Tests.Workers.csproj", "{3C4C0426-284D-4279-8A68-B1B396EFEC57}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Workers.ServiceBus.Topic", "Arcus.Messaging.Tests.Workers.ServiceBus.Topic\Arcus.Messaging.Tests.Workers.ServiceBus.Topic.csproj", "{0BAF4A9C-8F6B-4CF9-B654-95629C2B38DF}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Unit", "Arcus.Messaging.Tests.Unit\Arcus.Messaging.Tests.Unit.csproj", "{9EED9AD7-B69D-45D5-870F-D4F63A1C3495}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Core", "Arcus.Messaging.Tests.Core\Arcus.Messaging.Tests.Core.csproj", "{55DE6D12-4C54-4570-BDFA-00B0FFDE5AB6}"
@@ -33,8 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Abstraction
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.AzureFunctions.ServiceBus", "Arcus.Messaging.AzureFunctions.ServiceBus\Arcus.Messaging.AzureFunctions.ServiceBus.csproj", "{7386C41A-6F27-42B7-BBC1-B6CB41200A68}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Queue", "Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Queue\Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Queue.csproj", "{903E1D94-3157-47BB-9642-8B92755AB8B5}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Pumps.EventHubs", "Arcus.Messaging.Pumps.EventHubs\Arcus.Messaging.Pumps.EventHubs.csproj", "{F5A14425-FEF5-425D-875C-2601C988E6FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Abstractions.EventHubs", "Arcus.Messaging.Abstractions.EventHubs\Arcus.Messaging.Abstractions.EventHubs.csproj", "{040DE2D5-0400-4B4A-A333-60C45CBB0204}"
@@ -45,20 +39,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ServiceBus", "ServiceBus",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventHubs", "EventHubs", "{31250C96-0F22-482F-B3D7-127898A28CF3}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Workers.EventHubs", "Arcus.Messaging.Tests.Workers.EventHubs\Arcus.Messaging.Tests.Workers.EventHubs.csproj", "{D98DF5E5-1701-43B4-AC84-2F28D3AE1176}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Workers.ServiceBus", "Arcus.Messaging.Tests.Workers.ServiceBus\Arcus.Messaging.Tests.Workers.ServiceBus.csproj", "{F1B24FD4-099E-489D-B45D-A1A992CA5C3A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Workers.EventHubs.Core", "Arcus.Messaging.Tests.Workers.EventHubs.Core\Arcus.Messaging.Tests.Workers.EventHubs.Core.csproj", "{0D6C38A7-B684-43BA-9BCF-1EF4C1A943CD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs", "Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs\Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs.csproj", "{6FE22274-0A2D-4AAE-BF64-968987AD7435}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Topic", "Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Topic\Arcus.Messaging.Tests.Runtimes.AzureFunction.ServiceBus.Topic.csproj", "{728C9611-3B33-4751-8D75-5ABB44041531}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Messaging.AzureFunctions.EventHubs", "Arcus.Messaging.AzureFunctions.EventHubs\Arcus.Messaging.AzureFunctions.EventHubs.csproj", "{619AA74F-CDE4-48F6-B6E4-4B7C191CCB1B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs.InProcess", "Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs.InProcess\Arcus.Messaging.Tests.Runtimes.AzureFunction.EventHubs.InProcess.csproj", "{4466DA72-0636-481F-A186-D87735679582}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -93,18 +79,10 @@ Global
{B53BF9F5-AC72-4462-87DE-C19CA7C74C9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B53BF9F5-AC72-4462-87DE-C19CA7C74C9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B53BF9F5-AC72-4462-87DE-C19CA7C74C9D}.Release|Any CPU.Build.0 = Release|Any CPU
- {799D5D4C-2EED-400B-BF6B-134E8722B628}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {799D5D4C-2EED-400B-BF6B-134E8722B628}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {799D5D4C-2EED-400B-BF6B-134E8722B628}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {799D5D4C-2EED-400B-BF6B-134E8722B628}.Release|Any CPU.Build.0 = Release|Any CPU
{3C4C0426-284D-4279-8A68-B1B396EFEC57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C4C0426-284D-4279-8A68-B1B396EFEC57}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C4C0426-284D-4279-8A68-B1B396EFEC57}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C4C0426-284D-4279-8A68-B1B396EFEC57}.Release|Any CPU.Build.0 = Release|Any CPU
- {0BAF4A9C-8F6B-4CF9-B654-95629C2B38DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0BAF4A9C-8F6B-4CF9-B654-95629C2B38DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0BAF4A9C-8F6B-4CF9-B654-95629C2B38DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0BAF4A9C-8F6B-4CF9-B654-95629C2B38DF}.Release|Any CPU.Build.0 = Release|Any CPU
{9EED9AD7-B69D-45D5-870F-D4F63A1C3495}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9EED9AD7-B69D-45D5-870F-D4F63A1C3495}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9EED9AD7-B69D-45D5-870F-D4F63A1C3495}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -121,10 +99,6 @@ Global
{7386C41A-6F27-42B7-BBC1-B6CB41200A68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7386C41A-6F27-42B7-BBC1-B6CB41200A68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7386C41A-6F27-42B7-BBC1-B6CB41200A68}.Release|Any CPU.Build.0 = Release|Any CPU
- {903E1D94-3157-47BB-9642-8B92755AB8B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {903E1D94-3157-47BB-9642-8B92755AB8B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {903E1D94-3157-47BB-9642-8B92755AB8B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {903E1D94-3157-47BB-9642-8B92755AB8B5}.Release|Any CPU.Build.0 = Release|Any CPU
{F5A14425-FEF5-425D-875C-2601C988E6FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5A14425-FEF5-425D-875C-2601C988E6FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5A14425-FEF5-425D-875C-2601C988E6FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -137,10 +111,6 @@ Global
{05EA4B3A-C619-44BB-88C4-F067FCCF83AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05EA4B3A-C619-44BB-88C4-F067FCCF83AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05EA4B3A-C619-44BB-88C4-F067FCCF83AA}.Release|Any CPU.Build.0 = Release|Any CPU
- {D98DF5E5-1701-43B4-AC84-2F28D3AE1176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D98DF5E5-1701-43B4-AC84-2F28D3AE1176}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D98DF5E5-1701-43B4-AC84-2F28D3AE1176}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D98DF5E5-1701-43B4-AC84-2F28D3AE1176}.Release|Any CPU.Build.0 = Release|Any CPU
{F1B24FD4-099E-489D-B45D-A1A992CA5C3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1B24FD4-099E-489D-B45D-A1A992CA5C3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1B24FD4-099E-489D-B45D-A1A992CA5C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -149,22 +119,10 @@ Global
{0D6C38A7-B684-43BA-9BCF-1EF4C1A943CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D6C38A7-B684-43BA-9BCF-1EF4C1A943CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D6C38A7-B684-43BA-9BCF-1EF4C1A943CD}.Release|Any CPU.Build.0 = Release|Any CPU
- {6FE22274-0A2D-4AAE-BF64-968987AD7435}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6FE22274-0A2D-4AAE-BF64-968987AD7435}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6FE22274-0A2D-4AAE-BF64-968987AD7435}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6FE22274-0A2D-4AAE-BF64-968987AD7435}.Release|Any CPU.Build.0 = Release|Any CPU
- {728C9611-3B33-4751-8D75-5ABB44041531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {728C9611-3B33-4751-8D75-5ABB44041531}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {728C9611-3B33-4751-8D75-5ABB44041531}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {728C9611-3B33-4751-8D75-5ABB44041531}.Release|Any CPU.Build.0 = Release|Any CPU
{619AA74F-CDE4-48F6-B6E4-4B7C191CCB1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{619AA74F-CDE4-48F6-B6E4-4B7C191CCB1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{619AA74F-CDE4-48F6-B6E4-4B7C191CCB1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{619AA74F-CDE4-48F6-B6E4-4B7C191CCB1B}.Release|Any CPU.Build.0 = Release|Any CPU
- {4466DA72-0636-481F-A186-D87735679582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4466DA72-0636-481F-A186-D87735679582}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4466DA72-0636-481F-A186-D87735679582}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4466DA72-0636-481F-A186-D87735679582}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -174,24 +132,17 @@ Global
{49D85A35-F341-47A3-887F-68DEC06CEBCA} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{B21CD154-120D-49C5-AC2B-AC3ABDE97641} = {2CD090E7-7306-49A0-9680-6ED78CFECAE1}
{71FCD6E8-B586-4F4B-AA26-0C2F698B837E} = {2CD090E7-7306-49A0-9680-6ED78CFECAE1}
- {799D5D4C-2EED-400B-BF6B-134E8722B628} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{3C4C0426-284D-4279-8A68-B1B396EFEC57} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
- {0BAF4A9C-8F6B-4CF9-B654-95629C2B38DF} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{9EED9AD7-B69D-45D5-870F-D4F63A1C3495} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{55DE6D12-4C54-4570-BDFA-00B0FFDE5AB6} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{864C12DF-DE3D-421F-8687-EC3918FFB8BE} = {2CD090E7-7306-49A0-9680-6ED78CFECAE1}
{7386C41A-6F27-42B7-BBC1-B6CB41200A68} = {2CD090E7-7306-49A0-9680-6ED78CFECAE1}
- {903E1D94-3157-47BB-9642-8B92755AB8B5} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{F5A14425-FEF5-425D-875C-2601C988E6FA} = {31250C96-0F22-482F-B3D7-127898A28CF3}
{040DE2D5-0400-4B4A-A333-60C45CBB0204} = {31250C96-0F22-482F-B3D7-127898A28CF3}
{05EA4B3A-C619-44BB-88C4-F067FCCF83AA} = {31250C96-0F22-482F-B3D7-127898A28CF3}
- {D98DF5E5-1701-43B4-AC84-2F28D3AE1176} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{F1B24FD4-099E-489D-B45D-A1A992CA5C3A} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{0D6C38A7-B684-43BA-9BCF-1EF4C1A943CD} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
- {6FE22274-0A2D-4AAE-BF64-968987AD7435} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
{619AA74F-CDE4-48F6-B6E4-4B7C191CCB1B} = {31250C96-0F22-482F-B3D7-127898A28CF3}
- {4466DA72-0636-481F-A186-D87735679582} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
- {728C9611-3B33-4751-8D75-5ABB44041531} = {A1369CCD-42D1-43F6-98BC-D8EDA62C2B13}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {066FD85A-3DDE-4615-B550-BF67ACCDAA51}