149 lines
5.5 KiB
C#
149 lines
5.5 KiB
C#
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>();
|
||
}
|
||
} |