using System.Net; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using NSubstitute; using Xunit; using XiangYi.AppApi; using XiangYi.Application.DTOs.Responses; using XiangYi.Infrastructure.Storage; namespace XiangYi.Api.Tests.AppApi; /// /// 文件上传控制器集成测试 /// public class UploadControllerIntegrationTests : IClassFixture> { private readonly WebApplicationFactory _factory; private readonly IStorageProvider _mockStorageProvider; public UploadControllerIntegrationTests(WebApplicationFactory factory) { _mockStorageProvider = Substitute.For(); _factory = factory.WithWebHostBuilder(builder => { builder.UseEnvironment("Testing"); builder.ConfigureServices(services => { services.RemoveAll(); services.AddSingleton(_mockStorageProvider); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = TestAuthHandler.AuthenticationScheme; options.DefaultChallengeScheme = TestAuthHandler.AuthenticationScheme; }) .AddScheme( TestAuthHandler.AuthenticationScheme, options => { }); }); }); } /// /// 测试上传语音文件 - 成功 /// [Fact] public async Task UploadVoice_ValidMp3File_ReturnsSuccess() { // Arrange var expectedFileKey = "voice/test.mp3"; var expectedUrl = "https://example.com/voice/test.mp3"; _mockStorageProvider.UploadAsync(Arg.Any(), Arg.Any(), "voice") .Returns(Task.FromResult(expectedFileKey)); _mockStorageProvider.GetAccessUrl(expectedFileKey, Arg.Any()) .Returns(expectedUrl); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); // 创建模拟的 MP3 文件 var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("fake mp3 content")); fileContent.Headers.ContentType = new MediaTypeHeaderValue("audio/mpeg"); content.Add(fileContent, "file", "test.mp3"); // Act var response = await client.PostAsync("/api/app/upload/voice", content); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.Equal(0, result.Code); Assert.NotNull(result.Data); } /// /// 测试上传语音文件 - 不支持的格式 /// [Fact] public async Task UploadVoice_InvalidFormat_ReturnsError() { // Arrange var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); // 创建不支持的文件格式 var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("fake content")); fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); content.Add(fileContent, "file", "test.pdf"); // Act var response = await client.PostAsync("/api/app/upload/voice", content); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.NotEqual(0, result.Code); // 应返回错误码 } /// /// 测试上传语音文件 - 文件为空 /// [Fact] public async Task UploadVoice_EmptyFile_ReturnsError() { // Arrange var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); var content = new MultipartFormDataContent(); // Act var response = await client.PostAsync("/api/app/upload/voice", content); // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } /// /// 测试上传图片文件 - 成功 /// [Fact] public async Task UploadImage_ValidJpgFile_ReturnsSuccess() { // Arrange var expectedFileKey = "images/test.jpg"; var expectedUrl = "https://example.com/images/test.jpg"; _mockStorageProvider.UploadAsync(Arg.Any(), Arg.Any(), "images") .Returns(Task.FromResult(expectedFileKey)); _mockStorageProvider.GetAccessUrl(expectedFileKey, Arg.Any()) .Returns(expectedUrl); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); // 创建模拟的 JPG 文件 var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("fake jpg content")); fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); content.Add(fileContent, "file", "test.jpg"); // Act var response = await client.PostAsync("/api/app/upload/image", content); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.Equal(0, result.Code); Assert.NotNull(result.Data); } /// /// 测试上传图片文件 - 不支持的格式 /// [Fact] public async Task UploadImage_InvalidFormat_ReturnsError() { // Arrange var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); // 创建不支持的文件格式 var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("fake content")); fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); content.Add(fileContent, "file", "test.pdf"); // Act var response = await client.PostAsync("/api/app/upload/image", content); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.NotEqual(0, result.Code); // 应返回错误码 } /// /// 测试上传语音文件 - 支持的所有格式 /// [Theory] [InlineData("test.mp3", "audio/mpeg")] [InlineData("test.wav", "audio/wav")] [InlineData("test.m4a", "audio/mp4")] [InlineData("test.amr", "audio/amr")] public async Task UploadVoice_SupportedFormats_ReturnsSuccess(string fileName, string contentType) { // Arrange var expectedFileKey = $"voice/{fileName}"; var expectedUrl = $"https://example.com/voice/{fileName}"; _mockStorageProvider.UploadAsync(Arg.Any(), Arg.Any(), "voice") .Returns(Task.FromResult(expectedFileKey)); _mockStorageProvider.GetAccessUrl(Arg.Any(), Arg.Any()) .Returns(expectedUrl); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("fake content")); fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType); content.Add(fileContent, "file", fileName); // Act var response = await client.PostAsync("/api/app/upload/voice", content); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.Equal(0, result.Code); } /// /// 测试上传图片文件 - 支持的所有格式 /// [Theory] [InlineData("test.jpg", "image/jpeg")] [InlineData("test.jpeg", "image/jpeg")] [InlineData("test.png", "image/png")] [InlineData("test.gif", "image/gif")] [InlineData("test.webp", "image/webp")] public async Task UploadImage_SupportedFormats_ReturnsSuccess(string fileName, string contentType) { // Arrange var expectedFileKey = $"images/{fileName}"; var expectedUrl = $"https://example.com/images/{fileName}"; _mockStorageProvider.UploadAsync(Arg.Any(), Arg.Any(), "images") .Returns(Task.FromResult(expectedFileKey)); _mockStorageProvider.GetAccessUrl(Arg.Any(), Arg.Any()) .Returns(expectedUrl); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("fake content")); fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType); content.Add(fileContent, "file", fileName); // Act var response = await client.PostAsync("/api/app/upload/image", content); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.Equal(0, result.Code); } /// /// 测试上传文件 - 未授权 /// [Fact] public async Task UploadVoice_NoAuth_ReturnsUnauthorized() { // Arrange var client = _factory.CreateClient(); // 不添加 Authorization 头 var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("fake content")); fileContent.Headers.ContentType = new MediaTypeHeaderValue("audio/mpeg"); content.Add(fileContent, "file", "test.mp3"); // Act var response = await client.PostAsync("/api/app/upload/voice", content); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } }