Skip to content

Commit cade56c

Browse files
committed
fix image DI according to url format and fix some tests
1 parent ce61df7 commit cade56c

14 files changed

Lines changed: 78 additions & 66 deletions

File tree

VictoryCenter/VictoryCenter.BLL/Commands/Images/Update/UpdateImageHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public async Task<Result<ImageDTO>> Handle(UpdateImageCommand request, Cancellat
4949

5050
using var transaction = _repositoryWrapper.BeginTransaction();
5151

52+
var previousType = imageEntity.MimeType;
5253
imageEntity.MimeType = request.UpdateImageDto.MimeType!;
5354

5455
var result = _repositoryWrapper.ImageRepository.Update(imageEntity);
@@ -60,7 +61,7 @@ public async Task<Result<ImageDTO>> Handle(UpdateImageCommand request, Cancellat
6061

6162
var updatedBlobName = await _blobService.UpdateFileInStorageAsync(
6263
imageEntity.BlobName,
63-
imageEntity.MimeType,
64+
previousType,
6465
request.UpdateImageDto.Base64!,
6566
imageEntity.BlobName,
6667
request.UpdateImageDto.MimeType!);

VictoryCenter/VictoryCenter.BLL/Exceptions/BlobStorage.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ public BlobNotFoundException(string fileName, string message)
3737
public string FileName { get; }
3838
}
3939

40-
public class BlobCryptographyException : BlobStorageException
40+
public class ImageProcessingException : BlobStorageException
4141
{
42-
public BlobCryptographyException(string fileName, string message)
42+
public ImageProcessingException(string fileName, string message)
4343
: base(message)
4444
{
4545
FileName = fileName;
4646
}
4747

48-
public BlobCryptographyException(string fileName, string message, Exception innerException)
48+
public ImageProcessingException(string fileName, string message, Exception innerException)
4949
: base(message, innerException)
5050
{
5151
FileName = fileName;

VictoryCenter/VictoryCenter.BLL/Services/BlobStorage/BlobEnvironmentVariables.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
public sealed record BlobEnvironmentVariables
44
{
5-
public required string BlobStoreKey { get; init; }
6-
public required string BlobStorePath { get; init; }
5+
public required string RootPath { get; set; }
6+
public required string ImagesSubPath { get; set; }
7+
8+
public string FullPath => Path.Combine(RootPath, ImagesSubPath);
79
}

VictoryCenter/VictoryCenter.BLL/Services/BlobStorage/BlobService.cs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,31 @@ namespace VictoryCenter.BLL.Services.BlobStorage;
99

1010
public class BlobService : IBlobService
1111
{
12-
private readonly string _keyCrypt;
13-
private readonly string _blobPath;
12+
private readonly BlobEnvironmentVariables _blobEnv;
1413
private readonly IHttpContextAccessor _httpContextAccessor;
1514

1615
public BlobService(IOptions<BlobEnvironmentVariables> environment, IHttpContextAccessor httpContextAccessor)
1716
{
18-
_keyCrypt = environment.Value.BlobStoreKey;
19-
_blobPath = environment.Value.BlobStorePath;
17+
_blobEnv = environment.Value;
2018
_httpContextAccessor = httpContextAccessor;
19+
Directory.CreateDirectory(Path.Combine(_blobEnv.RootPath, _blobEnv.ImagesSubPath));
2120
}
2221

23-
public string BlobPath => _blobPath;
24-
2522
public async Task<string> SaveFileInStorageAsync(string base64, string name, string mimeType)
2623
{
2724
try
2825
{
2926
byte[] imageBytes = ConvertBase64ToBytes(base64);
3027
string extension = GetExtensionFromMimeType(mimeType);
3128

32-
Directory.CreateDirectory(_blobPath);
29+
Directory.CreateDirectory(_blobEnv.FullPath);
3330
await CreateFileAsync(imageBytes, extension, name);
3431

3532
return $"{name}.{extension}";
3633
}
3734
catch (Exception ex) when (ex is not BlobStorageException)
3835
{
39-
throw new BlobFileSystemException(BlobPath, ex.Message, ex);
36+
throw new BlobFileSystemException(_blobEnv.FullPath, ex.Message, ex);
4037
}
4138
}
4239

@@ -58,7 +55,7 @@ public string GetFileUrl(string name, string mimeType)
5855
}
5956

6057
var baseUrl = $"{request.Scheme}://{request.Host}";
61-
return $"{baseUrl}/{fileName}";
58+
return $"{baseUrl}/{_blobEnv.ImagesSubPath}/{fileName}";
6259
}
6360

6461
public async Task<string> UpdateFileInStorageAsync(string previousBlobName, string previousMimeType, string base64Format, string newBlobName, string mimeType)
@@ -71,7 +68,7 @@ public async Task<string> UpdateFileInStorageAsync(string previousBlobName, stri
7168
public void DeleteFileInStorage(string name, string mimeType)
7269
{
7370
var fullName = name + "." + GetExtensionFromMimeType(mimeType);
74-
string filePath = Path.Combine(_blobPath, fullName);
71+
string filePath = Path.Combine(_blobEnv.FullPath, fullName);
7572
try
7673
{
7774
if (File.Exists(filePath))
@@ -129,21 +126,21 @@ private string GetExtensionFromMimeType(string mimeType)
129126

130127
private async Task CreateFileAsync(byte[] imageBytes, string type, string name)
131128
{
132-
string filePath = Path.Combine(_blobPath, $"{name}.{type}");
129+
string filePath = Path.Combine(_blobEnv.FullPath, $"{name}.{type}");
133130

134131
try
135132
{
136133
await File.WriteAllBytesAsync(filePath, imageBytes);
137134
}
138-
catch (Exception ex) when (ex is not BlobStorageException)
135+
catch (Exception ex)
139136
{
140-
throw new BlobCryptographyException($"{name}.{type}", ImageConstants.EncryptionFailed, ex);
137+
throw new ImageProcessingException($"{name}.{type}", ImageConstants.EncryptionFailed, ex);
141138
}
142139
}
143140

144141
private async Task<byte[]> GetFileAsync(string fileName, string type)
145142
{
146-
string filePath = Path.Combine(_blobPath, $"{fileName}.{type}");
143+
string filePath = Path.Combine(_blobEnv.FullPath, $"{fileName}.{type}");
147144

148145
if (!File.Exists(filePath))
149146
{
@@ -156,7 +153,7 @@ private async Task<byte[]> GetFileAsync(string fileName, string type)
156153
}
157154
catch (Exception ex) when (ex is not BlobStorageException)
158155
{
159-
throw new BlobCryptographyException(fileName, ImageConstants.DecryptionFailed, ex);
156+
throw new ImageProcessingException(fileName, ImageConstants.DecryptionFailed, ex);
160157
}
161158
}
162159
}

VictoryCenter/VictoryCenter.IntegrationTests/ControllerTests/Images/Create/CreateImageTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public async Task CreateImage_ValidData_ShouldCreateImage()
5555

5656
// Перевіряємо що файл створено у blob storage
5757
string extension = GetExtensionFromMimeType(createImageDto.MimeType);
58-
string filePath = Path.Combine(_blobEnvironment.BlobStorePath, $"{responseContext.BlobName}.{extension}");
58+
string filePath = Path.Combine(_blobEnvironment.FullPath, $"{responseContext.BlobName}.{extension}");
5959
Assert.True(File.Exists(filePath));
6060

6161
// Перевіряємо що URL правильно сформований
@@ -111,7 +111,7 @@ public async Task CreateImage_ValidDataWithDataPrefix_ShouldCreateImage()
111111
{
112112
var createImageDto = new CreateImageDTO
113113
{
114-
Base64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=",
114+
Base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=",
115115
MimeType = "image/png"
116116
};
117117

@@ -130,7 +130,7 @@ public async Task CreateImage_ValidDataWithDataPrefix_ShouldCreateImage()
130130

131131
// Перевіряємо що файл створено
132132
string extension = GetExtensionFromMimeType(createImageDto.MimeType);
133-
string filePath = Path.Combine(_blobEnvironment.BlobStorePath, $"{responseContext.BlobName}.{extension}");
133+
string filePath = Path.Combine(_blobEnvironment.FullPath, $"{responseContext.BlobName}.{extension}");
134134
Assert.True(File.Exists(filePath));
135135
}
136136

@@ -161,7 +161,7 @@ public async Task CreateImage_DifferentMimeTypes_ShouldCreateImageWithCorrectExt
161161

162162
// Перевіряємо що файл створено з правильним розширенням
163163
string expectedExtension = GetExtensionFromMimeType(mimeType);
164-
string filePath = Path.Combine(_blobEnvironment.BlobStorePath, $"{responseContext.BlobName}.{expectedExtension}");
164+
string filePath = Path.Combine(_blobEnvironment.FullPath, $"{responseContext.BlobName}.{expectedExtension}");
165165
Assert.True(File.Exists(filePath));
166166

167167
// Перевіряємо що URL містить правильне розширення

VictoryCenter/VictoryCenter.IntegrationTests/ControllerTests/Images/Delete/DeleteImageTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public async Task DeleteImage_ValidData_ShouldDeleteImage()
3535
var imageId = testImage.Id;
3636

3737
string extension = GetExtensionFromMimeType(testImage.MimeType);
38-
string filePath = Path.Combine(_blobEnvironment.BlobStorePath, $"{testImage.BlobName}.{extension}");
38+
string filePath = Path.Combine(_blobEnvironment.FullPath, $"{testImage.BlobName}.{extension}");
3939

4040
// Переконуємося що файл існує перед видаленням
4141
Assert.True(File.Exists(filePath), "Test file should exist before deletion");
@@ -105,8 +105,8 @@ public async Task DeleteImage_MultipleImages_ShouldDeleteOnlySpecified()
105105
var image1 = await CreateTestImageAsync("test-image-1");
106106
var image2 = await CreateTestImageAsync("test-image-2");
107107

108-
string filePath1 = Path.Combine(_blobEnvironment.BlobStorePath, $"{image1.BlobName}.png");
109-
string filePath2 = Path.Combine(_blobEnvironment.BlobStorePath, $"{image2.BlobName}.png");
108+
string filePath1 = Path.Combine(_blobEnvironment.FullPath, $"{image1.BlobName}.png");
109+
string filePath2 = Path.Combine(_blobEnvironment.FullPath, $"{image2.BlobName}.png");
110110

111111
// Act: Видаляємо тільки перше зображення
112112
HttpResponseMessage response = await _client.DeleteAsync($"api/Image/{image1.Id}");
@@ -162,8 +162,8 @@ private async Task<Image> CreateTestImageAsync(string? customBlobName = null)
162162
await _dbContext.SaveChangesAsync();
163163

164164
// Створюємо тестовий файл
165-
var filePath = Path.Combine(_blobEnvironment.BlobStorePath, $"{blobName}.{extension}");
166-
Directory.CreateDirectory(_blobEnvironment.BlobStorePath);
165+
var filePath = Path.Combine(_blobEnvironment.FullPath, $"{blobName}.{extension}");
166+
Directory.CreateDirectory(_blobEnvironment.FullPath);
167167

168168
// Створюємо мінімальний PNG файл (1x1 pixel)
169169
var testImageBytes = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=");

VictoryCenter/VictoryCenter.IntegrationTests/ControllerTests/Images/GetById/GetImageByIdTest.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Net;
22
using System.Text.Json;
33
using VictoryCenter.BLL.DTOs.Images;
4+
using VictoryCenter.BLL.Services.BlobStorage;
45
using VictoryCenter.DAL.Data;
56
using VictoryCenter.DAL.Entities;
67
using VictoryCenter.IntegrationTests.ControllerTests.Base;
@@ -13,11 +14,13 @@ public class GetImageByIdTest
1314
private readonly HttpClient _client;
1415
private readonly VictoryCenterDbContext _dbContext;
1516
private readonly JsonSerializerOptions _jsonOptions;
17+
private readonly BlobEnvironmentVariables _blobEnv;
1618

1719
public GetImageByIdTest(IntegrationTestDbFixture fixture)
1820
{
1921
_client = fixture.HttpClient;
2022
_dbContext = fixture.DbContext;
23+
_blobEnv = fixture.BlobVariables;
2124
_jsonOptions = new JsonSerializerOptions
2225
{
2326
PropertyNameCaseInsensitive = true
@@ -28,7 +31,7 @@ public GetImageByIdTest(IntegrationTestDbFixture fixture)
2831
public async Task GetImageById_ValidData_ShouldReturnImage()
2932
{
3033
// Arrange: Створюємо тестове зображення
31-
var testImage = await CreateTestImageAsync();
34+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath);
3235
var imageId = testImage.Id;
3336

3437
// Act: Отримуємо зображення за ID
@@ -111,7 +114,7 @@ public async Task GetImageById_DifferentMimeTypes_ShouldReturnCorrectData()
111114

112115
foreach (var (mimeType, blobName) in testCases)
113116
{
114-
var testImage = await CreateTestImageAsync(blobName, mimeType);
117+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath, blobName, mimeType);
115118

116119
// Act: Отримуємо зображення
117120
HttpResponseMessage response = await _client.GetAsync($"api/Image/{testImage.Id}");
@@ -130,7 +133,7 @@ public async Task GetImageById_DifferentMimeTypes_ShouldReturnCorrectData()
130133
public async Task GetImageById_ResponseFormat_ShouldContainAllRequiredFields()
131134
{
132135
// Arrange: Створюємо тестове зображення
133-
var testImage = await CreateTestImageAsync();
136+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath);
134137

135138
// Act: Отримуємо зображення
136139
HttpResponseMessage response = await _client.GetAsync($"api/Image/{testImage.Id}");
@@ -156,12 +159,12 @@ public async Task GetImageById_ResponseFormat_ShouldContainAllRequiredFields()
156159
Assert.Contains("http", result.Url);
157160
}
158161

159-
private async Task<Image> CreateTestImageAsync(string? customBlobName = null, string? customMimeType = null)
162+
private async Task<Image> CreateTestImageAsync(string subPath, string? customBlobName = null, string? customMimeType = null)
160163
{
161164
var blobName = customBlobName ?? Guid.NewGuid().ToString().Replace("-", "");
162165
var mimeType = customMimeType ?? "image/png";
163166
var extension = GetExtensionFromMimeType(mimeType);
164-
var url = $"http://localhost/{blobName}.{extension}";
167+
var url = $"http://localhost/{subPath}/{blobName}.{extension}";
165168

166169
var image = new Image
167170
{

VictoryCenter/VictoryCenter.IntegrationTests/ControllerTests/Images/GetByName/GetImageByNameTest.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Net;
22
using System.Text.Json;
33
using VictoryCenter.BLL.DTOs.Images;
4+
using VictoryCenter.BLL.Services.BlobStorage;
45
using VictoryCenter.DAL.Data;
56
using VictoryCenter.DAL.Entities;
67
using VictoryCenter.IntegrationTests.ControllerTests.Base;
@@ -13,11 +14,13 @@ public class GetImageByNameTest
1314
private readonly HttpClient _client;
1415
private readonly VictoryCenterDbContext _dbContext;
1516
private readonly JsonSerializerOptions _jsonOptions;
16-
17+
private readonly string _imageSubPath;
18+
private readonly BlobEnvironmentVariables _blobEnv;
1719
public GetImageByNameTest(IntegrationTestDbFixture fixture)
1820
{
1921
_client = fixture.HttpClient;
2022
_dbContext = fixture.DbContext;
23+
_blobEnv = fixture.BlobVariables;
2124
_jsonOptions = new JsonSerializerOptions
2225
{
2326
PropertyNameCaseInsensitive = true
@@ -29,7 +32,7 @@ public async Task GetImageByName_ValidData_ShouldReturnImage()
2932
{
3033
// Arrange: Створюємо тестове зображення з унікальним ім'ям
3134
var testBlobName = "test-image-" + Guid.NewGuid().ToString("N")[..8];
32-
var testImage = await CreateTestImageAsync(testBlobName);
35+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath, testBlobName );
3336

3437
// Act: Отримуємо зображення за ім'ям
3538
HttpResponseMessage response = await _client.GetAsync($"api/Image/by-name/{testBlobName}");
@@ -105,15 +108,15 @@ public async Task GetImageByName_CaseSensitivityTest_ShouldFindExactMatch()
105108
{
106109
// Arrange: Створюємо зображення з конкретним регістром
107110
var originalBlobName = "TestImageName";
108-
var testImage = await CreateTestImageAsync(originalBlobName);
111+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath, originalBlobName);
109112

110113
// Act & Assert: Тестуємо точне співпадіння
111114
HttpResponseMessage exactResponse = await _client.GetAsync($"api/Image/by-name/{originalBlobName}");
112115
Assert.True(exactResponse.IsSuccessStatusCode);
113116

114117
// Act & Assert: Тестуємо різний регістр (має не знайти, якщо пошук case-sensitive)
115118
var lowerCaseName = originalBlobName.ToLower();
116-
if (lowerCaseName != originalBlobName) // Тільки якщо регістр дійсно відрізняється
119+
if (lowerCaseName != originalBlobName)
117120
{
118121
HttpResponseMessage caseResponse = await _client.GetAsync($"api/Image/by-name/{lowerCaseName}");
119122

@@ -127,7 +130,7 @@ public async Task GetImageByName_SpecialCharactersInName_ShouldHandleCorrectly()
127130
{
128131
// Arrange: Створюємо зображення з спеціальними символами в імені
129132
var specialCharName = "test-image_123.special";
130-
var testImage = await CreateTestImageAsync(specialCharName);
133+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath, specialCharName);
131134

132135
// Act: Отримуємо зображення з URL-encoded ім'ям
133136
var encodedName = Uri.EscapeDataString(specialCharName);
@@ -146,9 +149,9 @@ public async Task GetImageByName_MultipleImagesWithSimilarNames_ShouldReturnCorr
146149
{
147150
// Arrange: Створюємо кілька зображень з схожими іменами
148151
var baseName = "similar-image";
149-
var image1 = await CreateTestImageAsync($"{baseName}-1");
150-
var image2 = await CreateTestImageAsync($"{baseName}-2");
151-
var image3 = await CreateTestImageAsync($"{baseName}-test");
152+
var image1 = await CreateTestImageAsync(_blobEnv.ImagesSubPath, $"{baseName}-1");
153+
var image2 = await CreateTestImageAsync(_blobEnv.ImagesSubPath, $"{baseName}-2");
154+
var image3 = await CreateTestImageAsync(_blobEnv.ImagesSubPath, $"{baseName}-test");
152155

153156
// Act: Отримуємо конкретне зображення
154157
var targetName = $"{baseName}-2";
@@ -176,7 +179,7 @@ public async Task GetImageByName_DifferentMimeTypes_ShouldReturnCorrectData()
176179

177180
foreach (var (mimeType, blobName) in testCases)
178181
{
179-
var testImage = await CreateTestImageAsync(blobName, mimeType);
182+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath, blobName, mimeType);
180183

181184
// Act: Отримуємо зображення за ім'ям
182185
HttpResponseMessage response = await _client.GetAsync($"api/Image/by-name/{blobName}");
@@ -197,7 +200,7 @@ public async Task GetImageByName_ResponseFormat_ShouldContainAllRequiredFields()
197200
{
198201
// Arrange: Створюємо тестове зображення
199202
var testBlobName = "format-test-image";
200-
var testImage = await CreateTestImageAsync(testBlobName);
203+
var testImage = await CreateTestImageAsync(_blobEnv.ImagesSubPath, testBlobName);
201204

202205
// Act: Отримуємо зображення
203206
HttpResponseMessage response = await _client.GetAsync($"api/Image/by-name/{testBlobName}");
@@ -224,11 +227,12 @@ public async Task GetImageByName_ResponseFormat_ShouldContainAllRequiredFields()
224227
Assert.Equal(testBlobName, result.BlobName);
225228
}
226229

227-
private async Task<Image> CreateTestImageAsync(string blobName, string? customMimeType = null)
230+
private async Task<Image> CreateTestImageAsync(string? subPath, string blobName, string? customMimeType = null )
228231
{
229232
var mimeType = customMimeType ?? "image/png";
230233
var extension = GetExtensionFromMimeType(mimeType);
231-
var url = $"http://localhost/{blobName}.{extension}";
234+
var subString = subPath;
235+
var url = $"http://localhost/{subString}/{blobName}.{extension}";
232236

233237
var image = new Image
234238
{

0 commit comments

Comments
 (0)