dramaling-vocab-learning/backend/DramaLing.Api.Tests/Integration/DramaLingWebApplicationFact...

149 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using DramaLing.Api.Data;
using DramaLing.Api.Tests.Integration.Fixtures;
using DramaLing.Api.Tests.Integration.Mocks;
using DramaLing.Api.Services.AI.Gemini;
using DramaLing.Api.Models.Configuration;
namespace DramaLing.Api.Tests.Integration;
/// <summary>
/// API 整合測試的 WebApplicationFactory
/// 提供完整的測試環境設定,包含 InMemory 資料庫和測試配置
/// </summary>
public class DramaLingWebApplicationFactory : WebApplicationFactory<Program>
{
private readonly string _databaseName;
public DramaLingWebApplicationFactory()
{
_databaseName = $"TestDb_{Guid.NewGuid()}";
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// 移除原有的資料庫配置
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<DramaLingDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// 使用 InMemory 資料庫
services.AddDbContext<DramaLingDbContext>(options =>
{
options.UseInMemoryDatabase(_databaseName);
options.EnableSensitiveDataLogging();
});
// 替換 Gemini Client 為 Mock
var geminiDescriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(IGeminiClient));
if (geminiDescriptor != null)
{
services.Remove(geminiDescriptor);
}
services.AddScoped<IGeminiClient, MockGeminiClient>();
// 設定測試用的 Gemini 配置
services.Configure<GeminiOptions>(options =>
{
options.ApiKey = "AIza-test-key-for-integration-testing-purposes-only";
options.BaseUrl = "https://test.googleapis.com";
options.TimeoutSeconds = 10;
options.MaxRetries = 1;
options.Temperature = 0.5;
});
// 建立資料庫並種子資料
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<DramaLingDbContext>();
context.Database.EnsureCreated();
TestDataSeeder.SeedTestData(context);
});
builder.UseEnvironment("Testing");
// 設定測試用環境變數
Environment.SetEnvironmentVariable("USE_INMEMORY_DB", "true");
Environment.SetEnvironmentVariable("DRAMALING_SUPABASE_JWT_SECRET", "test-secret-minimum-32-characters-long-for-jwt-signing-in-test-mode-only");
Environment.SetEnvironmentVariable("DRAMALING_SUPABASE_URL", "https://test.supabase.co");
Environment.SetEnvironmentVariable("DRAMALING_GEMINI_API_KEY", "AIza-test-key-for-integration-testing-purposes-only");
// 設定測試專用的配置
builder.ConfigureAppConfiguration((context, config) =>
{
// 添加測試用的記憶體配置
var testConfig = new Dictionary<string, string>
{
["Supabase:JwtSecret"] = "test-secret-minimum-32-characters-long-for-jwt-signing-in-test-mode-only",
["Supabase:Url"] = "https://test.supabase.co",
["Gemini:ApiKey"] = "AIza-test-key-for-integration-testing-purposes-only"
};
config.AddInMemoryCollection(testConfig);
});
// 設定 Logging 層級
builder.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Warning);
});
}
/// <summary>
/// 取得測試用的 HttpClient並設定預設的 JWT Token
/// </summary>
public HttpClient CreateClientWithAuth(string? token = null)
{
var client = CreateClient();
if (!string.IsNullOrEmpty(token))
{
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
return client;
}
/// <summary>
/// 重置資料庫資料 - 用於測試間的隔離
/// </summary>
public void ResetDatabase()
{
using var scope = Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<DramaLingDbContext>();
// 清除所有資料
context.FlashcardReviews.RemoveRange(context.FlashcardReviews);
context.Flashcards.RemoveRange(context.Flashcards);
context.Users.RemoveRange(context.Users);
context.OptionsVocabularies.RemoveRange(context.OptionsVocabularies);
context.SaveChanges();
// 重新種子測試資料
TestDataSeeder.SeedTestData(context);
}
/// <summary>
/// 取得測試資料庫上下文
/// </summary>
public DramaLingDbContext GetDbContext()
{
var scope = Services.CreateScope();
return scope.ServiceProvider.GetRequiredService<DramaLingDbContext>();
}
}