# MigrateAllSeedAll Pattern

## Purpose

An integration-level verification gate that proves, against a real database,
that every module DbContext can migrate from scratch and that every seeder
runs successfully, twice (idempotency check). Failures here mean the Host
would fail to start or seeds would corrupt data on repeated runs.

## The ModuleMigrationManifest

Define a single, central registry that lists every module DbContext registered
in the application. The gate iterates this manifest rather than discovering
contexts by convention, so a context the Host forgot to wire is caught by the
manifest too.

Example shape (pseudo-code, adapt to your DI/registration pattern):

    // ModuleMigrationManifest.cs
    public static class ModuleMigrationManifest
    {
        // Add one entry per module that owns a DbContext.
        // Both the Host's startup loop and the MigrateAllSeedAll gate
        // read from this single source of truth.
        public static readonly IReadOnlyList<Type> DbContextTypes = new[]
        {
            typeof(ModuleADbContext),
            typeof(ModuleBDbContext),
            // ...
        };
    }

Benefits:
- A context added to a module but forgotten in the manifest causes the gate to fail.
- The Host startup loop can call the same manifest to guarantee every context is migrated before traffic is served.
- No magic scanning; the list is explicit and reviewable.

## Gate steps

    [Fact(Skip = "Category=RealSql")]
    public async Task MigrateAllAndSeedAll_TwiceIsIdempotent()
    {
        // 1. Spin up an ephemeral database
        //    (e.g. Testcontainers SqlServerBuilder, or a localdb connection string
        //    injected via environment variable).
        await using var db = await StartEphemeralDatabase();

        // 2. Apply migrations for every module context from the manifest.
        foreach (var contextType in ModuleMigrationManifest.DbContextTypes)
        {
            var context = (DbContext)serviceProvider.GetRequiredService(contextType);
            await context.Database.MigrateAsync();
        }

        // 3. Run every seeder once.
        foreach (var seeder in serviceProvider.GetServices<IDataSeeder>())
        {
            await seeder.SeedAsync(CancellationToken.None);
        }

        // 4. Run every seeder a second time (idempotency).
        foreach (var seeder in serviceProvider.GetServices<IDataSeeder>())
        {
            await seeder.SeedAsync(CancellationToken.None);
        }

        // 5. Smoke-read the anchor entity to confirm the seeded data is
        //    queryable and the schema is correct.
        //    "The anchor entity" is the primary through-line entity in your
        //    application -- the record that must exist for other modules
        //    to function (e.g. a canonical customer/account/employee record
        //    seeded by the registration module).
        var anchor = await anchorRepository.FindAsync(WellKnownAnchorId);
        Assert.NotNull(anchor);
    }

## Why MigrateAsync, not EnsureCreated

`EnsureCreated` creates the schema directly from the model without running
migrations. It bypasses the migration history table (`__EFMigrationsHistory`)
and produces a schema that cannot be updated by future migrations. Integration
tests that use `EnsureCreated` give a false green even when the migration SQL
itself is broken.

Always use `MigrateAsync` in integration tests and startup paths. The
`EnsureCreatedInIntegration` detector in this pack flags `EnsureCreated` calls
inside `\Integration\` test files.

## Reference

The concrete verification-gate design that inspired this pattern is recorded
in your project's planning notes (the original document is project-specific
and will not be present in a fresh installation).

That design covers the preflight gate EBGRI model (Environment, Build, Gate,
Run, Integration), the selftest anti-no-op design, and the reasoning behind
separating generic and stack-specific detectors into separate modules.

## Summary checklist

- [ ] `ModuleMigrationManifest` lists every module DbContext
- [ ] Gate migrates ALL contexts before running any seeder
- [ ] Seeders run twice (idempotency)
- [ ] Anchor entity is smoke-read after seeding
- [ ] Gate is tagged `Category=RealSql` and excluded from `-Fast` runs
- [ ] `EnsureCreated` is absent from all `\Integration\` test files
