#!/bin/bash # Unit tests for mdns/hap-bridge.py [#619] # Verifies record capture normalization + packet encode/decode round-trip. set -euo pipefail PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" SCRIPT="$PROJECT_ROOT/mdns/hap-bridge.py" TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT cat > "$TMP/test_bridge.py" <<'PY' import importlib.util import struct import sys spec = importlib.util.spec_from_file_location("hap_bridge", sys.argv[1]) hb = importlib.util.module_from_spec(spec) spec.loader.exec_module(hb) failed = 0 def check(name, cond): global failed print(("PASS " if cond else "FAIL ") + name) if not cond: failed += 1 # --- encode_name / read_name round trip enc = hb.encode_name("Main Floor._hap._tcp.local") back, off = hb.read_name(enc, 0) check("name round-trip", back == "Main Floor._hap._tcp.local" and off == len(enc)) # --- encode_record + build_packet round trip for PTR/SRV/TXT recs = [ {"name": "_hap._tcp.local", "type": 12, "ttl": 10, "ptr_target": "Main Floor._hap._tcp.local"}, {"name": "Main Floor._hap._tcp.local", "type": 33, "ttl": 10, "srv_prio": 0, "srv_weight": 0, "srv_port": 58528, "srv_target": "Main-Floor.local"}, {"name": "Main Floor._hap._tcp.local", "type": 16, "ttl": 10, "rdata_hex": "0268310568656c6c6f"}, {"name": "Main-Floor.local", "type": 1, "ttl": 10, "rdata_hex": "c0a801d4"}, ] packet = hb.build_packet(recs) check("packet header ancount", struct.unpack(">H", packet[6:8])[0] == 4) # parse the packet back qd = struct.unpack(">H", packet[4:6])[0] an = struct.unpack(">H", packet[6:8])[0] off = 12 parsed = [] for _ in range(qd): _, off = hb.read_name(packet, off) off += 4 for _ in range(an): name, name_end = hb.read_name(packet, off) rtype, _rc, ttl, rlen = struct.unpack(">HHIH", packet[name_end:name_end + 10]) rdata_start = name_end + 10 rec = {"name": name, "type": rtype, "ttl": ttl} if rtype == 12: rec["ptr_target"] = hb.read_name(packet, rdata_start)[0] elif rtype == 33: pri, w, port = struct.unpack(">HHH", packet[rdata_start:rdata_start + 6]) rec["srv_target"] = hb.read_name(packet, rdata_start + 6)[0] rec["srv_port"] = port else: rec["rdata_hex"] = packet[rdata_start:rdata_start + rlen].hex() parsed.append(rec) off = rdata_start + rlen check("ptr round-trip", parsed[0]["ptr_target"] == "Main Floor._hap._tcp.local") check("srv target round-trip", parsed[1]["srv_target"] == "Main-Floor.local") check("srv port round-trip", parsed[1]["srv_port"] == 58528) check("txt round-trip", parsed[2]["rdata_hex"] == "0268310568656c6c6f") check("a round-trip", parsed[3]["rdata_hex"] == "c0a801d4") check("consumed whole packet", off == len(packet)) sys.exit(1 if failed else 0) PY if timeout 60 docker run --rm -v "$SCRIPT:/b.py" -v "$TMP/test_bridge.py:/t.py" python:3.12-alpine python /t.py /b.py; then echo "✅ hap-bridge packet round-trip tests passed" else echo "❌ hap-bridge tests failed" exit 1 fi