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