236 lines
6.4 KiB
C#
236 lines
6.4 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using FluentAssertions;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using StorageService.Domain.AggregatesModel.FileAggregate;
|
|
using StorageService.Infrastructure.Persistence;
|
|
|
|
namespace StorageService.FunctionalTests.ApiTests;
|
|
|
|
/// <summary>
|
|
/// EN: Functional tests for Files API endpoints.
|
|
/// VI: Functional tests cho Files API endpoints.
|
|
/// </summary>
|
|
public class FilesApiTests : IClassFixture<CustomWebApplicationFactory>
|
|
{
|
|
private readonly HttpClient _client;
|
|
private readonly CustomWebApplicationFactory _factory;
|
|
|
|
public FilesApiTests(CustomWebApplicationFactory factory)
|
|
{
|
|
_factory = factory;
|
|
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
|
|
{
|
|
AllowAutoRedirect = false
|
|
});
|
|
|
|
// EN: Add mock authentication header / VI: Thêm mock authentication header
|
|
_client.DefaultRequestHeaders.Add("X-User-Id", "test-user-123");
|
|
}
|
|
|
|
#region GetFile Tests
|
|
|
|
[Fact]
|
|
public async Task GetFile_ExistingFile_Returns200WithFileMetadata()
|
|
{
|
|
// Arrange
|
|
var fileId = await SeedTestFile("test-file.pdf");
|
|
|
|
// Act
|
|
var response = await _client.GetAsync($"/api/v1/storage/files/{fileId}");
|
|
|
|
// Assert
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
|
|
var content = await response.Content.ReadFromJsonAsync<FileMetadataResponse>();
|
|
content.Should().NotBeNull();
|
|
content!.FileName.Should().Be("test-file.pdf");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetFile_NotFound_Returns404()
|
|
{
|
|
// Arrange
|
|
var nonExistentId = Guid.NewGuid();
|
|
|
|
// Act
|
|
var response = await _client.GetAsync($"/api/v1/storage/files/{nonExistentId}");
|
|
|
|
// Assert
|
|
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetFile_InvalidId_Returns400()
|
|
{
|
|
// Act
|
|
var response = await _client.GetAsync("/api/v1/storage/files/invalid-guid");
|
|
|
|
// Assert
|
|
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetUserFiles Tests
|
|
|
|
[Fact]
|
|
public async Task GetUserFiles_WithFiles_ReturnsFileList()
|
|
{
|
|
// Arrange
|
|
await SeedTestFile("file1.pdf");
|
|
await SeedTestFile("file2.pdf");
|
|
|
|
// Act
|
|
var response = await _client.GetAsync("/api/v1/storage/files");
|
|
|
|
// Assert
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
|
|
var content = await response.Content.ReadFromJsonAsync<UserFilesResponse>();
|
|
content.Should().NotBeNull();
|
|
content!.Files.Should().NotBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetUserFiles_WithPagination_RespectsPageSize()
|
|
{
|
|
// Arrange
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
await SeedTestFile($"file{i}.pdf");
|
|
}
|
|
|
|
// Act
|
|
var response = await _client.GetAsync("/api/v1/storage/files?page=1&pageSize=2");
|
|
|
|
// Assert
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DeleteFile Tests
|
|
|
|
[Fact]
|
|
public async Task DeleteFile_ExistingFile_Returns204()
|
|
{
|
|
// Arrange
|
|
var fileId = await SeedTestFile("to-delete.pdf");
|
|
|
|
// Act
|
|
var response = await _client.DeleteAsync($"/api/v1/storage/files/{fileId}");
|
|
|
|
// Assert
|
|
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteFile_NotFound_ReturnsError()
|
|
{
|
|
// Arrange
|
|
var nonExistentId = Guid.NewGuid();
|
|
|
|
// Act
|
|
var response = await _client.DeleteAsync($"/api/v1/storage/files/{nonExistentId}");
|
|
|
|
// Assert
|
|
// EN: Could be 404 or 400 depending on implementation
|
|
// VI: Có thể là 404 hoặc 400 tùy vào implementation
|
|
response.IsSuccessStatusCode.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteFile_AlreadyDeleted_ReturnsError()
|
|
{
|
|
// Arrange
|
|
var fileId = await SeedTestFile("already-deleted.pdf", isDeleted: true);
|
|
|
|
// Act
|
|
var response = await _client.DeleteAsync($"/api/v1/storage/files/{fileId}");
|
|
|
|
// Assert
|
|
response.IsSuccessStatusCode.Should().BeFalse();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetDownloadUrl Tests
|
|
|
|
[Fact]
|
|
public async Task GetDownloadUrl_ExistingFile_ReturnsUrl()
|
|
{
|
|
// Arrange
|
|
var fileId = await SeedTestFile("downloadable.pdf");
|
|
|
|
// Act
|
|
var response = await _client.GetAsync($"/api/v1/storage/files/{fileId}/download-url");
|
|
|
|
// Assert
|
|
// EN: May succeed or fail depending on storage provider mock
|
|
// VI: Có thể thành công hoặc thất bại tùy thuộc vào mock storage provider
|
|
// In real functional tests, we'd have proper MinIO mocking
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper Methods
|
|
|
|
private async Task<Guid> SeedTestFile(string fileName, bool isDeleted = false)
|
|
{
|
|
using var scope = _factory.Services.CreateScope();
|
|
var context = scope.ServiceProvider.GetRequiredService<StorageServiceContext>();
|
|
|
|
var file = new StorageFile(
|
|
fileName: fileName,
|
|
bucketName: "test-bucket",
|
|
objectKey: $"private/test-user-123/{DateTime.UtcNow:yyyyMMdd}/{Guid.NewGuid():N}_{fileName}",
|
|
contentType: "application/pdf",
|
|
fileSizeBytes: 1024,
|
|
userId: "test-user-123",
|
|
provider: StorageProvider.MinIO,
|
|
accessLevel: FileAccessLevel.Private);
|
|
|
|
if (isDeleted)
|
|
{
|
|
file.Delete();
|
|
}
|
|
|
|
context.Files.Add(file);
|
|
await context.SaveChangesAsync();
|
|
|
|
return file.Id;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#region Response DTOs
|
|
|
|
/// <summary>
|
|
/// EN: File metadata response DTO for tests.
|
|
/// VI: DTO response metadata file cho tests.
|
|
/// </summary>
|
|
public record FileMetadataResponse(
|
|
Guid Id,
|
|
string FileName,
|
|
string ContentType,
|
|
long FileSizeBytes,
|
|
string UserId,
|
|
string AccessLevel,
|
|
DateTime UploadedAt);
|
|
|
|
/// <summary>
|
|
/// EN: User files list response DTO for tests.
|
|
/// VI: DTO response danh sách files của user cho tests.
|
|
/// </summary>
|
|
public record UserFilesResponse(
|
|
IEnumerable<FileMetadataResponse> Files,
|
|
int TotalCount,
|
|
int Page,
|
|
int PageSize);
|
|
|
|
#endregion
|