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.AdminApi; using XiangYi.Application.DTOs.Requests; using XiangYi.Application.DTOs.Responses; using XiangYi.Application.Interfaces; namespace XiangYi.Api.Tests.AdminApi; /// /// 后台敏感词管理控制器集成测试 /// public class AdminSensitiveWordControllerIntegrationTests : IClassFixture> { private readonly WebApplicationFactory _factory; private readonly IAdminSensitiveWordService _mockAdminSensitiveWordService; public AdminSensitiveWordControllerIntegrationTests(WebApplicationFactory factory) { _mockAdminSensitiveWordService = Substitute.For(); _factory = factory.WithWebHostBuilder(builder => { builder.UseEnvironment("Testing"); builder.ConfigureServices(services => { services.RemoveAll(); services.AddSingleton(_mockAdminSensitiveWordService); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = AdminTestAuthHandler.AuthenticationScheme; options.DefaultChallengeScheme = AdminTestAuthHandler.AuthenticationScheme; }) .AddScheme( AdminTestAuthHandler.AuthenticationScheme, options => { }); }); }); } /// /// 测试获取敏感词列表 - 未授权返回401 /// [Fact] public async Task GetSensitiveWordList_WithoutAuth_ReturnsUnauthorized() { var client = _factory.CreateClient(); var response = await client.GetAsync("/api/admin/sensitiveWords"); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } /// /// 测试获取敏感词列表 - 授权后成功 /// [Fact] public async Task GetSensitiveWordList_WithAuth_ReturnsSuccess() { var expectedResult = new PagedResult { Items = new List { new AdminSensitiveWordDto { Id = 1, Word = "敏感词1", Category = "广告", Level = 1, LevelName = "替换", Status = 1, StatusName = "启用" } }, Total = 1, PageIndex = 1, PageSize = 10 }; _mockAdminSensitiveWordService.GetSensitiveWordListAsync(Arg.Any()) .Returns(Task.FromResult(expectedResult)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var response = await client.GetAsync("/api/admin/sensitiveWords"); 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.Single(result.Data.Items); } /// /// 测试获取敏感词详情 - 成功 /// [Fact] public async Task GetSensitiveWordDetail_WithAuth_ReturnsSuccess() { var expectedResult = new AdminSensitiveWordDto { Id = 1, Word = "敏感词1", Category = "广告", Level = 1, LevelName = "替换", Status = 1, StatusName = "启用" }; _mockAdminSensitiveWordService.GetSensitiveWordByIdAsync(1) .Returns(Task.FromResult(expectedResult)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var response = await client.GetAsync("/api/admin/sensitiveWords/1"); 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(expectedResult.Id, result.Data.Id); } /// /// 测试创建敏感词 - 成功 /// [Fact] public async Task CreateSensitiveWord_WithAuth_ReturnsSuccess() { _mockAdminSensitiveWordService.CreateSensitiveWordAsync(Arg.Any(), 1) .Returns(Task.FromResult(100L)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var request = new CreateSensitiveWordRequest { Word = "新敏感词", Category = "广告", Level = 1, Status = 1 }; var response = await client.PostAsJsonAsync("/api/admin/sensitiveWords", request); 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 UpdateSensitiveWord_WithAuth_ReturnsSuccess() { _mockAdminSensitiveWordService.UpdateSensitiveWordAsync(1, Arg.Any(), 1) .Returns(Task.FromResult(true)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var request = new UpdateSensitiveWordRequest { Word = "更新后的敏感词", Category = "广告", Level = 2, Status = 1 }; var response = await client.PutAsJsonAsync("/api/admin/sensitiveWords/1", request); 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); } /// /// 测试删除敏感词 - 成功 /// [Fact] public async Task DeleteSensitiveWord_WithAuth_ReturnsSuccess() { _mockAdminSensitiveWordService.DeleteSensitiveWordAsync(1, 1) .Returns(Task.FromResult(true)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var response = await client.DeleteAsync("/api/admin/sensitiveWords/1"); 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); } /// /// 测试批量删除敏感词 - 成功 /// [Fact] public async Task BatchDeleteSensitiveWords_WithAuth_ReturnsSuccess() { _mockAdminSensitiveWordService.BatchDeleteSensitiveWordsAsync(Arg.Any>(), 1) .Returns(Task.FromResult(3)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var request = new BatchDeleteSensitiveWordRequest { Ids = new List { 1, 2, 3 } }; var response = await client.PostAsJsonAsync("/api/admin/sensitiveWords/batchDelete", request); 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(3, result.Data); } /// /// 测试批量导入敏感词 - 成功 /// [Fact] public async Task ImportSensitiveWords_WithAuth_ReturnsSuccess() { var expectedResult = new ImportSensitiveWordResult { TotalCount = 10, SuccessCount = 8, SkippedCount = 2, UpdatedCount = 0, FailedCount = 0 }; _mockAdminSensitiveWordService.ImportSensitiveWordsAsync(Arg.Any(), 1) .Returns(Task.FromResult(expectedResult)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var request = new ImportSensitiveWordRequest { Words = new List { "敏感词1", "敏感词2", "敏感词3" }, Category = "广告", Level = 1, OverwriteExisting = false }; var response = await client.PostAsJsonAsync("/api/admin/sensitiveWords/import", request); 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(expectedResult.TotalCount, result.Data.TotalCount); Assert.Equal(expectedResult.SuccessCount, result.Data.SuccessCount); } /// /// 测试更新敏感词状态 - 成功 /// [Fact] public async Task UpdateSensitiveWordStatus_WithAuth_ReturnsSuccess() { _mockAdminSensitiveWordService.UpdateSensitiveWordStatusAsync(1, 2, 1) .Returns(Task.FromResult(true)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var response = await client.PutAsync("/api/admin/sensitiveWords/1/status/2", null); 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); } /// /// 测试获取敏感词分类列表 - 成功 /// [Fact] public async Task GetCategories_WithAuth_ReturnsSuccess() { var expectedResult = new List { "政治", "色情", "广告", "其他" }; _mockAdminSensitiveWordService.GetCategoriesAsync() .Returns(Task.FromResult(expectedResult)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var response = await client.GetAsync("/api/admin/sensitiveWords/categories"); 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(4, result.Data.Count); } /// /// 测试敏感词CRUD流程 - 完整流程测试 /// [Fact] public async Task SensitiveWordCRUD_CompleteFlow_Success() { var wordId = 100L; _mockAdminSensitiveWordService.CreateSensitiveWordAsync(Arg.Any(), 1) .Returns(Task.FromResult(wordId)); _mockAdminSensitiveWordService.GetSensitiveWordByIdAsync(wordId) .Returns(Task.FromResult(new AdminSensitiveWordDto { Id = wordId, Word = "测试敏感词", Category = "广告", Level = 1, Status = 1 })); _mockAdminSensitiveWordService.UpdateSensitiveWordAsync(wordId, Arg.Any(), 1) .Returns(Task.FromResult(true)); _mockAdminSensitiveWordService.DeleteSensitiveWordAsync(wordId, 1) .Returns(Task.FromResult(true)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); // Create var createRequest = new CreateSensitiveWordRequest { Word = "测试敏感词", Category = "广告", Level = 1, Status = 1 }; var createResponse = await client.PostAsJsonAsync("/api/admin/sensitiveWords", createRequest); Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode); // Read var readResponse = await client.GetAsync($"/api/admin/sensitiveWords/{wordId}"); Assert.Equal(HttpStatusCode.OK, readResponse.StatusCode); // Update var updateRequest = new UpdateSensitiveWordRequest { Word = "更新后的敏感词", Category = "广告", Level = 2, Status = 1 }; var updateResponse = await client.PutAsJsonAsync($"/api/admin/sensitiveWords/{wordId}", updateRequest); Assert.Equal(HttpStatusCode.OK, updateResponse.StatusCode); // Delete var deleteResponse = await client.DeleteAsync($"/api/admin/sensitiveWords/{wordId}"); Assert.Equal(HttpStatusCode.OK, deleteResponse.StatusCode); } /// /// 测试敏感词列表按分类筛选 - 成功 /// [Fact] public async Task GetSensitiveWordList_WithCategoryFilter_ReturnsFilteredResults() { var expectedResult = new PagedResult { Items = new List { new AdminSensitiveWordDto { Id = 1, Word = "广告词", Category = "广告", Level = 1, Status = 1 } }, Total = 1, PageIndex = 1, PageSize = 10 }; _mockAdminSensitiveWordService.GetSensitiveWordListAsync(Arg.Is(r => r.Category == "广告")) .Returns(Task.FromResult(expectedResult)); var client = _factory.CreateClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer admin-token-1"); var response = await client.GetAsync("/api/admin/sensitiveWords?category=广告"); 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.All(result.Data.Items, item => Assert.Equal("广告", item.Category)); } }