forked from xiongyuxing/tiku-backend.net
198 lines
7.3 KiB
C#
198 lines
7.3 KiB
C#
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Npgsql;
|
|
using Tiku.Application.Commerce;
|
|
using Tiku.Application.Auth;
|
|
using Tiku.Application.Growth;
|
|
using Tiku.Application.Storage;
|
|
using Tiku.Application.Security;
|
|
using Tiku.Application.Tenancy;
|
|
using Tiku.Api;
|
|
using Tiku.Domain.Identity;
|
|
using Tiku.Domain.QuestionBanks;
|
|
using Tiku.Domain.Content;
|
|
using Tiku.Domain.Tenancy;
|
|
using Tiku.IntegrationTests.Infrastructure;
|
|
using Tiku.Infrastructure.Persistence;
|
|
|
|
namespace Tiku.IntegrationTests.Api;
|
|
|
|
public sealed class ApiTestFactory(
|
|
IWechatOAuthClient? wechatOAuthClient = null,
|
|
IObjectStorageService? objectStorageService = null,
|
|
IReferralQrcodeGenerator? referralQrcodeGenerator = null,
|
|
IPaymentProviderGateway? paymentProviderGateway = null,
|
|
IDomainOwnershipVerifier? domainOwnershipVerifier = null,
|
|
IDomainGatewayProvisioner? domainGatewayProvisioner = null) : WebApplicationFactory<ApiProgramMarker>
|
|
{
|
|
private readonly PostgresTestDatabase database = PostgresTestDatabase.Create();
|
|
|
|
protected override void ConfigureWebHost(Microsoft.AspNetCore.Hosting.IWebHostBuilder builder)
|
|
{
|
|
builder.ConfigureServices(services =>
|
|
{
|
|
foreach (var descriptor in services
|
|
.Where(descriptor =>
|
|
descriptor.ServiceType == typeof(NpgsqlDataSource) ||
|
|
descriptor.ServiceType == typeof(TenantIsolationSaveChangesInterceptor) ||
|
|
descriptor.ServiceType == typeof(DbContextOptions<TikuDbContext>) ||
|
|
descriptor.ServiceType.FullName?.Contains(nameof(TikuDbContext), StringComparison.Ordinal) == true)
|
|
.ToArray())
|
|
{
|
|
services.Remove(descriptor);
|
|
}
|
|
|
|
services.AddSingleton(_ => NpgsqlDataSource.Create(database.ConnectionString));
|
|
services.AddScoped<TenantIsolationSaveChangesInterceptor>();
|
|
services.AddDbContext<TikuDbContext>((serviceProvider, options) =>
|
|
{
|
|
var dataSource = serviceProvider.GetRequiredService<NpgsqlDataSource>();
|
|
options.UseNpgsql(dataSource, npgsql =>
|
|
npgsql.MigrationsAssembly(typeof(TikuDbContext).Assembly.FullName));
|
|
options.AddInterceptors(serviceProvider.GetRequiredService<TenantIsolationSaveChangesInterceptor>());
|
|
});
|
|
|
|
if (wechatOAuthClient is not null)
|
|
{
|
|
services.AddSingleton(wechatOAuthClient);
|
|
}
|
|
|
|
if (objectStorageService is not null)
|
|
{
|
|
services.AddSingleton(objectStorageService);
|
|
}
|
|
|
|
if (referralQrcodeGenerator is not null)
|
|
{
|
|
services.AddSingleton(referralQrcodeGenerator);
|
|
}
|
|
|
|
if (paymentProviderGateway is not null)
|
|
{
|
|
services.AddSingleton(paymentProviderGateway);
|
|
}
|
|
|
|
if (domainOwnershipVerifier is not null)
|
|
{
|
|
services.RemoveAll<IDomainOwnershipVerifier>();
|
|
services.AddSingleton(domainOwnershipVerifier);
|
|
}
|
|
|
|
if (domainGatewayProvisioner is not null)
|
|
{
|
|
services.RemoveAll<IDomainGatewayProvisioner>();
|
|
services.AddSingleton(domainGatewayProvisioner);
|
|
}
|
|
});
|
|
}
|
|
|
|
public async Task SeedAsync(params object[] entities)
|
|
{
|
|
using var scope = Services.CreateScope();
|
|
scope.ServiceProvider.GetRequiredService<ITenantContextInitializer>()
|
|
.InitializeSystem(null, "Integration test fixture seeding");
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
dbContext.AddRange(entities);
|
|
await dbContext.SaveChangesAsync();
|
|
}
|
|
|
|
public IServiceScope CreateSystemScope(string reason = "Integration test verification")
|
|
{
|
|
var scope = Services.CreateScope();
|
|
scope.ServiceProvider.GetRequiredService<ITenantContextInitializer>()
|
|
.InitializeSystem(null, reason);
|
|
return scope;
|
|
}
|
|
|
|
public IServiceScope CreateTenantScope(Guid tenantId, string? tenantCode = null)
|
|
{
|
|
var scope = Services.CreateScope();
|
|
scope.ServiceProvider.GetRequiredService<ITenantContextInitializer>()
|
|
.Initialize(tenantId, tenantCode, TenantResolutionSource.TenantCode);
|
|
return scope;
|
|
}
|
|
|
|
public async Task SeedQuestionWithVersionAsync(Question question, QuestionVersion version)
|
|
{
|
|
question.CurrentVersionId = null;
|
|
await SeedAsync(question);
|
|
await SeedAsync(version);
|
|
|
|
using var scope = Services.CreateScope();
|
|
scope.ServiceProvider.GetRequiredService<ITenantContextInitializer>()
|
|
.InitializeSystem(question.TenantId, "Integration test question version linking");
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
var persistedQuestion = await dbContext.Questions.SingleAsync(item =>
|
|
item.TenantId == question.TenantId && item.Id == question.Id);
|
|
persistedQuestion.CurrentVersionId = version.Id;
|
|
await dbContext.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task<TenantQuestionReference> SeedQuestionReferenceAsync(
|
|
Guid tenantId,
|
|
Guid questionOwnerTenantId,
|
|
Guid questionId,
|
|
QuestionSource source = QuestionSource.Tenant)
|
|
{
|
|
var reference = new TenantQuestionReference
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = tenantId,
|
|
QuestionOwnerTenantId = questionOwnerTenantId,
|
|
QuestionId = questionId,
|
|
Source = source
|
|
};
|
|
await SeedAsync(reference);
|
|
return reference;
|
|
}
|
|
|
|
public async Task<Guid> SeedActiveSessionAsync(
|
|
Guid userId,
|
|
Guid? tenantId = null,
|
|
string tokenHash = "integration-test-token-hash")
|
|
{
|
|
var resolvedTenantId = tenantId ?? Guid.NewGuid();
|
|
await SeedAsync(
|
|
new Tenant
|
|
{
|
|
Id = resolvedTenantId,
|
|
Slug = resolvedTenantId.ToString("N"),
|
|
Name = "Test Tenant"
|
|
},
|
|
new User
|
|
{
|
|
Id = userId,
|
|
Phone = "13800000000"
|
|
},
|
|
new AuthSession
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = resolvedTenantId,
|
|
UserId = userId,
|
|
TokenHash = tokenHash,
|
|
Provider = "test",
|
|
ExpiresAt = DateTimeOffset.UtcNow.AddHours(1)
|
|
});
|
|
|
|
using var scope = Services.CreateScope();
|
|
scope.ServiceProvider.GetRequiredService<ITenantContextInitializer>()
|
|
.InitializeSystem(resolvedTenantId, "Integration test session lookup");
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
return await dbContext.AuthSessions
|
|
.Where(session => session.UserId == userId)
|
|
.Select(session => session.Id)
|
|
.SingleAsync();
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
if (disposing)
|
|
{
|
|
database.Dispose();
|
|
}
|
|
}
|
|
}
|