using System.Net; using System.Net.Http; using Microsoft.Extensions.Logging; using Moq; using mws.backend.dotnet.infrastructure.CloudFile; namespace tests; public class CloudFileDocumentContentStoreTests { private readonly Mock _cfMock = new(); private readonly Mock> _loggerMock = new(); private readonly CloudFileDocumentContentStore _sut; public CloudFileDocumentContentStoreTests() { _sut = new CloudFileDocumentContentStore(_cfMock.Object, _loggerMock.Object); } [Fact] public async Task EnsureBucketAsync_CallsCorrectBucket() { var projectId = Guid.NewGuid(); await _sut.EnsureBucketAsync(projectId, CancellationToken.None); _cfMock.Verify(c => c.EnsureBucketAsync($"mws-p-{projectId:N}", It.IsAny()), Times.Once); } [Fact] public async Task UploadAsync_BuildsCorrectBucketAndKey() { var projectId = Guid.NewGuid(); var documentId = Guid.NewGuid(); using var stream = new MemoryStream(new byte[] { 1, 2, 3 }); await _sut.UploadAsync(projectId, documentId, "text/html; charset=utf-8", stream, CancellationToken.None); _cfMock.Verify(c => c.UploadAsync( $"mws-p-{projectId:N}", $"{documentId:N}.html", "text/html; charset=utf-8", It.IsAny(), It.IsAny()), Times.Once); } [Fact] public async Task UploadAsync_ThrowsStorageUnavailableOnHttpRequestException() { var projectId = Guid.NewGuid(); var documentId = Guid.NewGuid(); using var stream = new MemoryStream(new byte[] { 1, 2, 3 }); _cfMock.Setup(c => c.UploadAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new HttpRequestException("Connection refused")); var ex = await Assert.ThrowsAsync( () => _sut.UploadAsync(projectId, documentId, "text/html", stream, CancellationToken.None)); Assert.Contains("Cloud file upload failed", ex.Message); } [Fact] public async Task DownloadAsync_ReturnsStreamNullOnNotFound() { var projectId = Guid.NewGuid(); var documentId = Guid.NewGuid(); _cfMock.Setup(c => c.DownloadAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(Stream.Null); var result = await _sut.DownloadAsync(projectId, documentId, CancellationToken.None); Assert.Equal(Stream.Null, result); } [Fact] public async Task DownloadAsync_ThrowsStorageUnavailableOnHttpRequestException() { var projectId = Guid.NewGuid(); var documentId = Guid.NewGuid(); _cfMock.Setup(c => c.DownloadAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new HttpRequestException("Connection refused")); await Assert.ThrowsAsync( () => _sut.DownloadAsync(projectId, documentId, CancellationToken.None)); } [Fact] public async Task DeleteAsync_Swallows404Errors() { var projectId = Guid.NewGuid(); var documentId = Guid.NewGuid(); // Should not throw await _sut.DeleteAsync(projectId, documentId, CancellationToken.None); _cfMock.Verify(c => c.DeleteAsync( $"mws-p-{projectId:N}", $"{documentId:N}.html", It.IsAny()), Times.Once); } [Fact] public async Task ExistsAsync_ReturnsTrueWhenFileExists() { var projectId = Guid.NewGuid(); var documentId = Guid.NewGuid(); _cfMock.Setup(c => c.ExistsAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(true); var result = await _sut.ExistsAsync(projectId, documentId, CancellationToken.None); Assert.True(result); } [Fact] public async Task ExistsAsync_ReturnsFalseOnException() { var projectId = Guid.NewGuid(); var documentId = Guid.NewGuid(); _cfMock.Setup(c => c.ExistsAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new HttpRequestException("Connection refused")); var result = await _sut.ExistsAsync(projectId, documentId, CancellationToken.None); Assert.False(result); } }