First half of T12 (parallel-safe BDD scenario isolation, ADR-0025 follow-up).
PR #28 had to revert BDD_SCHEMA_ISOLATION because the existing SetupScenarioSchema created empty per-scenario schemas — no migrations were run on them. This PR adds the missing factory that opens a *PostgresRepository from an arbitrary DSN AND runs AutoMigrate on that connection.
Opens a Postgres repo via an explicit DSN, runs AutoMigrate. The DSN can include search_path=<schema> so the migrations create tables in a per-scenario schema instead of public.
BuildSchemaIsolatedDSN(cfg, schemaName) string
Builds a DSN with search_path=<schema> from a base config object.
Existing Close() reused
For per-scenario teardown.
Verification
New integration test TestNewPostgresRepositoryFromDSN_SchemaIsolation verifies:
AutoMigrate creates the users table inside the per-scenario schema
A CreateUser through the isolated repo writes ONLY into the per-scenario schema
public.users sees ZERO rows for the test user (proves no leak)
The per-scenario schema sees exactly 1 row
Skips gracefully when DLC_DATABASE_HOST is not set (CI safe).
$ go test -count=1 ./pkg/user/... -run TestNewPostgresRepositoryFromDSN_SchemaIsolation
ok dance-lessons-coach/pkg/user 0.514s
Out of scope (T12 stage 2/2)
Wiring this factory into pkg/bdd/testserver/SetupScenarioSchema
Spawning a fresh *server.Server per scenario (requires a NewServerWithUserRepo factory)
Removing -p 1 from scripts/run-bdd-tests.sh after parallel safety is achieved
That follow-up PR will be smaller because this one establishes the contract.
go test ./pkg/... PASS (all packages, no regression)
New integration test PASS with Postgres up
CI passes
## Summary
First half of T12 (parallel-safe BDD scenario isolation, ADR-0025 follow-up).
PR #28 had to revert `BDD_SCHEMA_ISOLATION` because the existing `SetupScenarioSchema` created empty per-scenario schemas — no migrations were run on them. This PR adds the **missing factory** that opens a `*PostgresRepository` from an arbitrary DSN AND runs AutoMigrate on that connection.
## What's new
### `NewPostgresRepositoryFromDSN(cfg, dsn) (*PostgresRepository, error)`
Opens a Postgres repo via an explicit DSN, runs AutoMigrate. The DSN can include `search_path=<schema>` so the migrations create tables in a per-scenario schema instead of public.
### `BuildSchemaIsolatedDSN(cfg, schemaName) string`
Builds a DSN with `search_path=<schema>` from a base config object.
### Existing `Close()` reused
For per-scenario teardown.
## Verification
New integration test `TestNewPostgresRepositoryFromDSN_SchemaIsolation` verifies:
- AutoMigrate creates the `users` table inside the per-scenario schema
- A `CreateUser` through the isolated repo writes ONLY into the per-scenario schema
- `public.users` sees ZERO rows for the test user (proves no leak)
- The per-scenario schema sees exactly 1 row
Skips gracefully when `DLC_DATABASE_HOST` is not set (CI safe).
```
$ go test -count=1 ./pkg/user/... -run TestNewPostgresRepositoryFromDSN_SchemaIsolation
ok dance-lessons-coach/pkg/user 0.514s
```
## Out of scope (T12 stage 2/2)
- Wiring this factory into `pkg/bdd/testserver/SetupScenarioSchema`
- Spawning a fresh `*server.Server` per scenario (requires a `NewServerWithUserRepo` factory)
- Removing `-p 1` from `scripts/run-bdd-tests.sh` after parallel safety is achieved
That follow-up PR will be smaller because this one establishes the contract.
## Architectural note (per code-reviewer SOLID/DDD checklist)
- **SRP** : factory does ONE thing (open connection + AutoMigrate)
- **OCP** : extends the package without modifying existing callers
- **DDD modular** : the test infra concern (per-scenario isolation) stays at the boundary; production code is untouched
- **Cognitive load** : 1 file changed, ~70 lines added, 1 dedicated test file (~110 lines)
## Test plan
- [x] `go build ./pkg/user/...` PASS
- [x] `go test ./pkg/...` PASS (all packages, no regression)
- [x] New integration test PASS with Postgres up
- [ ] CI passes
First building block for parallel-safe BDD scenario isolation (T12 plan,
ADR-0025 follow-up). PR #28 had to revert BDD_SCHEMA_ISOLATION because
SetupScenarioSchema created an empty schema without migrations -- the
production server's repo never saw it. This PR adds the missing piece:
a factory that opens a *PostgresRepository connected via an arbitrary
DSN AND runs AutoMigrate against it, so a per-scenario schema actually
gets the users table.
Public API additions in pkg/user/postgres_repository.go:
- NewPostgresRepositoryFromDSN(cfg, dsn) (*PostgresRepository, error)
Opens the repo from an explicit DSN (overrides cfg's host/port/etc),
runs AutoMigrate -- creates tables in whatever schema the DSN's
search_path points to.
- BuildSchemaIsolatedDSN(cfg, schemaName) string
Builds a DSN with `search_path=<schemaName>` from a base config.
The existing NewPostgresRepository(cfg) is unchanged. Existing Close()
method is reused.
Integration test in postgres_repository_isolated_test.go proves:
- AutoMigrate creates `users` table in the per-scenario schema (not public)
- A CreateUser through the isolated repo writes into the per-scenario schema
- public.users sees ZERO rows for the test username
- The per-scenario schema users table sees exactly 1 row
Test skips gracefully when DLC_DATABASE_HOST is not set.
Out of scope (T12 stage 2/2 next):
- Wiring this factory into pkg/bdd/testserver/SetupScenarioSchema
- Spawning a fresh server.Server per scenario (requires NewServerWithUserRepo)
- Removing -p 1 from scripts/run-bdd-tests.sh after parallel safety is achieved
Per code-reviewer skill SOLID/DDD section :
- SRP : factory has single responsibility (open + migrate, no business logic)
- OCP : the new factory extends the package without changing existing callers
- Cognitive load : 1 file, 50 lines added, 1 dedicated test file
🤖 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
First half of T12 (parallel-safe BDD scenario isolation, ADR-0025 follow-up).
PR #28 had to revert
BDD_SCHEMA_ISOLATIONbecause the existingSetupScenarioSchemacreated empty per-scenario schemas — no migrations were run on them. This PR adds the missing factory that opens a*PostgresRepositoryfrom an arbitrary DSN AND runs AutoMigrate on that connection.What's new
NewPostgresRepositoryFromDSN(cfg, dsn) (*PostgresRepository, error)Opens a Postgres repo via an explicit DSN, runs AutoMigrate. The DSN can include
search_path=<schema>so the migrations create tables in a per-scenario schema instead of public.BuildSchemaIsolatedDSN(cfg, schemaName) stringBuilds a DSN with
search_path=<schema>from a base config object.Existing
Close()reusedFor per-scenario teardown.
Verification
New integration test
TestNewPostgresRepositoryFromDSN_SchemaIsolationverifies:userstable inside the per-scenario schemaCreateUserthrough the isolated repo writes ONLY into the per-scenario schemapublic.userssees ZERO rows for the test user (proves no leak)Skips gracefully when
DLC_DATABASE_HOSTis not set (CI safe).Out of scope (T12 stage 2/2)
pkg/bdd/testserver/SetupScenarioSchema*server.Serverper scenario (requires aNewServerWithUserRepofactory)-p 1fromscripts/run-bdd-tests.shafter parallel safety is achievedThat follow-up PR will be smaller because this one establishes the contract.
Architectural note (per code-reviewer SOLID/DDD checklist)
Test plan
go build ./pkg/user/...PASSgo test ./pkg/...PASS (all packages, no regression)