using DramaLing.Api.Tests.Integration.Fixtures; using System.Net; using System.Net.Http.Json; using System.Text.Json; namespace DramaLing.Api.Tests.Integration.EndToEnd; /// /// AI 詞彙生成到儲存完整流程測試 /// 驗證從 AI 分析句子、生成詞彙、同義詞到儲存的完整業務流程 /// public class AIVocabularyWorkflowTests : IntegrationTestBase { public AIVocabularyWorkflowTests(DramaLingWebApplicationFactory factory) : base(factory) { } [Fact] public async Task CompleteAIVocabularyWorkflow_ShouldGenerateAndStoreFlashcard() { // Arrange var client = CreateTestUser1Client(); // Step 1: AI 分析句子生成詞彙 var analysisRequest = new { text = "The magnificent sunset painted the sky with brilliant colors.", targetLevel = "B2", includeGrammar = true, includeVocabulary = true }; var analysisResponse = await client.PostAsJsonAsync("/api/ai/analyze-sentence", analysisRequest); analysisResponse.StatusCode.Should().Be(HttpStatusCode.OK); var analysisContent = await analysisResponse.Content.ReadAsStringAsync(); var analysisJson = JsonSerializer.Deserialize(analysisContent); // 驗證 AI 分析結果包含詞彙資訊 analysisJson.GetProperty("success").GetBoolean().Should().BeTrue(); // Step 2: 模擬從 AI 分析結果中選擇詞彙並建立詞卡 // 假設 AI 分析返回了 "magnificent" 這個詞 var newFlashcard = new { word = "magnificent", translation = "宏偉的,壯麗的", definition = "Very beautiful and impressive", partOfSpeech = "adjective", pronunciation = "/mæɡˈnɪf.ɪ.sənt/", example = "The magnificent sunset painted the sky.", exampleTranslation = "壯麗的夕陽將天空染色。", difficultyLevelNumeric = 4, // B2 synonyms = "[\"splendid\", \"impressive\", \"gorgeous\"]" // AI 生成的同義詞 }; var createResponse = await client.PostAsJsonAsync("/api/flashcards", newFlashcard); createResponse.StatusCode.Should().Be(HttpStatusCode.OK); var createContent = await createResponse.Content.ReadAsStringAsync(); var createJson = JsonSerializer.Deserialize(createContent); // Step 3: 驗證詞卡已正確儲存 var createdFlashcardId = createJson.GetProperty("data").GetProperty("id").GetString(); createdFlashcardId.Should().NotBeNullOrEmpty(); // Step 4: 取得儲存的詞卡並驗證同義詞 var getResponse = await client.GetAsync($"/api/flashcards/{createdFlashcardId}"); getResponse.StatusCode.Should().Be(HttpStatusCode.OK); var getContent = await getResponse.Content.ReadAsStringAsync(); var getJson = JsonSerializer.Deserialize(getContent); var flashcard = getJson.GetProperty("data"); flashcard.GetProperty("word").GetString().Should().Be("magnificent"); flashcard.GetProperty("synonyms").EnumerateArray().Should().HaveCountGreaterThan(0, "應該有同義詞"); } [Fact] public async Task SynonymsGeneration_ShouldBeStoredAndDisplayedCorrectly() { // Arrange var client = CreateTestUser1Client(); // Step 1: 建立包含同義詞的詞卡 var flashcardWithSynonyms = new { word = "brilliant", translation = "聰明的,傑出的", definition = "Exceptionally clever or talented", partOfSpeech = "adjective", pronunciation = "/ˈbrɪl.jənt/", example = "She has a brilliant mind.", exampleTranslation = "她有聰明的頭腦。", difficultyLevelNumeric = 3, // B1 synonyms = "[\"intelligent\", \"smart\", \"clever\", \"outstanding\"]" // JSON 格式的同義詞 }; var createResponse = await client.PostAsJsonAsync("/api/flashcards", flashcardWithSynonyms); createResponse.StatusCode.Should().Be(HttpStatusCode.OK); var createContent = await createResponse.Content.ReadAsStringAsync(); var createJson = JsonSerializer.Deserialize(createContent); var flashcardId = createJson.GetProperty("data").GetProperty("id").GetString(); // Step 2: 取得詞卡並驗證同義詞正確解析 var getResponse = await client.GetAsync($"/api/flashcards/{flashcardId}"); getResponse.StatusCode.Should().Be(HttpStatusCode.OK); var getContent = await getResponse.Content.ReadAsStringAsync(); var getJson = JsonSerializer.Deserialize(getContent); var retrievedFlashcard = getJson.GetProperty("data"); // Step 3: 驗證同義詞格式和內容 var synonymsArray = retrievedFlashcard.GetProperty("synonyms"); var synonymsList = synonymsArray.EnumerateArray().Select(s => s.GetString()).ToList(); synonymsList.Should().Contain("intelligent"); synonymsList.Should().Contain("smart"); synonymsList.Should().Contain("clever"); synonymsList.Should().Contain("outstanding"); synonymsList.Should().HaveCount(4, "應該有4個同義詞"); // Step 4: 驗證同義詞在複習時正確顯示 var dueResponse = await client.GetAsync("/api/flashcards/due"); var dueContent = await dueResponse.Content.ReadAsStringAsync(); var dueJson = JsonSerializer.Deserialize(dueContent); var flashcards = dueJson.GetProperty("data").GetProperty("flashcards"); var targetFlashcard = flashcards.EnumerateArray() .FirstOrDefault(f => f.GetProperty("id").GetString() == flashcardId); if (!targetFlashcard.Equals(default(JsonElement))) { var synonymsInDue = targetFlashcard.GetProperty("synonyms"); synonymsInDue.GetArrayLength().Should().BeGreaterThan(0, "複習時應該顯示同義詞"); } } [Fact] public async Task OptionsGeneration_ShouldProvideValidDistractors() { // Arrange var client = CreateTestUser1Client(); // Step 1: 建立詞卡 var flashcard = new { word = "extraordinary", translation = "非凡的", definition = "Very unusual or remarkable", partOfSpeech = "adjective", difficultyLevelNumeric = 4 // B2 }; var createResponse = await client.PostAsJsonAsync("/api/flashcards", flashcard); var createContent = await createResponse.Content.ReadAsStringAsync(); var createJson = JsonSerializer.Deserialize(createContent); var flashcardId = createJson.GetProperty("data").GetProperty("id").GetString(); // Step 2: 取得詞卡的待複習狀態 (應該包含 AI 生成的選項) var dueResponse = await client.GetAsync("/api/flashcards/due"); dueResponse.StatusCode.Should().Be(HttpStatusCode.OK); var dueContent = await dueResponse.Content.ReadAsStringAsync(); var dueJson = JsonSerializer.Deserialize(dueContent); var flashcards = dueJson.GetProperty("data").GetProperty("flashcards"); var targetFlashcard = flashcards.EnumerateArray() .FirstOrDefault(f => f.GetProperty("id").GetString() == flashcardId); if (!targetFlashcard.Equals(default(JsonElement))) { // Step 3: 驗證 AI 生成的測驗選項 var quizOptions = targetFlashcard.GetProperty("quizOptions"); quizOptions.GetArrayLength().Should().BeGreaterThan(0, "應該有 AI 生成的測驗選項"); // 驗證選項不包含正確答案 (混淆選項) var optionsList = quizOptions.EnumerateArray().Select(o => o.GetString()).ToList(); optionsList.Should().NotContain("非凡的", "混淆選項不應該包含正確翻譯"); } } [Fact] public async Task VocabularyGenerationToReview_EndToEndFlow_ShouldWork() { // Arrange var client = CreateTestUser1Client(); // Step 1: 從AI分析開始 → Step 2: 生成詞卡 → Step 3: 複習詞卡 var analysisRequest = new { text = "The sophisticated algorithm processes complex data efficiently.", targetLevel = "C1" }; await client.PostAsJsonAsync("/api/ai/analyze-sentence", analysisRequest); // Step 2: 建立從分析中得出的詞彙 (模擬用戶選擇 "algorithm") var newFlashcard = new { word = "algorithm", translation = "演算法", definition = "A process or set of rules for calculations", partOfSpeech = "noun", difficultyLevelNumeric = 5, // C1 synonyms = "[\"procedure\", \"method\", \"process\"]" }; var createResponse = await client.PostAsJsonAsync("/api/flashcards", newFlashcard); var createContent = await createResponse.Content.ReadAsStringAsync(); var createJson = JsonSerializer.Deserialize(createContent); var newFlashcardId = createJson.GetProperty("data").GetProperty("id").GetString(); // Step 3: 立即複習新詞卡 var reviewRequest = new { confidence = 1, // 中等信心度 wasSkipped = false, responseTime = 4000 }; var reviewResponse = await client.PostAsJsonAsync($"/api/flashcards/{newFlashcardId}/review", reviewRequest); // Assert: 驗證完整流程 reviewResponse.StatusCode.Should().Be(HttpStatusCode.OK); var reviewContent = await reviewResponse.Content.ReadAsStringAsync(); var reviewJson = JsonSerializer.Deserialize(reviewContent); var reviewResult = reviewJson.GetProperty("data"); reviewResult.GetProperty("newSuccessCount").GetInt32().Should().Be(1, "新詞卡第一次答對應該成功次數為1"); // 驗證下次複習間隔 var nextReviewDate = DateTime.Parse(reviewResult.GetProperty("nextReviewDate").GetString()!); var intervalHours = (nextReviewDate - DateTime.UtcNow).TotalHours; intervalHours.Should().BeInRange(40, 56, "第一次答對應該約2天後再複習 (2^1 = 2天)"); } }