-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
219 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using Azure.Storage.Queues; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace AzureStorageTopics | ||
{ | ||
public interface ISubscriptionFactory | ||
{ | ||
ValueTask<QueueClient> CreateAsync(string topicName, string subscriptionName, string connectionString, bool useCache = true, CancellationToken cancellationToken = default); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using Azure.Storage.Queues; | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace AzureStorageTopics | ||
{ | ||
internal sealed class SubscriptionFactory : ISubscriptionFactory | ||
{ | ||
private static readonly ConcurrentDictionary<string, Task<QueueClient>> _cache = new ConcurrentDictionary<string, Task<QueueClient>>(); | ||
|
||
public async ValueTask<QueueClient> CreateAsync( | ||
string topicName, | ||
string subscriptionName, | ||
string connectionString, | ||
bool useCache = true, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
string queueName = BuildQueueName(topicName, subscriptionName); | ||
|
||
if (useCache) | ||
{ | ||
var clientFactory = _cache.GetOrAdd(queueName, _ => CreateClient(connectionString, queueName, cancellationToken)); | ||
var client = await clientFactory.ConfigureAwait(false); | ||
return client; | ||
} | ||
|
||
var queueClient = await CreateClient(connectionString, queueName, cancellationToken).ConfigureAwait(false); | ||
|
||
return queueClient; | ||
} | ||
|
||
private static async Task<QueueClient> CreateClient(string connectionString, string queueName, CancellationToken cancellationToken) | ||
{ | ||
var queueClient = new QueueClient( | ||
connectionString, | ||
queueName); | ||
await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken) | ||
.ConfigureAwait(false); | ||
return queueClient; | ||
} | ||
|
||
private static string BuildQueueName(string topicName, string subscriptionName) | ||
{ | ||
return $"{topicName}-{subscriptionName}".ToLower(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 1 addition & 3 deletions
4
tests/AzureStorageTopics.Tests/StorageTopicAsyncCollectorTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
tests/AzureStorageTopics.Tests/SubscriptionsProviderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using FluentAssertions; | ||
using NSubstitute; | ||
|
||
namespace AzureStorageTopics.Tests | ||
{ | ||
public class SubscriptionsProviderTests | ||
{ | ||
[Fact] | ||
public async Task GetSubscriptionsAsync_should_fail_when_topic_name_null() | ||
{ | ||
var factory = Substitute.For<ISubscriptionFactory>(); | ||
var sut = new SubscriptionsProvider(new TopicsConfig(), factory); | ||
Func<Task> act = async () => await sut.GetSubscriptionsAsync(null, "connectionString"); | ||
await act.Should().ThrowAsync<ArgumentException>() | ||
.WithMessage("'topicName' cannot be null or whitespace. (Parameter 'topicName')"); | ||
} | ||
|
||
[Fact] | ||
public async Task GetSubscriptionsAsync_should_fail_when_connection_string_null() | ||
{ | ||
var factory = Substitute.For<ISubscriptionFactory>(); | ||
var sut = new SubscriptionsProvider(new TopicsConfig(), factory); | ||
Func<Task> act = async () => await sut.GetSubscriptionsAsync("topicName", null); | ||
await act.Should().ThrowAsync<ArgumentException>() | ||
.WithMessage("'connectionString' cannot be null or whitespace. (Parameter 'connectionString')"); | ||
} | ||
|
||
[Fact] | ||
public async Task GetSubscriptionsAsync_should_fail_when_invalid_topic_name() | ||
{ | ||
var factory = Substitute.For<ISubscriptionFactory>(); | ||
var sut = new SubscriptionsProvider(new TopicsConfig(), factory); | ||
Func<Task> act = async () => await sut.GetSubscriptionsAsync("topicName", "connectionString"); | ||
await act.Should().ThrowAsync<KeyNotFoundException>() | ||
.WithMessage("invalid topic name: topicName"); | ||
} | ||
|
||
[Fact] | ||
public async Task GetSubscriptionsAsync_should_fail_when_no_subscriptions() | ||
{ | ||
var config = new TopicsConfig(); | ||
config.Topics["topicName"] = new TopicConfig(); | ||
var factory = Substitute.For<ISubscriptionFactory>(); | ||
var sut = new SubscriptionsProvider(config, factory); | ||
Func<Task> act = async () => await sut.GetSubscriptionsAsync("topicName", "connectionString"); | ||
await act.Should().ThrowAsync<InvalidOperationException>() | ||
.WithMessage("no subscriptions found for topic: topicName"); | ||
} | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
tests/AzureStorageTopics.Tests/TopicsConfigValidatorTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using FluentAssertions; | ||
|
||
namespace AzureStorageTopics.Tests | ||
{ | ||
public class TopicsConfigValidatorTests | ||
{ | ||
[Fact] | ||
public void Validate_should_fail_when_input_null() | ||
{ | ||
var sut = new TopicsConfigValidator(); | ||
var result = sut.Validate(null, null); | ||
Check warning on line 11 in tests/AzureStorageTopics.Tests/TopicsConfigValidatorTests.cs
|
||
result.Failed.Should().BeTrue(); | ||
result.FailureMessage.Should().Be("The configuration is null."); | ||
} | ||
|
||
[Fact] | ||
public void Validate_should_fail_when_no_topics_provided() | ||
{ | ||
var config = new TopicsConfig(); | ||
var sut = new TopicsConfigValidator(); | ||
var result = sut.Validate(null, config); | ||
result.Failed.Should().BeTrue(); | ||
result.FailureMessage.Should().Be("The configuration does not contain any topics."); | ||
} | ||
|
||
[Fact] | ||
public void Validate_should_fail_when_no_subscriptions_provided() | ||
{ | ||
var config = new TopicsConfig(); | ||
config.Topics["topic1"] = new TopicConfig(); | ||
|
||
var sut = new TopicsConfigValidator(); | ||
var result = sut.Validate(null, config); | ||
result.Failed.Should().BeTrue(); | ||
result.FailureMessage.Should().Be("The topic 'topic1' does not contain any subscriptions."); | ||
} | ||
|
||
[Fact] | ||
public void Validate_should_fail_when_subscriptions_duplicated() | ||
{ | ||
var config = new TopicsConfig(); | ||
config.Topics["topic1"] = new TopicConfig() | ||
{ | ||
Subscriptions = new[] | ||
{ | ||
new SubscriptionConfig() { Name = "sub1" }, | ||
new SubscriptionConfig() { Name = "sub1" } | ||
} | ||
}; | ||
|
||
var sut = new TopicsConfigValidator(); | ||
var result = sut.Validate(null, config); | ||
result.Failed.Should().BeTrue(); | ||
result.FailureMessage.Should().Be("The topic 'topic1' contains a duplicate subscription 'sub1'."); | ||
} | ||
|
||
[Fact] | ||
public void Validate_should_succeed_when_input_valid() | ||
{ | ||
var config = new TopicsConfig(); | ||
config.Topics["topic1"] = new TopicConfig() | ||
{ | ||
Subscriptions = new[] | ||
{ | ||
new SubscriptionConfig() { Name = "sub1" }, | ||
new SubscriptionConfig() { Name = "sub2" } | ||
} | ||
}; | ||
|
||
var sut = new TopicsConfigValidator(); | ||
var result = sut.Validate(null, config); | ||
result.Failed.Should().BeFalse(); | ||
} | ||
} | ||
} |