using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.Extensions.DependencyInjection; using Tiku.Infrastructure.Persistence; using Tiku.IntegrationTests.Api; namespace Tiku.IntegrationTests; public sealed class MigrationExecutionTests { [Fact] public async Task Api_test_database_uses_postgresql_and_applies_every_migration() { await using var factory = new ApiTestFactory(); using var scope = factory.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); var appliedMigrations = await dbContext.Database.GetAppliedMigrationsAsync(); var expectedMigrations = dbContext.Database.GetMigrations(); Assert.Equal("Npgsql.EntityFrameworkCore.PostgreSQL", dbContext.Database.ProviderName); Assert.Equal(expectedMigrations, appliedMigrations); Assert.NotEmpty(appliedMigrations); Assert.True(await dbContext.Database.CanConnectAsync()); } [Fact] public async Task Migration_script_contains_database_guard_triggers() { await using var factory = new ApiTestFactory(); using var scope = factory.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); var migrator = dbContext.GetService(); var script = migrator.GenerateScript(); Assert.Contains("drop trigger if exists trg_tenant_question_references_platform_or_self_owner", script); Assert.Contains("create or replace function tiku_guard_tenant_question_reference()", script); Assert.Contains("create trigger trg_tenant_question_references_platform_or_self_owner", script); Assert.Contains("drop trigger if exists trg_taxonomy_nodes_parent_platform_or_self_owner", script); Assert.Contains("create or replace function tiku_guard_taxonomy_parent_owner()", script); Assert.Contains("create trigger trg_taxonomy_nodes_parent_platform_or_self_owner", script); Assert.Contains("drop trigger if exists trg_saas_offering_versions_published_immutable", script); Assert.Contains("create or replace function tiku_guard_saas_offering_version_immutable()", script); Assert.Contains("create trigger trg_saas_offering_versions_published_immutable", script); Assert.Contains("create or replace function tiku_guard_saas_offering_version_child_immutable()", script); Assert.Contains("create trigger trg_saas_offering_version_features_published_immutable", script); Assert.Contains("create trigger trg_saas_offering_version_limits_published_immutable", script); Assert.Contains("create trigger trg_tenant_saas_subscriptions_base_plan", script); Assert.Contains("create trigger trg_tenant_saas_subscription_items_offering_type", script); Assert.Contains("create or replace function tiku_guard_saas_subscription_offering_type()", script); Assert.Contains("CREATE OR REPLACE FUNCTION tiku_bump_tenant_authorization_version()", script); Assert.Contains("CREATE OR REPLACE FUNCTION tiku_bump_platform_authorization_version()", script); Assert.Contains("CREATE TRIGGER trg_backend_permissions_authorization_version", script); Assert.Contains("CREATE INDEX IF NOT EXISTS ix_scoreline_records_field_values_jsonb_path", script); Assert.Contains("CREATE INDEX IF NOT EXISTS ix_scoreline_records_school_name_trgm", script); Assert.Contains("CREATE INDEX IF NOT EXISTS ix_background_jobs_pending_due", script); Assert.Contains("CREATE OR REPLACE FUNCTION tiku_guard_question_revision_immutable()", script); Assert.Contains("CREATE TRIGGER trg_product_manifest_resources_published_immutable", script); Assert.Contains("CREATE TRIGGER trg_content_release_questions_release_immutable", script); } [Fact] public async Task Migrated_database_contains_required_non_model_objects() { await using var factory = new ApiTestFactory(); using var scope = factory.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); await dbContext.Database.OpenConnectionAsync(); try { Assert.Equal(3L, await CountAsync(""" SELECT count(*) FROM pg_proc WHERE proname IN ( 'tiku_bump_tenant_authorization_version', 'tiku_bump_platform_authorization_version', 'tiku_bump_all_authorization_versions') """)); Assert.Equal(7L, await CountAsync(""" SELECT count(*) FROM pg_trigger WHERE NOT tgisinternal AND tgname IN ( 'trg_tenant_backend_roles_authorization_version', 'trg_tenant_backend_role_permissions_authorization_version', 'trg_tenant_backend_user_roles_authorization_version', 'trg_platform_backend_roles_authorization_version', 'trg_platform_backend_role_permissions_authorization_version', 'trg_platform_backend_user_roles_authorization_version', 'trg_backend_permissions_authorization_version') """)); Assert.Equal(6L, await CountAsync(""" SELECT count(*) FROM pg_indexes WHERE schemaname = current_schema() AND indexname IN ( 'ix_scoreline_records_tenant_id_year_school_name_major_name_id', 'ix_scoreline_records_field_values_jsonb_path', 'ix_scoreline_records_school_name_trgm', 'ix_scoreline_records_major_name_trgm', 'ix_background_jobs_pending_due', 'ix_background_jobs_processing_lease') """)); Assert.Equal(1L, await CountAsync(""" SELECT count(*) FROM authorization_scope_versions WHERE realm = 'platform' AND tenant_id IS NULL """)); } finally { await dbContext.Database.CloseConnectionAsync(); } async Task CountAsync(string sql) { await using var command = dbContext.Database.GetDbConnection().CreateCommand(); command.CommandText = sql; return Convert.ToInt64(await command.ExecuteScalarAsync()); } } }