Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sb): add initial servicebus test components #201

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c07d3e9
pr-fix: correct merge w/ 'main'
stijnmoreels Aug 2, 2024
6b26d6d
Merge branch 'main' of https://github.com/stijnmoreels/arcus.testing
stijnmoreels Aug 2, 2024
0de9e56
Merge remote-tracking branch 'upstream/main'
stijnmoreels Aug 26, 2024
cbfd3d9
Merge remote-tracking branch 'upstream/main'
stijnmoreels Sep 6, 2024
cc62c4b
Merge remote-tracking branch 'upstream/main'
stijnmoreels Sep 13, 2024
7ad43ef
Merge remote-tracking branch 'upstream/main'
stijnmoreels Sep 23, 2024
483f449
Merge remote-tracking branch 'upstream/main'
stijnmoreels Sep 27, 2024
b496a00
Merge remote-tracking branch 'upstream/main'
stijnmoreels Oct 10, 2024
4d8b635
feat: add initial service-bus test components
stijnmoreels Oct 11, 2024
d996ec8
pr-add: provide net6.0 support
stijnmoreels Oct 14, 2024
be250d4
pr-fix: add missing xml comments
stijnmoreels Oct 14, 2024
2b8b374
pr-fix: add missing service-bus ns in appsettings
stijnmoreels Oct 14, 2024
46183ae
pr-fix: fallback w/ invalid BinaryData implementation
stijnmoreels Oct 17, 2024
3f70712
pr-fix: add service-bus namespace to test variables
stijnmoreels Oct 17, 2024
914b937
Merge branch 'main' into feature/add-servicebus-components
stijnmoreels Dec 20, 2024
782c766
Apply suggestions from code review
stijnmoreels Dec 20, 2024
57f368c
Apply suggestions from code review
stijnmoreels Dec 20, 2024
2add5e0
pr-fix: revert df unit test changes
stijnmoreels Dec 20, 2024
adc7467
pr-fix: use v1.13.1 az identity
stijnmoreels Dec 20, 2024
7d6075b
pr-add: az service bus namespace to deployment component
stijnmoreels Dec 20, 2024
6056d28
pr-fix: remove zone-redundant availability features
stijnmoreels Dec 20, 2024
abf80ed
pr-fix: disable zone redudant availability zones
stijnmoreels Dec 20, 2024
26ef680
pr-fix: correct cosmos db parameter setting
stijnmoreels Dec 20, 2024
b8e8170
pr-add: message handling in temp queue
stijnmoreels Jan 1, 2025
364901b
pr-add: az service bus topic sub rule test fixture
stijnmoreels Jan 1, 2025
1c0996a
pr-add: service bus message filter
stijnmoreels Jan 10, 2025
aa1cda6
pr-add: feature docs for functionality
stijnmoreels Jan 10, 2025
5c0ef1d
pr-fix: correct ctor update in unit tests
stijnmoreels Jan 10, 2025
04fc75a
temp commit
stijnmoreels Jan 13, 2025
ec0a678
Merge branch 'main' into feature/add-servicebus-components
stijnmoreels Jan 13, 2025
bb1a296
Merge branch 'feature/add-servicebus-components' of https://github.co…
stijnmoreels Jan 13, 2025
ea7e17d
pr-fix: add messaging project to solution
stijnmoreels Jan 13, 2025
bf8c666
pr-fix: complete merge w/ 'main'
stijnmoreels Jan 13, 2025
d217e06
pr-fix: disable nuget warnings as errors
stijnmoreels Jan 14, 2025
4abda9f
pr-fix: add warning when matches both custom message filters
stijnmoreels Jan 14, 2025
5ecc401
pr-add: missing arg check unit tests
stijnmoreels Jan 14, 2025
ad55717
pr-add: missing randomization topic sub creation options
stijnmoreels Jan 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build/deploy-test-resources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ stages:
inputs:
azureSubscription: '${{ parameters.azureServiceConnection }}'
addSpnToEnvironment: true
failOnStandardError: true
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: |
Expand All @@ -48,7 +49,7 @@ stages:
--parameters location=westeurope

$objectId = (az ad sp show --id $env:servicePrincipalId | ConvertFrom-Json).id
$deployOutput = az deployment group create `
az deployment group create `
--resource-group ${{ parameters.resourceGroupName }} `
--template-file ./build/templates/test-resources.bicep `
--parameters location=westeurope `
Expand All @@ -58,6 +59,7 @@ stages:
--parameters cosmosDb_mongoDb_databaseName=${{ variables['Arcus.Testing.Cosmos.MongoDb.DatabaseName'] }} `
--parameters cosmosDb_noSql_name=${{ variables['Arcus.Testing.Cosmos.NoSql.Name'] }} `
--parameters cosmosDb_noSql_databaseName=${{ variables['Arcus.Testing.Cosmos.NoSql.DatabaseName'] }} `
--parameters serviceBusNamespaceName=${{ variables['Arcus.Testing.ServiceBus.Namespace'] }} `
--parameters keyVaultName=${{ parameters.keyVaultName }} `
--parameters servicePrincipal_objectId=$objectId

Expand Down
43 changes: 36 additions & 7 deletions build/templates/smoke-tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Describe 'Storage account' {
-DatabaseName $env:ARCUS_TESTING_COSMOS_MONGODB_DATABASENAME `
-Name $collectionName
}
catch {
finally {
Remove-AzCosmosDBMongoDBCollection `
-ResourceGroupName $env:ARCUS_TESTING_RESOURCEGROUP_NAME `
-AccountName $env:ARCUS_TESTING_COSMOS_MONGODB_NAME `
Expand All @@ -44,14 +44,43 @@ Describe 'Storage account' {
-AccountName $env:ARCUS_TESTING_COSMOS_NOSQL_NAME `
-DatabaseName $env:ARCUS_TESTING_COSMOS_NOSQL_DATABASENAME `
-Name $containerName `
-PartitionKeyPath '/pk'
-PartitionKeyPath '/pk' `
-PartitionKeyKind Hash
}
catch {
finally {
Remove-AzCosmosDBSqlContainer `
-ResourceGroupName $env:ARCUS_TESTING_RESOURCEGROUP_NAME `
-AccountName $env:ARCUS_TESTING_COSMOS_NOSQL_NAME `
-DatabaseName $env:ARCUS_TESTING_COSMOS_NOSQL_DATABASENAME `
-Name $containerName
-ResourceGroupName $env:ARCUS_TESTING_RESOURCEGROUP_NAME `
-AccountName $env:ARCUS_TESTING_COSMOS_NOSQL_NAME `
-DatabaseName $env:ARCUS_TESTING_COSMOS_NOSQL_DATABASENAME `
-Name $containerName
}
}
It "Service principal can create Service bus queue" {
$queueName = 'test-queue'
try {
New-AzServiceBusQueue `
-ResourceGroupName $env:ARCUS_TESTING_RESOURCEGROUP_NAME `
-NamespaceName $env:ARCUS_TESTING_SERVICEBUS_NAMESPACE `
-Name $queueName
} finally {
Remove-AzServiceBusQueue `
-ResourceGroupName $env:ARCUS_TESTING_RESOURCEGROUP_NAME `
-NamespaceName $env:ARCUS_TESTING_SERVICEBUS_NAMESPACE `
-Name $queueName
}
}
It "Service principal can create Service bus topic" {
$topicName = 'test-topic'
try {
New-AzServiceBusTopic `
-ResourceGroupName $env:ARCUS_TESTING_RESOURCEGROUP_NAME `
-NamespaceName $env:ARCUS_TESTING_SERVICEBUS_NAMESPACE `
-Name $topicName
} finally {
Remove-AzServiceBusTopic `
-ResourceGroupName $env:ARCUS_TESTING_RESOURCEGROUP_NAME `
-NamespaceName $env:ARCUS_TESTING_SERVICEBUS_NAMESPACE `
-Name $topicName
}
}
}
23 changes: 23 additions & 0 deletions build/templates/test-resources.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ param cosmosDb_noSql_name string
// Define the name of the CosmosDb NoSql database that will be created.
param cosmosDb_noSql_databaseName string

// Define the name of the Service bus namespace resource that will be created.
param serviceBusNamespaceName string

// Define the name of the key vault where the necessary secrets will be stored to access the deployed test resources.
param keyVaultName string

Expand Down Expand Up @@ -125,6 +128,26 @@ module cosmosDb_noSql 'br/public:avm/res/document-db/database-account:0.6.0' = {
}
}

module serviceBusNamespace 'br/public:avm/res/service-bus/namespace:0.10.1' = {
name: 'serviceBusNamespaceDeployment'
params: {
name: serviceBusNamespaceName
location: location
enableTelemetry: false
publicNetworkAccess: 'Enabled'
skuObject: {
name: 'Standard'
}
zoneRedundant: false
roleAssignments: [
{
principalId: servicePrincipal_objectId
roleDefinitionIdOrName: 'Azure Service Bus Data Owner'
}
]
}
}

module vault 'br/public:avm/res/key-vault/vault:0.6.1' = {
name: 'vaultDeployment'
params: {
Expand Down
7 changes: 3 additions & 4 deletions build/variables/test.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
variables:
Arcus.Testing.ResourceGroup.Name: 'arcus-testing-dev-we-rg'
Arcus.Testing.DataFactory.Name: 'arcus-testing-adf'

Arcus.Testing.DataFactory.Name: 'arcus-testing-adf'
Arcus.Testing.Cosmos.MongoDb.Name: 'arcus-testing-cosmos-mongo'
Arcus.Testing.Cosmos.MongoDb.DatabaseName: 'arcus-testing-cosmos-mongo-db'
Arcus.Testing.Cosmos.NoSql.Name: 'arcus-testing-cosmos-nosql'
Arcus.Testing.Cosmos.NoSql.DatabaseName: 'arcus-testing-cosmos-nosql-db'

Arcus.Testing.Cosmos.NoSql.DatabaseName: 'arcus-testing-cosmos-nosql-db'
Arcus.Testing.KeyVault.Name: 'arcus-testing-kv'
Arcus.Testing.ServiceBus.Namespace: 'arcus-testing-servicebus'
Arcus.Testing.StorageAccount.Name: 'arcustestingstorage'
Arcus.Testing.StorageAccount.Key.SecretName: 'Arcus-Testing-StorageAccount-Key'
234 changes: 234 additions & 0 deletions docs/preview/02-Features/05-Messaging/01-servicebus.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Service bus
The `Arcus.Testing.Messaging.ServiceBus` package provides test fixtures related to Azure Service Bus. By using the common testing practice 'clean environment', it provides a temporary Topic (subscription) and queue.

## Installation
The following functionality is available when installing this package:

```powershell
PM> Install-Package -Name Arcus.Testing.Messaging.ServiceBus
```

<Tabs groupId="messaging-systems">
<TabItem value="topic" label="Topic" default>

## Temporary topic
The `TemporaryTopic` provides a solution when the integration test requires an Azure Service Bus topic during the test run. A topic is created upon the setup of the test fixture and is deleted again when the test fixture is disposed.

> ✨ Only when the test fixture was responsible for creating the topic, will the topic be deleted upon the fixture's disposal. This follows the 'clean environment' testing principle that describes that after the test run, the same state should be achieved as before the test run.

```csharp
using Arcus.Testing;

await using var topic = await TemporaryTopic.CreateIfNotExistsAsync(
"<fully-qualified-namespace>", "<topic-name>", logger);
```

> ⚡ Uses by default the [`DefaultAzureCredential`](https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential) but other type of authentication mechanisms are supported with overloads.

Adding subscriptions to the topic can also be done via the test fixture. It always makes sure that any added subscriptions are deleted again afterwards.

```csharp
using Arcus.Testing;

await using TemporaryTopic topic = ...

await topic.AddSubscriptionAsync("<subscription-name>");
```

### Customization
The `TemporaryTopic` allows testers to configure setup/teardown operations on any messages that were on the topic subscriptions (or the topic itself) at the time of setup/teardown. This follows the 'clean environment' testing principle.

```csharp
using Arcus.Testing;

await TemporaryTopic.CreateIfNotExistsAsync(..., options =>
{
// Options related to when the test fixture is set up.
// ---------------------------------------------------

// Change the default topic-creation behavior.
options.OnSetup.CreateTopicWith((CreateTopicOptions opt) => ...);

// (Default) leave any existing messages on all the topic subscriptions.
options.OnSetup.LeaveExistingMessages();

// Dead-letter any existing messages on all the topic subscriptions.
options.OnSetup.DeadLetterMessages(); // 💡 Max wait time default 5 seconds.
options.OnSetup.DeadLetterMessages(TimeSpan.FromSeconds(10));
options.OnSetup.DeadLetterMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});

// Complete any existing messages on all the topic subscriptions.
options.OnSetup.CompleteMessages(); // 💡 Max wait time default 5 seconds.
options.OnSetup.CompleteMessages(TimeSpan.FromSeconds(10));
options.OnSetup.CompleteMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});

// Options related to when the test fixture is teared down.
// --------------------------------------------------------

// (Default) Dead-letter any lingering messages on all the topic subscriptions.
options.OnTeardown.DeadLetterMessages(); // 💡 Max wait time default 5 seconds.
options.OnTeardown.DeadLetterMessages(TimeSpan.FromSeconds(10));
options.OnTeardown.DeadLetterMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});

// Complete any lingering messages on all the topic subscriptions.
options.OnTeardown.CompleteMessages(); // 💡 Max wait time default 5 seconds.
options.OnTeardown.CompleteMessages(TimeSpan.FromSeconds(10));
options.OnTeardown.CompleteMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});
});
```

### Peek for messages
The `TemporaryTopic` is equipped with a message filtering system that allows testers to search for messages during the lifetime of the test fixture. This can be useful to verify the current state of a topic, or as a test assertion to verify Service bus-related implementations.

```csharp
using Arcus.Testing;

await using TemporaryTopic topic = ...

IEnumerable<ServiceBusReceivedMessage> messages =
await topic.MessagesOn("<subscription-name>")

// Get subset messages currently on the topic subscription.
.Where(msg => msg.ApplicationProperties.ContainsKey("<my-key>"))
.Where(msg => msg.ContentType == "application/json")

// Get messages only from the dead-letter sub-queue.
.FromDeadLetter()

// Get only a number of messages (default: 100).
.Take(10)

// Start peeking for messages.
.ToListAsync();
```

</TabItem>
<TabItem value="queue" label="Queue">

## Temporary queue
The `TemporaryQueue` provides a solution when the integration test requires an Azure Service Bus queue during the test run. A queue is created upon the setup of the test fixture and is deleted again when the test fixture is disposed.

> ✨ Only when the test fixture was responsible for creating the queue, will the queue be deleted upon the fixture's disposal. This follows the 'clean environment' testing principle that describes that after the test run, the same state should be achieved as before the test run.

```csharp
using Arcus.Testing;

await using var queue = await TemporaryQueue.CreateIfNotExistsAsync(
"<fully-qualified-namespace>", "<queue-name>", logger);
```

> ⚡ Uses by default the [`DefaultAzureCredential`](https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential) but other type of authentication mechanisms are supported with overloads.


### Customization
The `TemporaryQueue` allows testers to configure setup/teardown operations on any messages that were on the queue (or the queue itself) at the time of setup/teardown. This follows the 'clean environment' testing principle.

```csharp
using Arcus.Testing;

await TemporaryQueue.CreateIfNotExistsAsync(..., options =>
{
// Options related to when the test fixture is set up.
// ---------------------------------------------------

// Change the default queue-creation behavior.
options.OnSetup.CreateQueueWith((CreateQueueOptions opt) => ...);

// (Default) leave any existing messages on the queue.
options.OnSetup.LeaveExistingMessages();

// Dead-letter any existing messages on the queue.
options.OnSetup.DeadLetterMessages(); // 💡 Max wait time default 5 seconds.
options.OnSetup.DeadLetterMessages(TimeSpan.FromSeconds(10));
options.OnSetup.DeadLetterMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});

// Complete any existing messages on the queue.
options.OnSetup.CompleteMessages(); // 💡 Max wait time default 5 seconds.
options.OnSetup.CompleteMessages(TimeSpan.FromSeconds(10));
options.OnSetup.CompleteMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});

// Options related to when the test fixture is teared down.
// --------------------------------------------------------

// (Default) Dead-letter any lingering messages on the queue.
options.OnTeardown.DeadLetterMessages(); // 💡 Max wait time default 5 seconds.
options.OnTeardown.DeadLetterMessages(TimeSpan.FromSeconds(10));
options.OnTeardown.DeadLetterMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});

// Complete any lingering messages on the queue.
options.OnTeardown.CompleteMessages(); // 💡 Max wait time default 5 seconds.
options.OnTeardown.CompleteMessages(TimeSpan.FromSeconds(10));
options.OnTeardown.CompleteMessages((ServiceBusReceivedMessage msg) =>
{
// ⚡ Multiple calls will be aggregated.
// ⚡ Can be used in combination with other complete/dead-letter operations.
return msg.ApplicationProperties.ContainsKey("<key>");
});
});
```

### Peek for messages
The `TemporaryQueue` is equipped with a message filtering system that allows testers to search for messages during the lifetime of the test fixture. This can be useful to verify the current state of a queue, or as a test assertion to verify Service bus-related implementations.

```csharp
using Arcus.Testing;

await using TemporaryQueue queue = ...

IEnumerable<ServiceBusReceivedMessage> messages =
await queue.Messages

// Get subset messages currently on the queue.
.Where(msg => msg.ApplicationProperties.ContainsKey("<my-key>"))
.Where(msg => msg.ContentType == "application/json")

// Get messages only from the dead-letter sub-queue.
.FromDeadLetter()

// Get only a number of messages (default: 100).
.Take(10)

// Start peeking for messages.
.ToListAsync();
```

</TabItem>
</Tabs>
Loading
Loading