tests/unit/test_bencode_udp.py (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
"""Bencode and UDP packet unit tests."""
from __future__ import annotations
import struct
import pytest
from app.probes.bencode import BencodeError, bdecode, validate_peers_then_discard
from app.probes.udp import (
create_announce_request,
create_connect_request,
parse_announce_response,
parse_connect_response,
)
def test_bencode_valid_and_invalid():
# d8:intervali1800e5:peers6:\x01\x02\x03\x04\x00Pe
peers = b"\x01\x02\x03\x04\x00\x50"
payload = b"d8:intervali1800e5:peers6:" + peers + b"e"
decoded = bdecode(payload)
assert decoded["interval"] == 1800
assert isinstance(decoded["peers"], list)
assert validate_peers_then_discard(decoded) is True
assert decoded["peers"] is None
with pytest.raises(BencodeError):
bdecode(b"")
with pytest.raises(BencodeError):
bdecode(b"i123") # incomplete
with pytest.raises(BencodeError):
bdecode(b"4:spam") # not a dict root for tracker helper — actually bdecode requires dict
# 4:spam is a string root
with pytest.raises(BencodeError):
bdecode(b"l4:spame")
def test_udp_connect_and_announce_roundtrip_fields():
req, tid = create_connect_request()
assert len(req) == 16
# forge response
conn_id = 0x1122334455667788
resp = struct.pack("!iiq", 0, tid, conn_id)
assert parse_connect_response(resp, tid) == conn_id
info = b"a" * 20
peer = b"b" * 20
ann, atid = create_announce_request(conn_id, info, peer, num_want=0)
assert len(ann) == 98
# announce response: action, tid, interval, leechers, seeders + fake peers
aresp = struct.pack("!iiiii", 1, atid, 1800, 2, 5) + b"\x00" * 6
interval, leechers, seeders = parse_announce_response(aresp, atid)
assert interval == 1800
assert leechers == 2
assert seeders == 5
def test_udp_transaction_mismatch():
resp = struct.pack("!iiq", 0, 123, 99)
with pytest.raises(RuntimeError, match="transaction"):
parse_connect_response(resp, 456)
aresp = struct.pack("!iiiii", 1, 1, 10, 0, 0)
with pytest.raises(RuntimeError, match="transaction"):
parse_announce_response(aresp, 2)
|