Skip to content

Commit 1fd9fab

Browse files
authored
Add integration tests and Service Bus emulator setup (#13)
* Add integration tests and Service Bus emulator setup Introduces a new IntegrationTests project with a basic smoke test for DotQueue, adds configuration for Azure Service Bus emulator and Azure SQL Edge via docker-compose, and updates the solution file to include the new test project. This enables local integration testing against a Service Bus emulator.
1 parent 7192bec commit 1fd9fab

File tree

6 files changed

+314
-1
lines changed

6 files changed

+314
-1
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Azure.Storage.Queues" Version="12.19.1" />
12+
<PackageReference Include="coverlet.collector" Version="6.0.2" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
14+
<PackageReference Include="System.Text.Json" Version="9.0.9" />
15+
<PackageReference Include="Testcontainers" Version="4.7.0" />
16+
<PackageReference Include="xunit" Version="2.9.2" />
17+
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
18+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
19+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<ProjectReference Include="..\..\DotQueue\DotQueue.csproj" />
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<Using Include="Xunit" />
28+
</ItemGroup>
29+
</Project>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.Json;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Azure.Messaging.ServiceBus;
7+
using DotQueue;
8+
using Microsoft.Extensions.DependencyInjection;
9+
using Microsoft.Extensions.Hosting;
10+
using Microsoft.Extensions.Logging;
11+
using Xunit;
12+
13+
public class DotQueue_Smoke
14+
{
15+
private const string Conn =
16+
"Endpoint=sb://localhost;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
17+
18+
[Fact(Timeout = 60000)]
19+
public async Task Message_is_received_and_completed()
20+
{
21+
const string queueName = "demo-messages";
22+
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
23+
24+
var gotIt = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
25+
26+
using var host = Host.CreateDefaultBuilder()
27+
.ConfigureServices(s =>
28+
{
29+
s.AddLogging(b => b.AddSimpleConsole(o => { o.TimestampFormat = "HH:mm:ss "; o.SingleLine = true; }));
30+
31+
s.AddSingleton(_ => new ServiceBusClient(Conn, new ServiceBusClientOptions
32+
{
33+
TransportType = ServiceBusTransportType.AmqpTcp
34+
}));
35+
36+
s.AddSingleton(gotIt);
37+
38+
s.AddQueue<SimpleMsg, SimpleHandler>(queueName, o =>
39+
{
40+
o.MaxConcurrentCalls = 1;
41+
o.PrefetchCount = 0;
42+
o.MaxRetryAttempts = 0;
43+
});
44+
})
45+
.Build();
46+
47+
await host.StartAsync(cts.Token);
48+
await Task.Delay(200, cts.Token);
49+
50+
var client = host.Services.GetRequiredService<ServiceBusClient>();
51+
var sender = client.CreateSender(queueName);
52+
53+
var payload = new SimpleMsg("hello");
54+
var body = JsonSerializer.Serialize(payload);
55+
56+
await sender.SendMessageAsync(
57+
new ServiceBusMessage(body) { ContentType = "application/json" },
58+
cts.Token);
59+
60+
var receivedText = await gotIt.Task.WaitAsync(cts.Token);
61+
Assert.Equal("hello", receivedText);
62+
63+
await sender.DisposeAsync();
64+
await host.StopAsync(TimeSpan.FromSeconds(5));
65+
}
66+
67+
private sealed record SimpleMsg(string Text);
68+
69+
private sealed class SimpleHandler : IQueueHandler<SimpleMsg>
70+
{
71+
private readonly TaskCompletionSource<string> _tcs;
72+
private readonly ILogger<SimpleHandler> _log;
73+
74+
public SimpleHandler(TaskCompletionSource<string> tcs, ILogger<SimpleHandler> log)
75+
{
76+
_tcs = tcs;
77+
_log = log;
78+
}
79+
80+
public async Task HandleAsync(SimpleMsg message, IReadOnlyDictionary<string, string>? _, Func<Task> complete, CancellationToken ct)
81+
{
82+
_log.LogInformation("Got: {Text}", message.Text);
83+
await complete();
84+
_tcs.TrySetResult(message.Text);
85+
}
86+
}
87+
}

DotQueue/DotQueue.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<Nullable>enable</Nullable>
77
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
88
<PackageId>DotQueue</PackageId>
9-
<Version>1.0.5</Version>
9+
<Version>1.0.6</Version>
1010
<Authors>Alexander Kulyabin</Authors>
1111
<Company>Zionet</Company>
1212
<Description>Generic queue listener</Description>

DotQueue/DotQueue.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotQueue", "DotQueue.csproj
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotQueue.Tests", "..\DotQueue.Tests\DotQueue.Tests.csproj", "{CB881C0B-EE4E-EEAE-1DA0-10C2DAF0F68F}"
99
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "..\DotQueue.IntegrationTests\IntegrationTests\IntegrationTests.csproj", "{2A2C8AA0-7589-4DC0-AC35-88AF9D7BAB45}"
11+
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1214
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
2123
{CB881C0B-EE4E-EEAE-1DA0-10C2DAF0F68F}.Debug|Any CPU.Build.0 = Debug|Any CPU
2224
{CB881C0B-EE4E-EEAE-1DA0-10C2DAF0F68F}.Release|Any CPU.ActiveCfg = Release|Any CPU
2325
{CB881C0B-EE4E-EEAE-1DA0-10C2DAF0F68F}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{2A2C8AA0-7589-4DC0-AC35-88AF9D7BAB45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{2A2C8AA0-7589-4DC0-AC35-88AF9D7BAB45}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{2A2C8AA0-7589-4DC0-AC35-88AF9D7BAB45}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{2A2C8AA0-7589-4DC0-AC35-88AF9D7BAB45}.Release|Any CPU.Build.0 = Release|Any CPU
2430
EndGlobalSection
2531
GlobalSection(SolutionProperties) = preSolution
2632
HideSolutionNode = FALSE

docker-compose.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
version: "3.9"
2+
3+
services:
4+
5+
# -------------------- Azure SQL Edge --------------------
6+
sqledge:
7+
container_name: sqledge
8+
image: mcr.microsoft.com/azure-sql-edge:latest
9+
environment:
10+
ACCEPT_EULA: "Y"
11+
MSSQL_SA_PASSWORD: "YourStrongP@ssword123!"
12+
ports:
13+
- "1433:1433" # Optional: expose for local dev
14+
# volumes:
15+
# - sqledge-data:/var/opt/mssql
16+
networks:
17+
- sb-emulator
18+
restart: unless-stopped
19+
healthcheck:
20+
test: ["CMD", "/bin/bash", "-c", "nc -z localhost 1433"]
21+
interval: 10s
22+
retries: 3
23+
24+
# -------------------- Azure Service Bus Emulator --------------------
25+
servicebus-emulator:
26+
container_name: servicebus-emulator
27+
image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
28+
pull_policy: always
29+
volumes:
30+
- ./servicebus-emulator:/ServiceBus_Emulator/ConfigFiles
31+
ports:
32+
- "5672:5672"
33+
- "5671:5671"
34+
- "5300:5300"
35+
environment:
36+
SQL_SERVER: sqledge
37+
MSSQL_SA_PASSWORD: "YourStrongP@ssword123!"
38+
ACCEPT_EULA: "Y"
39+
depends_on:
40+
- sqledge
41+
networks:
42+
sb-emulator:
43+
aliases:
44+
- sbemulatorns
45+
healthcheck:
46+
test: ["CMD", "/bin/bash", "-c", "nc -z localhost 5672"]
47+
interval: 5s
48+
timeout: 5s
49+
retries: 15
50+
51+
52+
# ------------------------ Shared Networks ---------------------------
53+
networks:
54+
microservice-net:
55+
driver: bridge
56+
sb-emulator:
57+
driver: bridge

servicebus-emulator/config.json

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
{
2+
"UserConfig": {
3+
"Namespaces": [
4+
{
5+
"Name": "sbemulatorns",
6+
"Queues": [
7+
{
8+
"Name": "demo-messages",
9+
"Properties": {
10+
"DeadLetteringOnMessageExpiration": false,
11+
"DefaultMessageTimeToLive": "PT1H",
12+
"DuplicateDetectionHistoryTimeWindow": "PT20S",
13+
"ForwardDeadLetteredMessagesTo": "",
14+
"ForwardTo": "",
15+
"LockDuration": "PT1M",
16+
"MaxDeliveryCount": 3,
17+
"RequiresDuplicateDetection": false,
18+
"RequiresSession": false
19+
}
20+
}
21+
],
22+
23+
"Topics": [
24+
{
25+
"Name": "topic.1",
26+
"Properties": {
27+
"DefaultMessageTimeToLive": "PT1H",
28+
"DuplicateDetectionHistoryTimeWindow": "PT20S",
29+
"RequiresDuplicateDetection": false
30+
},
31+
"Subscriptions": [
32+
{
33+
"Name": "subscription.1",
34+
"Properties": {
35+
"DeadLetteringOnMessageExpiration": false,
36+
"DefaultMessageTimeToLive": "PT1H",
37+
"LockDuration": "PT1M",
38+
"MaxDeliveryCount": 3,
39+
"ForwardDeadLetteredMessagesTo": "",
40+
"ForwardTo": "",
41+
"RequiresSession": false
42+
},
43+
"Rules": [
44+
{
45+
"Name": "app-prop-filter-1",
46+
"Properties": {
47+
"FilterType": "Correlation",
48+
"CorrelationFilter": {
49+
"ContentType": "application/text",
50+
"CorrelationId": "id1",
51+
"Label": "subject1",
52+
"MessageId": "msgid1",
53+
"ReplyTo": "someQueue",
54+
"ReplyToSessionId": "sessionId",
55+
"SessionId": "session1",
56+
"To": "xyz"
57+
}
58+
}
59+
}
60+
]
61+
},
62+
{
63+
"Name": "subscription.2",
64+
"Properties": {
65+
"DeadLetteringOnMessageExpiration": false,
66+
"DefaultMessageTimeToLive": "PT1H",
67+
"LockDuration": "PT1M",
68+
"MaxDeliveryCount": 3,
69+
"ForwardDeadLetteredMessagesTo": "",
70+
"ForwardTo": "",
71+
"RequiresSession": false
72+
},
73+
"Rules": [
74+
{
75+
"Name": "user-prop-filter-1",
76+
"Properties": {
77+
"FilterType": "Correlation",
78+
"CorrelationFilter": {
79+
"Properties": {
80+
"prop1": "value1"
81+
}
82+
}
83+
}
84+
}
85+
]
86+
},
87+
{
88+
"Name": "subscription.3",
89+
"Properties": {
90+
"DeadLetteringOnMessageExpiration": false,
91+
"DefaultMessageTimeToLive": "PT1H",
92+
"LockDuration": "PT1M",
93+
"MaxDeliveryCount": 3,
94+
"ForwardDeadLetteredMessagesTo": "",
95+
"ForwardTo": "",
96+
"RequiresSession": false
97+
}
98+
},
99+
{
100+
"Name": "subscription.4",
101+
"Properties": {
102+
"DeadLetteringOnMessageExpiration": false,
103+
"DefaultMessageTimeToLive": "PT1H",
104+
"LockDuration": "PT1M",
105+
"MaxDeliveryCount": 3,
106+
"ForwardDeadLetteredMessagesTo": "",
107+
"ForwardTo": "",
108+
"RequiresSession": false
109+
},
110+
"Rules": [
111+
{
112+
"Name": "sql-filter-1",
113+
"Properties": {
114+
"FilterType": "Sql",
115+
"SqlFilter": {
116+
"SqlExpression": "sys.MessageId = '123456' AND userProp1 = 'value1'"
117+
},
118+
"Action": {
119+
"SqlExpression": "SET sys.To = 'Entity'"
120+
}
121+
}
122+
}
123+
]
124+
}
125+
]
126+
}
127+
]
128+
}
129+
],
130+
"Logging": {
131+
"Type": "File"
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)