Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 7 additions & 6 deletions ManagedCode.Storage.Azure/BlobStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,19 @@ public override long Length
{
get
{
var realLength = 0L;
var metadata = _pageBlob.GetProperties().Value.Metadata;
var properties = _pageBlob.GetProperties();
var metadata = properties.Value.Metadata;
if (metadata.TryGetValue(MetadataLengthKey, out var length))
{
if (long.TryParse(length, out realLength))
if (long.TryParse(length, out var realLength))
{
return realLength;
}
}

SetLengthInternal(realLength);
return realLength;

var contentLenght = properties.Value.ContentLength;
SetLengthInternal(contentLenght);
return contentLenght;
}
}

Expand Down
28 changes: 28 additions & 0 deletions ManagedCode.Storage.Tests/Common/FileHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,34 @@ public static LocalFile GenerateLocalFile(string fileName, int byteSize)
return localFile;
}

public static LocalFile GenerateLocalFileWithData(LocalFile file, int sizeInBytes)
{
using (var fileStream = file.FileStream)
{
Random random = new Random();
byte[] buffer = new byte[1024]; // Buffer for writing in chunks

while (sizeInBytes > 0)
{
int bytesToWrite = (int) Math.Min(sizeInBytes, buffer.Length);

for (int i = 0; i < bytesToWrite; i++)
{
buffer[i] = (byte) random.Next(65, 91); // 'A' to 'Z'
if (random.Next(2) == 0)
{
buffer[i] = (byte) random.Next(97, 123); // 'a' to 'z'
}
}

fileStream.Write(buffer, 0, bytesToWrite);
sizeInBytes -= bytesToWrite;
}
}

return file;
}

public static IFormFile GenerateFormFile(string fileName, int byteSize)
{
var localFile = GenerateLocalFile(fileName, byteSize);
Expand Down
175 changes: 175 additions & 0 deletions ManagedCode.Storage.Tests/Storages/Azure/AzureBlobStreamTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using ManagedCode.Storage.Azure;
using ManagedCode.Storage.Core.Models;
using ManagedCode.Storage.Tests.Common;
using Microsoft.Extensions.DependencyInjection;
using Testcontainers.Azurite;
using Xunit;

namespace ManagedCode.Storage.Tests.Storages.Azure;

public class AzureBlobStreamTests : StreamTests<AzuriteContainer>
{
protected override AzuriteContainer Build()
{
return new AzuriteBuilder()
.WithImage("mcr.microsoft.com/azure-storage/azurite:3.26.0")
.Build();
}

protected override ServiceProvider ConfigureServices()
{
return AzureConfigurator.ConfigureServices(Container.GetConnectionString());
}

[Fact]
public async Task ReadStreamWithStreamReader_WhenFileExists_ReturnData()
{
// Arrange
var directory = "test-directory";
var fileSizeInBytes = 10;
await using var localFile = LocalFile.FromRandomNameWithExtension(".txt");
FileHelper.GenerateLocalFileWithData(localFile, fileSizeInBytes);
var storage = (IAzureStorage) Storage;

UploadOptions options = new() { FileName = localFile.Name, Directory = directory };
await using var localFileStream = localFile.FileInfo.OpenRead();
var result = await storage.UploadAsync(localFileStream, options);

await using var blobStream = storage.GetBlobStream(result.Value.FullName);

// Act
using var streamReader = new StreamReader(blobStream);
var content = await streamReader.ReadToEndAsync();

// Assert
await using var fileStream = localFile.FileInfo.OpenRead();
using var fileReader = new StreamReader(fileStream);
var fileContent = await fileReader.ReadToEndAsync();
content.Should().NotBeNullOrEmpty();
fileContent.Should().NotBeNullOrEmpty();
content.Should().Be(fileContent);
}

[Fact]
public async Task ReadStream_WhenFileExists_ReturnData()
{
// Arrange
var directory = "test-directory";
var fileSizeInBytes = 10;
await using var localFile = LocalFile.FromRandomNameWithExtension(".txt");
FileHelper.GenerateLocalFileWithData(localFile, fileSizeInBytes);
var storage = (IAzureStorage) Storage;

UploadOptions options = new() { FileName = localFile.Name, Directory = directory };
await using var fileStream = localFile.FileInfo.OpenRead();
var result = await storage.UploadAsync(fileStream, options);

await using var blobStream = storage.GetBlobStream(result.Value.FullName);

var chunkSize = (int) blobStream.Length / 2;
var chunk1 = new byte[chunkSize];
var chunk2 = new byte[chunkSize];

// Act
var bytesReadForChunk1 = await blobStream.ReadAsync(chunk1, 0, chunkSize);
var bytesReadForChunk2 = await blobStream.ReadAsync(chunk2, 0, chunkSize);

// Assert
bytesReadForChunk1.Should().Be(chunkSize);
bytesReadForChunk2.Should().Be(chunkSize);
chunk1.Should().NotBeNullOrEmpty().And.HaveCount(chunkSize);
chunk2.Should().NotBeNullOrEmpty().And.HaveCount(chunkSize);
}

[Fact]
public async Task ReadStream_WhenFileDoesNotExists_ReturnNoData()
{
// Arrange
var directory = "test-directory";
var storage = (IAzureStorage) Storage;
await storage.CreateContainerAsync();
var fullFileName = $"{directory}/{Guid.NewGuid()}.txt";

await using var blobStream = storage.GetBlobStream(fullFileName);
var chunk = new byte[4];

// Act
var bytesRead = await blobStream.ReadAsync(chunk, 0, 4);

// Assert
bytesRead.Should().Be(0);
chunk.Should().NotBeNullOrEmpty();
chunk.Should().AllBeEquivalentTo(0);
}

[Fact]
public async Task WriteStreamWithStreamWriter_SaveData()
{
// Arrange
var directory = "test-directory";
await using var localFile = LocalFile.FromRandomNameWithExtension(".txt");
var fileSizeInBytes = 10;
FileHelper.GenerateLocalFileWithData(localFile, fileSizeInBytes);
var fullFileName = $"{directory}/{localFile.FileInfo.FullName}";

var storage = (IAzureStorage) Storage;

await storage.CreateContainerAsync();

// Act
await using (var blobStream = storage.GetBlobStream(fullFileName))
{
await using (var localFileStream = localFile.FileStream)
{
await localFileStream.CopyToAsync(blobStream);
}
}

// Assert
var fileResult = await storage.DownloadAsync(fullFileName);
fileResult.IsSuccess.Should().BeTrue();
fileResult.Value.Should().NotBeNull();
await using var fileStream = fileResult.Value.FileStream;
using var streamReader = new StreamReader(fileStream);
var fileContent = await streamReader.ReadLineAsync();
fileContent.Should().NotBeNullOrEmpty();
}

[Fact]
public async Task Seek_WhenFileExists_ReturnData()
{
// Arrange
var directory = "test-directory";
var fileSizeInBytes = 10;
await using var localFile = LocalFile.FromRandomNameWithExtension(".txt");
FileHelper.GenerateLocalFileWithData(localFile, fileSizeInBytes);
var storage = (IAzureStorage) Storage;

UploadOptions options = new() { FileName = localFile.Name, Directory = directory };
await using var localFileStream = localFile.FileInfo.OpenRead();
var result = await storage.UploadAsync(localFileStream, options);

await using var blobStream = storage.GetBlobStream(result.Value.FullName);

// Act
var seekInPosition = fileSizeInBytes / 2;
blobStream.Seek(seekInPosition, SeekOrigin.Current);
var buffer = new byte[seekInPosition];
var bytesRead = await blobStream.ReadAsync(buffer);

// Assert
bytesRead.Should().Be(seekInPosition);
await using var fileStream = localFile.FileInfo.OpenRead();
using var fileReader = new StreamReader(fileStream);
var fileContent = await fileReader.ReadToEndAsync();
var content = Encoding.UTF8.GetString(buffer);
content.Should().NotBeNullOrEmpty();
var trimmedFileContent = fileContent.Remove(0, seekInPosition);
content.Should().Be(trimmedFileContent);
}
}
12 changes: 12 additions & 0 deletions ManagedCode.Storage.Tests/Storages/StreamTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Threading.Tasks;
using DotNet.Testcontainers.Containers;
using ManagedCode.Storage.Tests.Common;
using Xunit;

namespace ManagedCode.Storage.Tests.Storages;

public abstract class StreamTests<T> : BaseContainer<T>
where T : IContainer
{

}