Skip to content

Make the test suite safe to run in parallel worker processes - #389

Open
jeremydmiller wants to merge 1 commit into
mainfrom
feature/tests-parallel-safe
Open

Make the test suite safe to run in parallel worker processes#389
jeremydmiller wants to merge 1 commit into
mainfrom
feature/tests-parallel-safe

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Two changes needed before the suite can be split across processes that each own a database.

Measured with Bobcat's supervisor driving the MTP executable directly: 15.9 min → 6.1 min at four workers, 1587/1587 green. No changes to Polecat.Tests.csproj — it is already an MTP host.

1. Never rewrite a connection string by text

This was in three MultiTenancy files and was silently wrong:

ConnectionSource.ConnectionString.Replace("Initial Catalog=master", $"Database={name}")

It only rewrites while the catalog happens to be master. Point POLECAT_TESTING_DATABASE at anything else and the literal is absent, the replace matches nothing, and the "other" database quietly resolves to the current one — so the test asserts against itself.

ConnectionSource now exposes ConnectionStringFor(name), MasterConnectionString and DatabaseName, built on SqlConnectionStringBuilder, which is correct whatever the catalog is called.

Worth noting: fixing this also cleared a MultiTenancy failure that predated any of this work. The 25 MultiTenancy tests now pass against the default master catalog too, where one was failing before.

2. Scope the names of databases a test creates

A database a test creates is a sibling of the process's own database, not a child of it — so giving each worker its own catalog does not isolate them. A hardcoded name is one database shared by every worker on the box:

// WRONG — every worker fights over the same database
private const string DbA = "polecat_tenant_a";

// RIGHT — "master_tenant_a" locally, "polecat_w3_tenant_a" under a parallel runner
private static readonly string DbA = ConnectionSource.Scoped("tenant_a");

Under four workers this produced "User does not have permission to alter database 'polecat_tenant_a'" — non-deterministically, as they raced to create and drop it.

Schema names deliberately keep their hardcoded values: they live inside the catalog, so per-worker databases already separate them. That is why SchemaName = "doc_usage" in nine files is fine.

Not a behaviour change for CI today

Nothing here alters the sequential run. With POLECAT_TESTING_DATABASE unset everything resolves to master exactly as before — verified green on the existing path. This only makes the suite able to be parallelised; adopting that is a separate decision.

Both rules are written up in CLAUDE.md under Testing, since they are easy to reintroduce by accident.

🤖 Generated with Claude Code

https://claude.ai/code/session_012vmmUkRtSFMk1Pbf9E5eor

Two changes, both needed before the suite can be split across processes that
each own a database. Measured with Bobcat's supervisor driving the MTP
executable: 15.9 min sequential -> 6.1 min at four workers, 1587/1587 green.

Never rewrite a connection string by text. This appeared in three MultiTenancy
files and was silently wrong:

    ConnectionSource.ConnectionString.Replace("Initial Catalog=master", $"Database={name}")

It only rewrites while the catalog happens to be "master". Point
POLECAT_TESTING_DATABASE at anything else and the literal is absent, the
replace matches nothing, and the "other" database quietly resolves to the
current one — so the test asserts against itself. ConnectionSource now exposes
ConnectionStringFor / MasterConnectionString / DatabaseName built on
SqlConnectionStringBuilder, which is correct whatever the catalog is called.

Name every database a test creates with ConnectionSource.Scoped(...). A
database a test creates is a sibling of the process's own database, not a child
of it, so giving each worker its own catalog does NOT isolate them. A hardcoded
"polecat_tenant_a" is one database shared by every worker on the box, and they
raced to create and drop it — "User does not have permission to alter database
'polecat_tenant_a'" under four workers. Scoped() yields "master_tenant_a"
locally and "polecat_w3_tenant_a" under a parallel runner.

Schema names deliberately keep hardcoded names: they live inside the catalog,
so per-worker databases already separate them.

Fixing the Replace bug also cleared a MultiTenancy failure that predated any of
this work — the 25 MultiTenancy tests now pass against the default master
catalog too, where one was failing before.

Both rules are written up in CLAUDE.md under Testing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vmmUkRtSFMk1Pbf9E5eor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant