diff --git a/ChangeLog.md b/ChangeLog.md index d1538ca..5a519ef 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Fix _send_packet()/_send_ack(): replace misleading `pass` with explicit comment explaining PacketTrack polling contract; restructure condition to `if not (scheduler and scheduler.is_alive()):` [`2842851`](https://github.com/craigerl/aprsd/commit/2842851) +- Fix RejectPacket.__post__init__ typo — rename to __post_init__ so the dataclass lifecycle hook is actually called [`f75ed8a`](https://github.com/craigerl/aprsd/commit/f75ed8a) + - Add reset() to @singleton decorator; update tests to use ClassName.reset() instead of ClassName.instance = None [`ef19aaa`](https://github.com/craigerl/aprsd/commit/ef19aaa) - Fix PacketTrack.keys/items/values — return list snapshots instead of live dict views outside the lock [`088436e`](https://github.com/craigerl/aprsd/commit/088436e) diff --git a/aprsd/packets/core.py b/aprsd/packets/core.py index f7cda4c..19e2a9c 100644 --- a/aprsd/packets/core.py +++ b/aprsd/packets/core.py @@ -229,7 +229,7 @@ class RejectPacket(Packet): _type: str = field(default='RejectPacket', hash=False) response: Optional[str] = field(default=None) - def __post__init__(self): + def __post_init__(self): if self.response: LOG.warning('Response set!') diff --git a/tests/packets/test_packet.py b/tests/packets/test_packet.py index cf7df68..c188154 100644 --- a/tests/packets/test_packet.py +++ b/tests/packets/test_packet.py @@ -125,3 +125,37 @@ class TestFactory(unittest.TestCase): allowlist, f'{cls.__name__} is in TYPE_LOOKUP but missing from the allowlist', ) + + +class TestRejectPacket(unittest.TestCase): + """Tests for RejectPacket dataclass lifecycle.""" + + def test_post_init_called_with_response(self): + """RejectPacket.__post_init__ must be called by the dataclass machinery. + + The method was previously named __post__init__ (double underscores) which + is not a recognised dataclass lifecycle hook, so it was silently ignored. + """ + import logging + + with self.assertLogs(level=logging.WARNING) as cm: + packets.RejectPacket( + from_call=fake.FAKE_FROM_CALLSIGN, + to_call=fake.FAKE_TO_CALLSIGN, + response='REJ', + ) + # The warning should have been emitted via __post_init__ + self.assertTrue( + any('Response set!' in msg for msg in cm.output), + f'Expected "Response set!" warning; got: {cm.output}', + ) + + def test_post_init_called_without_response(self): + """RejectPacket.__post_init__ must not emit a warning when response is None.""" + import logging + + with self.assertNoLogs(level=logging.WARNING): + packets.RejectPacket( + from_call=fake.FAKE_FROM_CALLSIGN, + to_call=fake.FAKE_TO_CALLSIGN, + )