Skip to content

Commit

Permalink
feat: expose OnTeardown for blob infra (#186)
Browse files Browse the repository at this point in the history
* pr-fix: correct merge w/ 'main'

* feat: expose OnTeardown for blob test infra
  • Loading branch information
stijnmoreels authored Sep 25, 2024
1 parent 4225c0b commit e577f63
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 3 deletions.
12 changes: 11 additions & 1 deletion docs/preview/02-Features/04-Storage/01-blob.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@ await TemporaryBlobContainer.CreateIfNotExistsAsync(..., options =>

// Delete Azure Blob container regardless if the test fixture created the container or not.
options.OnTeardown.DeleteExistingContainer();
})
});

// `OnTeardown` is also still available after the temporary container is created:
await using TemporaryBlobContainer container = ...

container.OnTeardown.CleanAllBlobs();
```

> 🎖️ The `TemporaryBlobContainer` will always remove any Azure Blobs that were uploaded on the temporary container itself with the `container.UploadBlobAsync`. This follows the 'clean environment' testing principal that any test should not leave any state it created behind after the test has run.
Expand Down Expand Up @@ -140,4 +145,9 @@ await TemporaryBlobFile.UploadIfNotExistsAsync(..., options =>
// regardless if the test fixture created the blob.
options.OnTeardown.DeleteExistingBlob();
});

// `OnTeardown` is also still available after the temporary file is created.
await using TemporaryBlobFile file = ...

file.OnTeardown.DeleteExistingBlob();
```
5 changes: 5 additions & 0 deletions src/Arcus.Testing.Storage.Blob/TemporaryBlobContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@ private TemporaryBlobContainer(
/// </summary>
public BlobContainerClient Client { get; }

/// <summary>
/// Gets the additional options to manipulate the deletion of the <see cref="TemporaryBlobContainer"/>.
/// </summary>
public OnTeardownBlobContainerOptions OnTeardown => _options.OnTeardown;

/// <summary>
/// Creates a new instance of the <see cref="TemporaryBlobContainer"/> which creates a new Azure Blob storage container if it doesn't exist yet.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Arcus.Testing.Storage.Blob/TemporaryBlobFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ private TemporaryBlobFile(
/// </summary>
public BlobClient Client { get; }

/// <summary>
/// Gets the additional options to manipulate the deletion of the <see cref="TemporaryBlobFile"/>.
/// </summary>
public OnTeardownBlobFileOptions OnTeardown => _options.OnTeardown;

/// <summary>
/// Uploads a temporary blob to the Azure Blob container.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ public async Task CreateTempBlobContainerWithCleanMatchingUponCreationAndDeletio
TemporaryBlobContainer container = await WhenTempContainerCreatedAsync(context, containerClient, options =>
{
options.OnSetup.CleanMatchingBlobs(Matching(matchingCreationBlob.Name));
options.OnTeardown.CleanMatchingBlobs(Matching(matchingDeletionBlobName));
});
container.OnTeardown.CleanMatchingBlobs(Matching(matchingDeletionBlobName));

await context.ShouldDeleteBlobFileAsync(containerClient, matchingCreationBlob.Name);
BlobClient matchingDeletionBlob = await context.WhenBlobAvailableAsync(containerClient, matchingDeletionBlobName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Arcus.Testing.Tests.Integration.Storage.Fixture;
using Azure.Storage.Blobs;
using Microsoft.Extensions.Options;
using Xunit;
using Xunit.Abstractions;

Expand Down Expand Up @@ -143,8 +144,8 @@ public async Task UploadTempBlobFileWithAllAvailableOutOfScopeOptions_OnExisting
TemporaryBlobFile sut = await WhenBlobUploadedAsync(containerClient, existingBlob.Name, newContent, configureOptions: options =>
{
options.OnSetup.UseExistingBlob();
options.OnTeardown.DeleteExistingBlob();
});
sut.OnTeardown.DeleteExistingBlob();
await context.ShouldStoreBlobFileAsync(containerClient, sut.Name, originalContent);

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<ProjectReference Include="..\Arcus.Testing.Messaging.Pumps.EventHubs\Arcus.Testing.Messaging.Pumps.EventHubs.csproj" />
<ProjectReference Include="..\Arcus.Testing.Messaging.Pumps.ServiceBus\Arcus.Testing.Messaging.Pumps.ServiceBus.csproj" />
<ProjectReference Include="..\Arcus.Testing.Security.Providers.InMemory\Arcus.Testing.Security.Providers.InMemory.csproj" />
<ProjectReference Include="..\Arcus.Testing.Storage.Blob\Arcus.Testing.Storage.Blob.csproj" />
<ProjectReference Include="..\Arcus.Testing.Tests.Core\Arcus.Testing.Tests.Core.csproj" />
</ItemGroup>

Expand Down
43 changes: 43 additions & 0 deletions src/Arcus.Testing.Tests.Unit/Storage/BlobNameFilterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using Bogus;
using Xunit;

namespace Arcus.Testing.Tests.Unit.Storage
{
public class BlobNameFilterTests
{
private static readonly Faker Bogus = new();

[Theory]
[ClassData(typeof(Blanks))]
public void NameEqual_WithoutName_Fails(string name)
{
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameEqual(name));
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameEqual(name, Bogus.PickRandom<StringComparison>()));
}

[Theory]
[ClassData(typeof(Blanks))]
public void NameContains_WithoutValue_Fails(string value)
{
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameContains(value));
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameContains(value, Bogus.PickRandom<StringComparison>()));
}

[Theory]
[ClassData(typeof(Blanks))]
public void NameStartsWith_WithoutPrefix_Fails(string prefix)
{
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameStartsWith(prefix));
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameStartsWith(prefix, Bogus.PickRandom<StringComparison>()));
}

[Theory]
[ClassData(typeof(Blanks))]
public void NameEndsWith_WithoutSuffix_Fails(string suffix)
{
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameEndsWith(suffix));
Assert.ThrowsAny<ArgumentException>(() => BlobNameFilter.NameEndsWith(suffix, Bogus.PickRandom<StringComparison>()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using Xunit;

namespace Arcus.Testing.Tests.Unit.Storage
{
public class TemporaryBlobContainerOptionsTests
{
[Fact]
public void CleanMatchingBlobsOnSetup_WithNullFilters_Fails()
{
// Arrange
var options = new TemporaryBlobContainerOptions();

// Act / Assert
Assert.ThrowsAny<ArgumentException>(() => options.OnSetup.CleanMatchingBlobs(filters: null));
Assert.ThrowsAny<ArgumentException>(() => options.OnSetup.CleanMatchingBlobs(BlobNameFilter.NameEqual("some-name"), null));
}

[Fact]
public void CleanMatchingBlobsOnTeardown_WithNullFilters_Fails()
{
// Arrange
var options = new TemporaryBlobContainerOptions();

// Act / Assert
Assert.ThrowsAny<ArgumentException>(() => options.OnTeardown.CleanMatchingBlobs(filters: null));
Assert.ThrowsAny<ArgumentException>(() => options.OnTeardown.CleanMatchingBlobs(BlobNameFilter.NameEqual("some-name"), null));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;

namespace Arcus.Testing.Tests.Unit.Storage
{
public class TemporaryBlobContainerTests
{
[Theory]
[ClassData(typeof(Blanks))]
public async Task CreateBlobContainerIfNotExists_WithoutAccountName_Fails(string accountName)
{
await Assert.ThrowsAnyAsync<ArgumentException>(() => TemporaryBlobContainer.CreateIfNotExistsAsync(accountName, "<container-name>", NullLogger.Instance));
await Assert.ThrowsAnyAsync<ArgumentException>(() => TemporaryBlobContainer.CreateIfNotExistsAsync(accountName, "<container-name>", NullLogger.Instance, configureOptions: opt => { }));
}

[Theory]
[ClassData(typeof(Blanks))]
public async Task CreateBlobContainerIfNotExists_WithoutContainerName_Fails(string containerName)
{
await Assert.ThrowsAnyAsync<ArgumentException>(() => TemporaryBlobContainer.CreateIfNotExistsAsync("<account-name>", containerName, NullLogger.Instance));
await Assert.ThrowsAnyAsync<ArgumentException>(() => TemporaryBlobContainer.CreateIfNotExistsAsync("<account-name>", containerName, NullLogger.Instance, configureOptions: opt => { }));
}

[Fact]
public async Task CreateBlobContainerIfNotExists_WithoutContainerClient_Fails()
{
await Assert.ThrowsAnyAsync<ArgumentException>(() => TemporaryBlobContainer.CreateIfNotExistsAsync(containerClient: null, NullLogger.Instance));
await Assert.ThrowsAnyAsync<ArgumentException>(() => TemporaryBlobContainer.CreateIfNotExistsAsync(containerClient: null, NullLogger.Instance, configureOptions: opt => { }));
}
}
}
105 changes: 105 additions & 0 deletions src/Arcus.Testing.Tests.Unit/Storage/TemporaryBlobFileTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;

namespace Arcus.Testing.Tests.Unit.Storage
{
public class TemporaryBlobFileTests
{
[Fact]
public async Task UploadBlobFileIfNotExists_WithoutContainerUri_Fails()
{
await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
blobContainerUri: null,
"<blob-name>",
BinaryData.FromString("<blob-content>"),
NullLogger.Instance));

await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
blobContainerUri: null,
"<blob-name>",
BinaryData.FromString("<blob-content>"),
NullLogger.Instance,
configureOptions: opt => { }));
}

[Theory]
[ClassData(typeof(Blanks))]
public async Task UploadBlobFileIfNotExistsViaContainerUri_WithoutBlobName_Fails(string blobName)
{
await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
new Uri("https://some-url"),
blobName,
BinaryData.FromString("<blob-content>"),
NullLogger.Instance));

await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
new Uri("https://some-url"),
blobName,
BinaryData.FromString("<blob-content>"),
NullLogger.Instance,
configureOptions: opt => { }));
}

[Fact]
public async Task UploadBlobFileIfNotExistsViaContainerUri_WithoutBlobContent_Fails()
{
await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
new Uri("https://some-url"),
"<blob-name>",
blobContent: null,
NullLogger.Instance));

await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
new Uri("https://some-url"),
"<blob-name>",
blobContent: null,
NullLogger.Instance,
configureOptions: opt => { }));
}

[Fact]
public async Task UploadBlobFileIfNotExists_WithoutBlobClient_Fails()
{
await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
blobClient: null,
BinaryData.FromString("<blob-content>"),
NullLogger.Instance));

await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
blobClient: null,
BinaryData.FromString("<blob-content>"),
NullLogger.Instance,
configureOptions: opt => { }));
}

[Fact]
public async Task UploadBlobFileIfNotExistsViaBlobClient_WithoutBlobContent_Fails()
{
var client = Mock.Of<BlobClient>();
await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
client,
blobContent: null,
NullLogger.Instance));

await Assert.ThrowsAnyAsync<ArgumentException>(
() => TemporaryBlobFile.UploadIfNotExistsAsync(
client,
blobContent: null,
NullLogger.Instance,
opt => { }));
}
}
}

0 comments on commit e577f63

Please sign in to comment.