"""rawcli Phase 1 — session state, breadcrumb, trace rendering, CLI dispatch. Hardware-free.""" import re from pm3py.cli.main import build_parser, main from pm3py.cli.rawcli.session import RawSession from pm3py.cli.rawcli.trace_view import render_exchange _ANSI = re.compile(r"\x1b\[[0-9;]*m") def _plain(s: str) -> str: return _ANSI.sub("", s) class TestBreadcrumb: def test_default(self): assert RawSession().breadcrumb() == "[raw / 0x]" def test_toggle_to_binary(self): s = RawSession() assert s.toggle_entry_mode() == "bin" assert s.entry_prefix == "0b" assert s.breadcrumb() == "[raw / 0b]" assert s.toggle_entry_mode() == "hex" assert s.breadcrumb() == "[raw / 0x]" def test_identified_fills_segments(self): s = RawSession() s.field = "hf" s.transponder = "NTAG215" assert s.breadcrumb() == "[raw / hf / NTAG215 / 0x]" def test_field_only(self): s = RawSession() s.field = "lf" assert s.breadcrumb() == "[raw / lf / 0x]" class TestTraceView: def test_command_and_response_lines(self): out = _plain(render_exchange(bytes([0x30, 0x04]), bytes([0x00, 0x01, 0x02, 0x03]), protocol="hf14a", is_tty=False)) assert "Reader → Tag" in out assert "Tag → Reader" in out assert "30 04" in out # command bytes shown def test_command_only_when_no_response(self): out = _plain(render_exchange(bytes([0x26]), None, protocol="hf14a", is_tty=False)) assert "Reader → Tag" in out assert "Tag → Reader" not in out def test_annotation_present_for_known_command(self): # 0x30 = 14a READ; the decoder should annotate it (exact text may vary) out = _plain(render_exchange(bytes([0x30, 0x04]), None, protocol="hf14a", is_tty=False)) assert "READ" in out.upper() class TestCliDispatch: def test_rawcli_subcommand_parsed(self): args = build_parser().parse_args(["rawcli", "--port", "/dev/ttyACM0"]) assert args.command == "rawcli" and args.port == "/dev/ttyACM0" def test_no_command_prints_help(self, capsys): rc = main([]) assert rc == 1 assert "rawcli" in capsys.readouterr().out