using System.Net; using System.Text.Json; using Tiku.Domain.Common; using Tiku.Domain.Catalog; using Tiku.Domain.Content; using Tiku.Domain.Tenancy; namespace Tiku.IntegrationTests.Api; public sealed class StudyContentEndpointTests { [Fact] public async Task Vocabulary_units_are_active_sorted_and_region_scoped() { var tenantId = Guid.NewGuid(); var regionId = Guid.NewGuid(); await using var factory = new ApiTestFactory(); await factory.SeedAsync( Tenant(tenantId, "master"), new Region { Id = regionId, TenantId = tenantId, Name = "测试地区" }, new VocabularyUnit { Id = Guid.NewGuid(), TenantId = tenantId, RegionId = regionId, Name = "核心词汇", SortOrder = 2, IsActive = true }, new VocabularyUnit { Id = Guid.NewGuid(), TenantId = tenantId, Name = "全局词汇", SortOrder = 1, IsActive = true }, new VocabularyUnit { Id = Guid.NewGuid(), TenantId = tenantId, RegionId = regionId, Name = "停用词汇", SortOrder = 0, IsActive = false }); using var client = factory.CreateClient(); using var response = await client.GetAsync($"/api/catalog/vocabulary-units?tenantCode=master®ionId={regionId}"); var items = await ReadItemsAsync(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(["全局词汇", "核心词汇"], items.Select(item => item.GetProperty("name").GetString()!).ToArray()); } [Fact] public async Task Vocabulary_words_support_unit_and_keyword_filters() { var tenantId = Guid.NewGuid(); var unitId = Guid.NewGuid(); await using var factory = new ApiTestFactory(); await factory.SeedAsync( Tenant(tenantId, "master"), new VocabularyUnit { Id = unitId, TenantId = tenantId, Name = "Unit 1" }, new VocabularyWord { Id = Guid.NewGuid(), TenantId = tenantId, UnitId = unitId, Word = "abandon", Meaning = "放弃", SortOrder = 1, IsActive = true }, new VocabularyWord { Id = Guid.NewGuid(), TenantId = tenantId, UnitId = unitId, Word = "ability", Meaning = "能力", SortOrder = 2, IsActive = true }, new VocabularyWord { Id = Guid.NewGuid(), TenantId = tenantId, Word = "other", Meaning = "其他", IsActive = true }); using var client = factory.CreateClient(); using var response = await client.GetAsync($"/api/catalog/vocabulary-words?tenantCode=master&unitId={unitId}&keyword=放弃"); var items = await ReadItemsAsync(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); var item = Assert.Single(items); Assert.Equal("abandon", item.GetProperty("word").GetString()); } [Fact] public async Task Handbook_chapters_are_filtered_by_subject() { var tenantId = Guid.NewGuid(); var subjectId = Guid.NewGuid(); await using var factory = new ApiTestFactory(); await factory.SeedAsync( Tenant(tenantId, "master"), new HandbookSubject { Id = subjectId, TenantId = tenantId, Name = "语文手册", IsActive = true }, new HandbookChapter { Id = Guid.NewGuid(), TenantId = tenantId, SubjectId = subjectId, Name = "古诗词", SortOrder = 1, IsActive = true }, new HandbookChapter { Id = Guid.NewGuid(), TenantId = tenantId, Name = "其他章节", SortOrder = 2, IsActive = true }); using var client = factory.CreateClient(); using var response = await client.GetAsync($"/api/catalog/handbook-chapters?tenantCode=master&subjectId={subjectId}"); var items = await ReadItemsAsync(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("古诗词", Assert.Single(items).GetProperty("name").GetString()); } [Fact] public async Task Handbook_entries_hide_content_until_requested() { var tenantId = Guid.NewGuid(); var chapterId = Guid.NewGuid(); await using var factory = new ApiTestFactory(); await factory.SeedAsync( Tenant(tenantId, "master"), new HandbookChapter { Id = chapterId, TenantId = tenantId, Name = "章节", IsActive = true }, new HandbookEntry { Id = Guid.NewGuid(), TenantId = tenantId, ChapterId = chapterId, Title = "知识点", Summary = "摘要", Content = "完整正文", SortOrder = 1, IsActive = true }); using var client = factory.CreateClient(); using var listResponse = await client.GetAsync($"/api/catalog/handbook-entries?tenantCode=master&chapterId={chapterId}"); using var detailResponse = await client.GetAsync($"/api/catalog/handbook-entries?tenantCode=master&chapterId={chapterId}&includeContent=true"); var listItem = Assert.Single(await ReadItemsAsync(listResponse)); var detailItem = Assert.Single(await ReadItemsAsync(detailResponse)); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, detailResponse.StatusCode); Assert.Equal(JsonValueKind.Null, listItem.GetProperty("content").ValueKind); Assert.Equal("完整正文", detailItem.GetProperty("content").GetString()); } private static Tenant Tenant(Guid id, string slug) { return new Tenant { Id = id, Slug = slug, Name = slug, Status = TenantStatus.Active, Metadata = JsonDefaults.Object() }; } private static async Task ReadItemsAsync(HttpResponseMessage response) { var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); return body.RootElement .GetProperty("items") .EnumerateArray() .Select(item => item.Clone()) .ToArray(); } }