Orchestrata Ultimate › Plans › briefs › 2026-09-22-adr-0058-ha-sandbox-fixture

Brief: retarget the HA controller tests at the two-node orchgraph sandbox

Source: plans/briefs/2026-09-22-adr-0058-ha-sandbox-fixture.md at 8a2cb42ad · last changed 2026-09-22 · first committed 2026-09-22 · 135 lines.

Prose pending.

Plan text

Brief: retarget the HA controller tests at the two-node orchgraph sandbox

DESIGN_WAIVER: ADR-0058 (Accepted 2026-09-22). Scope is exactly the ADR's execution.

GOAL

tests/semantic_graph/test_memgraph_ha.py never touches the shared test orchgraph (10.53.2.10) again. Its register / demote / failover tests run against the two real sandbox nodes and assert real outcomes. Every test in the module passes, in any order, twice in a row, and neither sandbox node logs an assertion.

FACTS (measured 2026-09-22, do not re-derive)

FILES

DESIGN (settled; implement as written)

  1. Delete the module-local memgraph_config fixture.
  2. Add a function-scoped ha_sandbox fixture:
    • reads TEST_MEMGRAPH_HA_MAIN_URI / TEST_MEMGRAPH_HA_REPLICA_URI; blank or unset -> pytest.fail("ADR-0058: TEST_MEMGRAPH_HA_*_URI not set in the active env profile; the HA tests need the two-node sandbox"). Never skip.
    • builds main = NodeConfig(name="memgraph-db", bolt_uri=..., replication_host=<host>) and replica = NodeConfig(name="memgraph-replica", bolt_uri=..., replication_host=<host>). Keep the name memgraph-db so the single-node assertions stay valid.
    • a _reset(nodes) helper, run in setup AND teardown, that for each node: if role is main, DROP REPLICA every name from SHOW REPLICAS; then SET REPLICATION ROLE TO MAIN when not already main. Use orchestrata_ultimate.core.graph_factory.create_graph(uri, auth=NO_AUTH) with an auto-commit session (replication commands refuse explicit transactions). Teardown runs even when the test failed.
    • yields (main, replica).
  3. ha_controller fixture now depends on ha_sandbox and builds the controller with nodes=[main] (single node, sandbox MAIN). All existing single-node tests keep working unchanged.
  4. New ha_pair_controller fixture: same but nodes=[main, replica].
  5. Rewrite these tests on ha_pair_controller with REAL expectations:
    • test_reconcile_roles_when_main_is_healthy: mark both healthy, main role main, replica role unknown; _reconcile_roles(); assert persisted main is memgraph-db and _show_replicas(main) lists memgraph-replica with every db status ready (poll up to 10 s).
    • test_ensure_replicas_registered_attempts_registration -> rename to test_ensure_replicas_registered_registers_real_replica: same registration assertion; second call is a no-op (registration left alone: call _ensure_replicas_registered twice, still one entry).
    • test_demote_stray_mains_attempts_demotion -> ..._demotes_real_stray_main: both nodes MAIN and healthy, replica state role main; call _demote_stray_mains(main_state); assert _query_role(replica) == "replica" and it is registered on main.
    • test_perform_failover_falls_back_to_main_or_unknown_candidates: persisted main memgraph-db role main healthy, replica role unknown healthy; _perform_failover(); assert persisted main is memgraph-db (the main/unknown fallback picked it) and the replica ended registered.
    • test_perform_failover_promotes_real_node -> promotes the REAL replica: persisted main memgraph-db marked unhealthy, replica healthy role replica; _perform_failover(); assert persisted main is memgraph-replica, epoch bumped, _query_role(replica) == "main".
    • test_reconcile_roles_triggers_failover_when_main_unhealthy: same shape via _reconcile_roles().
    • test_perform_failover_attempts_promotion keeps its unreachable candidate (it never contacts the real node) — leave as is.
  6. Module docstring: state the two-node sandbox, ADR-0058, and the rule "the shared test node is never a replication target".
  7. TestNoteReplicaProgress is pure; untouched.

CONSTRAINTS

DONE WHEN

VERIFY (report these verbatim)