test(validation): ScadaBridge consumer matrix — failover suite, UNS subscribe, ConditionRefresh after reconnect (ScadaBridge#125)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
Probe 'failover <primary> <backup> <mode> <dir>' drives ScadaBridge's own DataConnectionActor (production primary/backup machine, real DataConnectionFactory, default DataConnectionOptions, FailoverRetryCount 3) against two servers; run.py loses the primary (SIGKILL or SIGSTOP) once the probe reports a steady primary, and the probe requires Bad quality on loss, "Connected to backup", the backup's own value, a fresh ConditionRefresh replaying the native condition, and a write via the backup. rig-failover.sh runs the same probe against the local docker-dev central pair. Matrix legs gain a UNS subscription case and a Server-object alarm subscription on the reconnected session whose ConditionRefresh must replay the active condition (11 cases). run.py gains --versions, --suites, --failover-modes and --failover-loss. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
@@ -41,7 +41,10 @@ Never commit an entire output directory: it contains the fixture's private PKI.
|
||||
|
||||
The consumed subset covers API-key activation, raw/UNS scalar and Int32[]
|
||||
roundtrips, durable hierarchical browse including the native alarm, data quality
|
||||
and timestamps, alarm filter/ConditionRefresh and disconnect/reconnect. History
|
||||
and timestamps on raw and UNS subscriptions, alarm filter/ConditionRefresh,
|
||||
disconnect/reconnect, and (ScadaBridge#125) a Server-object alarm subscription on
|
||||
the reconnected session whose ConditionRefresh must replay the active native
|
||||
condition (11 cases per trusted leg). History
|
||||
and Part 9 acknowledgment are not exposed by the inspected consumer interfaces;
|
||||
the parent Shared/CLI matrix covers those application services instead.
|
||||
|
||||
@@ -52,3 +55,44 @@ fixed in ScadaBridge `ff835447`/`36c64a6b`. Re-run 2026-11-12 with ScadaBridge
|
||||
ACL off; all trust negative controls failed closed. The runner retains the
|
||||
nonzero result and each actual failure; it does not loosen trust or change
|
||||
expected results to return success. No ScadaBridge production source or user PKI is modified.
|
||||
|
||||
## Redundancy failover (ScadaBridge#125)
|
||||
|
||||
`run.py` also runs a `failover` suite (`--suites matrix,failover`, both by
|
||||
default; `--versions candidate` runs one application version only). Each leg
|
||||
starts two independent servers and runs `Probe failover <primary> <backup>
|
||||
<mode> <dir>`, which drives ScadaBridge's own `DataConnectionActor` (the
|
||||
production primary/backup state machine, the real `DataConnectionFactory`,
|
||||
production `DataConnectionOptions` defaults, `FailoverRetryCount = 3`). Once the
|
||||
probe has a steady primary (Good values on a raw and a UNS tag, a write, the
|
||||
Server-object alarm subscription with its ConditionRefresh) it writes
|
||||
`primary-ready`; the runner then loses the primary and writes `primary-killed`.
|
||||
The probe must then see Bad quality pushed, "Connected to backup", Good values
|
||||
whose first value is the backup's own (proving the source), a fresh
|
||||
ConditionRefresh replaying the native condition, and a write through the backup.
|
||||
Two loss shapes run per security mode (`--failover-modes None,SignAndEncrypt`,
|
||||
`--failover-loss kill,freeze`):
|
||||
|
||||
- `kill` — SIGKILL; the OS closes the primary's sockets, so the client sees the loss at once.
|
||||
- `freeze` — SIGSTOP; the sockets stay open and silent (hung host / dead link), so only the
|
||||
consumer's session keep-alive can detect it.
|
||||
|
||||
ScadaBridge does not read `ServiceLevel`/`ServerRedundancy`: its failover is
|
||||
endpoint-level (primary/backup connection details), so two independent fixture
|
||||
servers exercise exactly what it consumes. `rig-failover.sh <Probe.dll> <dir>`
|
||||
runs the same probe against the real local docker-dev pair (central-1 `:4840`,
|
||||
central-2 `:4841`), `docker kill`s central-1 once the primary is steady and
|
||||
starts it again afterwards. The rig admits anonymous sessions read-only and has
|
||||
no active native alarm, so that variant checks values and ConditionRefresh
|
||||
completion only. Local docker-dev only; never point it at a wonder box.
|
||||
|
||||
Run 2026-11-12 (ScadaBridge#125) with ScadaBridge `94ae1860` (SDK client
|
||||
1.5.378.106), ACL off, against the SDK 1.5.378.182 baseline (`83ed880e`) and
|
||||
master `a28adba2` (SDK 2.0.1): matrix 11/11 in None, Sign and SignAndEncrypt on
|
||||
both, all 8 trust controls failed closed (0x80130000 / 0x801A0000); failover 8/8
|
||||
legs (11/11 cases each). Loss-to-backup timings are the same on both versions:
|
||||
`kill` about 20 s (three 5 s reconnect attempts, then the backup), `freeze` about
|
||||
20 s to detect and 86–130 s to reach the backup, because each reconnect attempt
|
||||
waits out a connect timeout against the silent primary. `rig-failover.sh` passed
|
||||
9/9 against the docker-dev pair (image SDK 2.0.1), central-1 killed, about 20 s
|
||||
to central-2.
|
||||
|
||||
Reference in New Issue
Block a user