Files
rdm-card-reader/tests/test_card.py
T
ske087 576f22268f RDM card reader: print card events, select the right port, serve taps to a web app
Card reading (rdm6300 + pyserial) printing card inserted/removed with the
full card data, plus a --serve mode that publishes taps over HTTP so a web
page can authenticate a tap.

The bridge reports card ids only - mapping a card to a user stays on the
server. The tap is latched for --tap-ttl seconds, including after the card is
lifted, and is single use, so a page reload cannot replay one tap into two
logins.

Port selection identifies the reader by serial number or by-id path instead of
/dev/ttyUSBn, and refuses to guess when a selector matches several adapters.

deploy/ has the systemd unit, a udev rule for a stable /dev/rdm-reader, and
install.sh. Covered by unit tests plus PTY end-to-end tests.
2026-10-01 15:31:04 +03:00

112 lines
2.7 KiB
Python

"""The event model and its output formatting."""
import json
import pytest
from rdm_card_reader.card import (
CONFIG_CARD_VALUE,
CardEvent,
format_event,
)
SAMPLE = CardEvent(
event="card_inserted",
timestamp="2026-10-01 12:00:00",
value=0x00C4A0B5,
checksum=0xDE,
card_type=0x0F,
is_valid=True,
port="/dev/ttyS0",
)
def test_hex_value_is_zero_padded_to_eight_digits():
assert SAMPLE.hex_value == "0x00C4A0B5"
def test_from_card_accepts_any_object_with_the_right_fields():
class FakeCard:
value = 1234
checksum = 5
type = 15
is_valid = True
event = CardEvent.from_card("card_inserted", FakeCard(), "/dev/ttyUSB0")
assert event.value == 1234
assert event.hex_value == "0x000004D2"
assert event.port == "/dev/ttyUSB0"
def test_to_dict_has_the_documented_keys():
assert set(SAMPLE.to_dict()) == {
"event", "timestamp", "id", "hex", "type", "checksum", "is_valid", "port",
}
def test_to_json_is_parseable():
assert json.loads(SAMPLE.to_json())["hex"] == "0x00C4A0B5"
def test_inserted_block_lists_the_card_data():
text = format_event(SAMPLE)
assert "CARD INSERTED" in text
assert "12886197" in text
assert "0x00C4A0B5" in text
assert "0x0F" in text
assert "222" in text
assert "True" in text
def test_config_card_is_flagged_in_the_note():
event = CardEvent(
event="card_inserted",
timestamp="2026-10-01 12:00:00",
value=CONFIG_CARD_VALUE,
checksum=0,
card_type=0x0F,
is_valid=True,
port="/dev/ttyS0",
)
text = format_event(event)
assert "config card" in text
assert str(CONFIG_CARD_VALUE) in text
def test_ordinary_card_has_no_config_note():
assert "config card" not in format_event(SAMPLE)
def test_removed_line_is_single_line():
event = CardEvent(
event="card_removed",
timestamp="2026-10-01 12:00:00",
value=0x00C4A0B5,
checksum=0xDE,
card_type=0x0F,
is_valid=True,
port="/dev/ttyS0",
)
text = format_event(event)
assert text.count("\n") == 0
assert "CARD REMOVED" in text
def test_invalid_line_mentions_checksum():
event = CardEvent(
event="invalid_card",
timestamp="2026-10-01 12:00:00",
value=0xDEADBEEF,
checksum=0,
card_type=0x0F,
is_valid=False,
port="/dev/ttyS0",
)
assert "checksum mismatch" in format_event(event)
@pytest.mark.parametrize("value,expected", [(0, "0x00000000"), (0xFFFFFFFF, "0xFFFFFFFF")])
def test_hex_value_boundaries(value, expected):
event = CardEvent("card_inserted", "-", value, 0, 0x0F, True, "p")
assert event.hex_value == expected