"""Tests for app.tcp_proxy.encoder — binary payload builders.""" import struct from app.tcp_proxy.encoder import ( build_contact, build_contact_from_dict, build_device_info, build_self_info, build_self_info_from_runtime, ) from app.tcp_proxy.protocol import ( PROXY_FW_VER, PROXY_MAX_CHANNELS, PROXY_MAX_CONTACTS_RAW, PUSH_NEW_ADVERT, RESP_CONTACT, RESP_DEVICE_INFO, RESP_SELF_INFO, ) EXAMPLE_KEY = "ab" * 32 # 64-char hex → 32 bytes # ── build_contact ──────────────────────────────────────────────────── class TestBuildContact: def test_basic_structure(self): payload = build_contact(EXAMPLE_KEY, name="Alice") assert payload[0] == RESP_CONTACT # public key at bytes 1-32 assert payload[1:33] == bytes.fromhex(EXAMPLE_KEY) # total length: 1 + 32 + 1(type) + 1(flags) + 1(path) + 64(path) + 32(name) + 4(adv) + 4(lat) + 4(lon) + 4(lastmod) = 148 assert len(payload) == 148 def test_push_variant(self): payload = build_contact(EXAMPLE_KEY, push=True) assert payload[0] == PUSH_NEW_ADVERT assert len(payload) == 148 def test_favorite_flag(self): payload = build_contact(EXAMPLE_KEY, favorite=True) flags_byte = payload[34] # byte 1+32+1 = 34 assert flags_byte & 0x01 == 1 def test_not_favorite(self): payload = build_contact(EXAMPLE_KEY, favorite=False) flags_byte = payload[34] assert flags_byte & 0x01 == 0 def test_flood_path(self): payload = build_contact(EXAMPLE_KEY) path_byte = payload[35] # byte 1+32+1+1 = 35 assert path_byte == 0xFF def test_direct_path(self): payload = build_contact( EXAMPLE_KEY, direct_path="aabb", direct_path_len=2, direct_path_hash_mode=1, ) path_byte = payload[35] # mode=1 → 0x40, hops=2 → 0x02 → packed = 0x42 assert path_byte == 0x42 def test_name_truncated(self): long_name = "A" * 50 payload = build_contact(EXAMPLE_KEY, name=long_name) # name field is 32 bytes at offset 100 (1+32+1+1+1+64) name_bytes = payload[100:132] assert name_bytes == b"A" * 32 def test_lat_lon_encoding(self): payload = build_contact(EXAMPLE_KEY, lat=45.123456, lon=-122.654321) lat_offset = 136 # 1+32+1+1+1+64+32+4 = 136 lat = struct.unpack_from("= 58 def test_name_appended(self): payload = build_self_info(name="TestNode") # name starts at offset 58 name_bytes = payload[58:] assert name_bytes == b"TestNode" def test_public_key_encoded(self): payload = build_self_info(public_key=EXAMPLE_KEY) assert payload[4:36] == bytes.fromhex(EXAMPLE_KEY) def test_radio_params(self): payload = build_self_info(radio_freq=868.0, radio_bw=125.0, radio_sf=12, radio_cr=8) freq = struct.unpack_from("