using System.Net; using System.Net.Http.Json; 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.Requests; using XiangYi.Application.DTOs.Responses; using XiangYi.Application.Interfaces; namespace XiangYi.Api.Tests.AppApi; /// /// 资料控制器集成测试 /// public class ProfileControllerIntegrationTests : IClassFixture> { private readonly WebApplicationFactory _factory; private readonly IProfileService _mockProfileService; public ProfileControllerIntegrationTests(WebApplicationFactory factory) { _mockProfileService = Substitute.For(); _factory = factory.WithWebHostBuilder(builder => { builder.UseEnvironment("Testing"); builder.ConfigureServices(services => { services.RemoveAll(); services.AddSingleton(_mockProfileService); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = TestAuthHandler.AuthenticationScheme; options.DefaultChallengeScheme = TestAuthHandler.AuthenticationScheme; }) .AddScheme( TestAuthHandler.AuthenticationScheme, options => { }); }); }); } /// /// 测试提交资料 - 未授权返回401 /// [Fact] public async Task CreateOrUpdate_WithoutAuth_ReturnsUnauthorized() { // Arrange var client = _factory.CreateClient(); var request = new ProfileRequest { Relationship = 1, Surname = "李", ChildGender = 1 }; // Act var response = await client.PostAsJsonAsync("/api/app/profile", request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } /// /// 测试提交资料 - 授权后成功 /// [Fact] public async Task CreateOrUpdate_WithAuth_ReturnsSuccess() { // Arrange _mockProfileService.CreateOrUpdateAsync(1, Arg.Any()) .Returns(Task.FromResult(100L)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); var request = new ProfileRequest { Relationship = 1, Surname = "李", ChildGender = 1, BirthYear = 1995, Education = 4, WorkProvince = "广东省", WorkCity = "深圳市" }; // Act var response = await client.PostAsJsonAsync("/api/app/profile", request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.Equal(0, result.Code); Assert.Equal(100L, result.Data); } /// /// 测试获取我的资料 - 成功 /// [Fact] public async Task GetMyProfile_WithAuth_ReturnsProfile() { // Arrange var expectedProfile = new ProfileResponse { UserId = 1, Nickname = "李家长(父亲)", Relationship = 1, Surname = "李", ChildGender = 1, BirthYear = 1995, Education = 4 }; _mockProfileService.GetByUserIdAsync(1) .Returns(Task.FromResult(expectedProfile)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); // Act var response = await client.GetAsync("/api/app/profile"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.Equal(0, result.Code); Assert.NotNull(result.Data); Assert.Equal(expectedProfile.Nickname, result.Data.Nickname); } /// /// 测试获取我的资料 - 资料不存在 /// [Fact] public async Task GetMyProfile_ProfileNotExists_ReturnsError() { // Arrange _mockProfileService.GetByUserIdAsync(1) .Returns(Task.FromResult(null)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); // Act var response = await client.GetAsync("/api/app/profile"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(result); Assert.NotEqual(0, result.Code); // 应返回错误码 } /// /// 测试照片数量限制 - 超过5张返回错误 /// [Fact] public async Task UploadPhotos_ExceedsLimit_ReturnsError() { // Arrange _mockProfileService.CanUploadPhotosAsync(1, 3) .Returns(Task.FromResult(false)); // 模拟已有3张,再上传3张超过限制 var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token-1"); // 创建多文件上传请求 using var content = new MultipartFormDataContent(); for (int i = 0; i < 3; i++) { var fileContent = new ByteArrayContent(new byte[] { 0x89, 0x50, 0x4E, 0x47 }); // PNG header fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png"); content.Add(fileContent, "files", $"photo{i}.png"); } // Act var response = await client.PostAsync("/api/app/profile/photos", 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); // 应返回错误码 } }