OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / daemon / test_daemon_public_dht.py

149 lines · python ref:
#!/usr/bin/env python3
"""The daemon reaches the PUBLIC DHT with no --bootstrap, and two peers mesh.

    python3 -B test_daemon_public_dht.py

⛔ THIS IS THE GATE THAT WAS MISSING, AND ITS ABSENCE COST A LIVE SESSION.
Every other test of this stack — test_session_channel.cpp (8/8),
test_three_peer_swarm, test_daemon_e2e.py — supplies an explicit `--bootstrap`
pointing at a local testnet, because that is what makes a test fast and
hermetic. So all of them passed while the one configuration the ADDON actually
uses, `--bootstrap` omitted, was dead: an empty list left _bootstrap_count at 0,
bootstrap() sent zero FIND_NODEs, and on_bootstrapped never fired. Two Blenders
on two machines joined the same topic, both reported success, and neither had
ever sent a packet.

A hermetic suite cannot catch that, because the bug IS "the non-hermetic path
was never exercised". Hence this file, and hence it talks to the real network.

⚠ REQUIRES INTERNET. A failure here means the daemon is unreachable OR this
machine is — check `dht=` in the captured output before believing the verdict.
"""

import os
import re
import subprocess
import sys
import tempfile
import time

HERE = os.path.dirname(os.path.realpath(__file__))
DAEMON = os.path.join(HERE, "session-daemon")

BOOTSTRAP_TIMEOUT = 45.0
MESH_TIMEOUT = 90.0

passed = failed = 0


def ck(name, cond, detail=""):
    global passed, failed
    if cond:
        passed += 1
        print("  PASS: %-52s %s" % (name, detail))
    else:
        failed += 1
        print("  FAIL: %-52s %s" % (name, detail))


class Daemon:
    """One session-daemon, started EXACTLY as blender_addon does."""

    def __init__(self, topic, tag):
        self.log_path = os.path.join(tempfile.gettempdir(),
                                     "dht-gate-%s-%d.log" % (tag, os.getpid()))
        self.sock = os.path.join(tempfile.gettempdir(),
                                 "dht-gate-%s-%d.sock" % (tag, os.getpid()))
        self._log = open(self.log_path, "wb")
        # ⛔ NO --bootstrap. That omission is the whole point of this gate; adding
        # one here would turn it back into the test that already passed.
        self.proc = subprocess.Popen(
            [DAEMON, "--topic", topic, "--socket", self.sock],
            stdout=self._log, stderr=subprocess.STDOUT)

    def output(self):
        try:
            with open(self.log_path, "r", errors="replace") as fh:
                return fh.read()
        except OSError:
            return ""

    def state(self):
        """(dht, peers) from the most recent status line, or (None, None)."""
        lines = re.findall(r"dht=(\w+) peers=(\d+)", self.output())
        if not lines:
            return None, None
        return lines[-1][0], int(lines[-1][1])

    def stop(self):
        if self.proc.poll() is None:
            self.proc.terminate()
            try:
                self.proc.wait(timeout=5)
            except subprocess.TimeoutExpired:
                self.proc.kill()
        self._log.close()


def wait_for(fn, timeout, step=1.0):
    deadline = time.time() + timeout
    while time.time() < deadline:
        if fn():
            return True
        time.sleep(step)
    return False


def main():
    print("=" * 78)
    print("session-daemon on the PUBLIC DHT (no --bootstrap)")
    print("=" * 78)

    if not os.path.isfile(DAEMON):
        print("SKIP: no session-daemon binary — run ./build.sh")
        return 0

    topic = "gate-%d-%d" % (os.getpid(), int(time.time()))
    a = Daemon(topic, "a")
    b = Daemon(topic, "b")
    try:
        # 1. It must actually enter the DHT. This is the assertion that was RED:
        #    before the fix it sat on dht=connecting for ever.
        up = wait_for(lambda: a.state()[0] == "up" and b.state()[0] == "up",
                      BOOTSTRAP_TIMEOUT)
        ck("both daemons bootstrapped the public DHT", up,
           "A=%s B=%s" % (a.state()[0], b.state()[0]))

        if not up:
            print("\n--- A ---\n%s\n--- B ---\n%s" % (a.output(), b.output()))
            return 1

        # 2. And find each other on the topic. Bootstrapping alone is not the
        #    claim — "joined" was already true when nothing worked.
        met = wait_for(lambda: a.state()[1] >= 1 and b.state()[1] >= 1,
                       MESH_TIMEOUT)
        ck("they found each other on the topic", met,
           "A sees %d, B sees %d" % (a.state()[1], b.state()[1]))

        # 3. Both directions. A channel that opens one way is a half-session,
        #    and the panel would show a peer that never receives a grab.
        ck("the channel is open on BOTH peers", a.state()[1] >= 1 and b.state()[1] >= 1,
           "one-sided means grabs travel one way only")

        # 4. The status line itself, because it is now the only way anyone can
        #    tell a live session from a dead one.
        ck("the daemon reports its DHT state", "dht=" in a.output(),
           "an unobservable daemon is why this took ten minutes to notice")

    finally:
        a.stop()
        b.stop()

    print()
    print("%d/%d passed" % (passed, passed + failed))
    return 0 if failed == 0 else 1


if __name__ == "__main__":
    sys.exit(main())