Files
WorkClub Automation f8f3e0f01e test(harness): stabilize backend+frontend QA test suite (12/12+63/63 unit+integration, 45/45 frontend)
Stabilize test harness across full stack:

Backend integration tests:
- Fix Auth/Club/Migration/RLS/Member/Tenant/RLS Isolation/Shift/Task test suites
- Add AssemblyInfo.cs for test configuration
- Enhance CustomWebApplicationFactory + TestAuthHandler for stable test environment
- Expand RlsIsolationTests with comprehensive multi-tenant RLS verification

Frontend test harness:
- Align vitest.config.ts with backend API changes
- Add bunfig.toml for bun test environment stability
- Enhance api.test.ts with proper test setup integration
- Expand test/setup.ts with fixture initialization

All tests now passing: backend 12/12 unit + 63/63 integration, frontend 45/45
2026-03-06 09:19:32 +01:00

119 lines
3.7 KiB
C#

using Dapper;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using Testcontainers.PostgreSql;
using WorkClub.Infrastructure.Data;
namespace WorkClub.Tests.Integration.Data;
public class MigrationTests : IAsyncLifetime
{
private PostgreSqlContainer? _container;
private string? _connectionString;
public async Task InitializeAsync()
{
_container = new PostgreSqlBuilder()
.WithImage("postgres:16-alpine")
.Build();
await _container.StartAsync();
_connectionString = _container.GetConnectionString();
}
public async Task DisposeAsync()
{
if (_container != null)
{
await _container.DisposeAsync();
}
}
[Fact]
public async Task Migration_AppliesSuccessfully_CreatesAllTables()
{
// Arrange
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseNpgsql(_connectionString)
.Options;
// Act
await using var context = new AppDbContext(options);
await context.Database.MigrateAsync();
// Assert - verify all expected tables exist
await using var connection = new NpgsqlConnection(_connectionString);
var tables = (await connection.QueryAsync<string>(
@"SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name")).ToList();
Assert.Contains("clubs", tables);
Assert.Contains("members", tables);
Assert.Contains("work_items", tables);
Assert.Contains("shifts", tables);
Assert.Contains("shift_signups", tables);
}
[Fact]
public async Task Migration_CreatesCorrectIndexes()
{
// Arrange
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseNpgsql(_connectionString)
.Options;
// Act
await using var context = new AppDbContext(options);
await context.Database.MigrateAsync();
// Assert - verify critical indexes exist
await using var connection = new NpgsqlConnection(_connectionString);
var indexes = (await connection.QueryAsync<string>(
@"SELECT indexname
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY indexname")).ToList();
// TenantId indexes
Assert.Contains(indexes, i => i.Contains("tenant_id"));
// ClubId indexes
Assert.Contains(indexes, i => i.Contains("club_id"));
// Status indexes for WorkItem
Assert.Contains(indexes, i => i.Contains("status"));
}
[Fact]
public async Task Migration_CreatesExpectedSchema()
{
// Arrange
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseNpgsql(_connectionString)
.Options;
// Act
await using var context = new AppDbContext(options);
await context.Database.MigrateAsync();
// Assert - verify schema integrity (RLS and policies are applied via SeedDataService, not migrations)
await using var connection = new NpgsqlConnection(_connectionString);
// Verify tenant_id columns exist on all tables
var tenantColumns = (await connection.QueryAsync<string>(
@"SELECT table_name
FROM information_schema.columns
WHERE table_schema = 'public'
AND column_name = 'TenantId'
ORDER BY table_name")).ToList();
Assert.Contains("clubs", tenantColumns);
Assert.Contains("members", tenantColumns);
Assert.Contains("work_items", tenantColumns);
Assert.Contains("shifts", tenantColumns);
Assert.Contains("shift_signups", tenantColumns);
}
}