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);
}
}