14746f2995
The full-suite run surfaced two load-dependent failures that were green in isolation. Root cause: SeedDefaultClusterAsync no-opped unless OTOPCUA_HARNESS_USE_SQL=1, on the reasoning that "the in-memory provider ignores FK constraints, so deploy E2E tests pass without it". Phase 1 invalidated that. With no ClusterNode rows the coordinator's expected-ack set is empty, so it seals IMMEDIATELY. "Wait for Sealed" therefore stopped implying "every node has applied" — and because DriverHostActor.UpsertNodeDeploymentState writes each node's row on its own schedule, every test counting those rows after a seal became a race. Reproducibly green alone, red under suite load, and it surfaced in a different test each run, which is what made it look like flakiness rather than a contract change. Seeding in both modes restores the invariant those tests were written against and is closer to production either way: a real fleet always has these rows, because the FK requires them. Deployment_started_with_node_b_down_seals_with_one_node_state asserted the behaviour Phase 1 deliberately removed — its own comment documented membership snapshotting, i.e. sealing green while a configured node never received the deployment. Replaced by two tests covering the new contract: a stopped node is still expected (both state rows exist, B still Applying, deployment does not seal), and MaintenanceMode is what makes it seal with one. The new "does not seal" test asserts both rows exist rather than only the absence of a seal, so it cannot pass against a coordinator that simply died. Host.IntegrationTests 195/201, sole remaining failure AbCip_Green_AgainstSim — verified failing on master in a clean worktree, so pre-existing fixture baseline, not a regression. Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
ZB.MOM.WW.OtOpcUa.Host.IntegrationTests
Two-node Akka cluster integration tests on top of TwoNodeClusterHarness.
Default mode (no infra required)
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests
Uses Microsoft.EntityFrameworkCore.InMemory for ConfigDb and a stub ILdapAuthService that
accepts any username when the password is valid-password. Each harness instance creates a
unique in-memory database scoped to its lifetime. This is the mode CI runs by default.
Real-infra mode (SQL Server + OpenLDAP)
When you need to exercise EF behaviors that diverge between providers (index uniqueness,
RowVersion concurrency, JSON columns, migration application) or a real LDAP bind, bring up
the bundled compose stack and set the env-var switches:
docker compose -f tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/docker-compose.yml up -d
export OTOPCUA_HARNESS_USE_SQL=1
export OTOPCUA_HARNESS_USE_LDAP=1
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests
docker compose -f tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/docker-compose.yml down -v
SQL Server mode (OTOPCUA_HARNESS_USE_SQL=1)
- Container:
mcr.microsoft.com/mssql/server:2022-latestonlocalhost:14331 - Each
TwoNodeClusterHarness.StartAsync()creates a unique databaseOtOpcUa_Harness_{guid}viaDatabase.EnsureCreatedAsync()and drops it onDisposeAsync()(best-effort). - Port
14331chosen to avoid colliding with thedocker-dev/fleet (which uses14330).
LDAP mode (OTOPCUA_HARNESS_USE_LDAP=1)
- Container:
bitnami/openldap:2.6onlocalhost:3894 - Users
alice/alice123andbob/bob123, all underou=FleetAdmin. - Port
3894chosen to avoid colliding with thedocker-dev/fleet (which uses3893).
Local-dev caveat
This dev VM (DESKTOP-6JL3KKO) does not run Docker locally. Real-infra mode runs on the
shared Linux Docker host (10.100.0.35) per docs/v2/dev-environment.md, or in CI on Linux.