66# test-mini-stack`.
77#
88# Scenarios:
9- # 1. boot syncing → dashboard HOLDS itest-p2pool + itest-xmrig-proxy (#35)
10- # 2. both chains synced → dashboard RELEASES them
11- # 3. monerod down → dashboard REJECTS workers (stops itest-xmrig-proxy) (#31)
12- # 4. monerod back → dashboard READMITS workers
13- # +. healthchecks (#79) → REAL loop fires a liveness heartbeat to the fake-hc receiver
14- # (pure dead-man's switch; node-health alerting is Telegram #121)
9+ # 1. boot syncing → dashboard HOLDS itest-p2pool + itest-xmrig-proxy (#35)
10+ # 2. monerod synced, Tari not → still HELD (the gate needs both required chains)
11+ # 3. both chains synced → dashboard RELEASES them
12+ # +. healthchecks (#79) → REAL loop fires a liveness heartbeat to the fake-hc receiver
13+ # (pure dead-man's switch; node-health alerting is Telegram #121)
14+ # 4. Tari down (required) → dashboard REJECTS workers (stops itest-xmrig-proxy) (#31)
15+ # 5. Tari back → dashboard READMITS workers
16+ # 6. monerod down → dashboard REJECTS workers (#31/#564)
17+ # 7. monerod back → dashboard READMITS workers (#564)
18+ # 8. monerod busy/mid-reorg → dashboard REJECTS, then READMITS on recovery
19+ # 9. monerod + Tari both down → REJECTS; recovering only one does NOT readmit; both does
20+ # 10. dashboard restart → the one-way sync latch survives (#35 persistence)
21+ # 11. Tari OPTIONAL, Tari down → dashboard keeps mining, workers stay accepted (#562)
1522#
1623set -uo pipefail
1724
@@ -191,10 +198,6 @@ log "scenario 3b: the dashboard fires a real healthchecks heartbeat"
191198wait_hc " healthchecks: real loop fired a liveness heartbeat" ' ^/ph$' 40
192199
193200# 4. Tari down while required → reject workers (stop the proxy); itest-p2pool keeps running. (#31)
194- # NOTE: monerod-down failover is deliberately NOT simulated here — the dashboard's monerod
195- # down-path falls back to log-scraping a real `monerod` container, which this fake stack has
196- # no equivalent of. That path is covered on real hardware by the tier-4 --fault-injection
197- # phase. Tari has no such fallback, so its reject/readmit exercises the failover cleanly.
198201log " scenario 4: rejects workers when required Tari is down"
199202set_tari down
200203assert_state " rejected on Tari outage: itest-xmrig-proxy stopped" itest-xmrig-proxy exited 90
@@ -209,9 +212,46 @@ log "scenario 5: readmits workers when Tari recovers"
209212set_tari synced
210213assert_state " readmitted after Tari recovery: itest-xmrig-proxy running" itest-xmrig-proxy running 90
211214
212- # 6. Dashboard restart after release → the one-way latch is persisted, so the miner is NOT
213- # re-held: both containers stay running across the restart. (#35 persistence)
214- log " scenario 6: a dashboard restart does not re-hold a released miner"
215+ # 6. monerod down → reject workers (stop the proxy); itest-p2pool keeps running. (#31/#564)
216+ # Needs LOCAL_MONERO_HOST == MONERO_NODE_HOST in the compose env — otherwise the dashboard
217+ # treats monerod as "remote" and never probes it for reachability at all (see that env var's
218+ # comment in docker-compose.fake.yml).
219+ log " scenario 6: rejects workers when monerod is down"
220+ set_monerod down
221+ assert_state " rejected on monerod outage: itest-xmrig-proxy stopped" itest-xmrig-proxy exited 90
222+ if [ " $( cstate itest-p2pool) " = " running" ]; then
223+ c_ok " monerod-outage rejection leaves itest-p2pool running (only the proxy fails over)"
224+ else
225+ c_bad " monerod-outage rejection leaves itest-p2pool running" " itest-p2pool is '$( cstate itest-p2pool) '"
226+ fi
227+
228+ # 7. monerod recovers → readmit (after the recovery-hysteresis window). (#564)
229+ log " scenario 7: readmits workers when monerod recovers"
230+ set_monerod synced
231+ assert_state " readmitted after monerod recovery: itest-xmrig-proxy running" itest-xmrig-proxy running 90
232+
233+ # 8. monerod busy (HTTP 200 but status != OK, e.g. mid-reorg) — the client must distrust the
234+ # heights and treat the node as unreachable, not synced, the same as a clean outage.
235+ log " scenario 8: rejects workers when monerod reports busy/mid-reorg"
236+ set_monerod busy
237+ assert_state " rejected on monerod busy: itest-xmrig-proxy stopped" itest-xmrig-proxy exited 90
238+ set_monerod synced
239+ assert_state " readmitted after monerod busy clears: itest-xmrig-proxy running" itest-xmrig-proxy running 90
240+
241+ # 9. Double outage — monerod AND Tari both down → rejected; recovering only Tari must NOT
242+ # readmit (monerod is still down); recovering monerod too readmits both.
243+ log " scenario 9: double outage — readmits only once BOTH nodes recover"
244+ set_monerod down
245+ set_tari down
246+ assert_state " rejected on double outage: itest-xmrig-proxy stopped" itest-xmrig-proxy exited 90
247+ set_tari synced
248+ assert_stays " still rejected with only Tari recovered" itest-xmrig-proxy exited 8
249+ set_monerod synced
250+ assert_state " readmitted once both nodes recovered: itest-xmrig-proxy running" itest-xmrig-proxy running 90
251+
252+ # 10. Dashboard restart after release → the one-way latch is persisted, so the miner is NOT
253+ # re-held: both containers stay running across the restart. (#35 persistence)
254+ log " scenario 10: a dashboard restart does not re-hold a released miner"
215255compose restart dashboard > /dev/null 2>&1
216256for _ in $( seq 1 30) ; do
217257 compose exec -T dashboard python3 -c \
@@ -221,6 +261,30 @@ done
221261assert_stays " itest-p2pool stays up across restart" itest-p2pool running 6
222262assert_stays " itest-xmrig-proxy stays up across restart" itest-xmrig-proxy running 6
223263
264+ # 11. Tari OPTIONAL (dashboard.tari_required=false) → a Tari outage must NOT reject workers;
265+ # Monero mining keeps going. (#562 — the silent-yield-loss path: unit-tested at
266+ # test_data_service.py::test_tari_down_ignored_when_non_blocking; this drives the same
267+ # decision through real containers.) TARI_REQUIRED is baked into the dashboard container at
268+ # boot, so this needs its own compose cycle rather than a live toggle.
269+ log " scenario 11: Tari-optional — a Tari outage does not reject workers (#562)"
270+ compose down -v --remove-orphans > /dev/null 2>&1 || true
271+ TARI_REQUIRED=false compose up -d > /dev/null 2>&1
272+ api_up=0
273+ for _ in $( seq 1 30) ; do
274+ if compose exec -T dashboard python3 -c \
275+ " import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/state', timeout=3)" > /dev/null 2>&1 ; then
276+ api_up=1
277+ break
278+ fi
279+ sleep 2
280+ done
281+ [ " $api_up " = 1 ] && c_ok " Tari-optional stack: dashboard API is up" || c_bad " Tari-optional stack: dashboard API is up" " no /api/state after ~60s"
282+ # Tari is non-blocking, so monerod alone gates the sync-hold; release it to reach steady state.
283+ set_monerod synced
284+ assert_state " Tari-optional: released itest-xmrig-proxy running" itest-xmrig-proxy running 90
285+ set_tari down
286+ assert_stays " Tari-optional: itest-xmrig-proxy keeps mining through a Tari outage" itest-xmrig-proxy running 8
287+
224288echo " "
225289log " mini-stack: $PASS passed, $FAIL failed"
226290[ " $FAIL " -eq 0 ]
0 commit comments