Commit Graph
33 Commits
Author SHA1 Message Date
hemna f83154b6e9 fix: remove no-op self-assignment 'fromcall = fromcall' in USMetarPlugin (#272)
In the else-branch of USMetarPlugin.process(), 'fromcall = fromcall'
is a self-assignment that does nothing.  It was likely a leftover from
an edit that intended to reassign fromcall but forgot to write the RHS.
Remove the dead line.  The variable is still used correctly on the next
line (get_aprs_fi call).

Closes #253
2026-08-28 14:13:05 -04:00
hemna aa43f60728 fix: add reset() to @singleton decorator to allow test isolation (#262)
The @singleton decorator stored its instance in wrapper_singleton.instance
inside a closure. Tests could reset __new__-based singletons via
ClassName._instance = None, but @singleton classes had no equivalent reset
mechanism, causing state to leak between tests.

Add wrapper_singleton.reset() to every @singleton-decorated class. The
method clears wrapper_singleton.instance so the next call creates a fresh
instance, matching the __new__ singleton pattern.

Update all tests to call ClassName.reset() instead of manually setting
ClassName.instance = None (tests/client/test_client.py x5,
tests/client/test_registry.py x1).

Tests added (tests/utils/test_utils.py):
- test_singleton_has_reset: asserts reset is callable on the wrapper
- test_singleton_reset_clears_instance: verifies a new instance is created
  after reset(), not the cached one
- test_singleton_instance_is_none_before_first_call: verifies the instance
  lifecycle — None → populated → None after reset

Closes #240
2026-08-28 13:51:33 -04:00
hemna 28d0ac0e6b fix: initialise StatsStore.data = {} in __init__ (#260)
ObjectStoreMixin.save() calls len(self) -> len(self.data) immediately.
StatsStore never set self.data in __init__, so any call to save() before
add() raised AttributeError: 'StatsStore' object has no attribute 'data',
crashing APRSDStatsStoreThread on the first tick if the path where
enable_save is True is hit.

Tests updated/added (tests/threads/test_stats.py):
- test_init: now asserts data exists and equals {} (was asserting absence)
- test_save_before_add_does_not_raise: regression guard — save() must not
  raise AttributeError when called before add()

Closes #241
2026-08-28 13:45:43 -04:00
hemna ec1f221867 fix: PacketTrack.keys/items/values return list snapshots instead of live dict views (#261)
keys(), items(), values() each acquired self.lock then returned a live
dict view (dict_keys, dict_items, dict_values). The lock was released
on return, leaving callers with an unsynchronised view that races with
concurrent tx()/rx()/remove() calls on other threads.

Wrap each return in list() so a snapshot is taken while the lock is held.

Tests added (tests/packets/test_tracker.py):
- test_keys_returns_snapshot_not_view: asserts isinstance list and that
  a subsequent mutation does not appear in the returned value
- test_items_returns_snapshot_not_view: same for items()
- test_values_returns_snapshot_not_view: same for values()

Closes #242
2026-08-28 12:56:13 -04:00
hemna 16d497fd35 fix: validate _type against allowlist in factory() before globals() lookup (#259)
factory() was calling globals()[raw['_type']] on a value read from a
persisted JSON file on disk without validation, allowing an attacker who
can write to ~/.config/aprsd/ to reference arbitrary names in the module
global namespace.

Add an allowlist (_known_packet_type_names) derived lazily from
TYPE_LOOKUP. Any _type value not in the set raises ValueError before
globals() is ever called.

Tests added (tests/packets/test_packet.py):
- test_factory_known_type_roundtrip: valid known _type still deserialises
- test_factory_unknown_type_raises: module-global name ('os') is rejected
- test_factory_arbitrary_string_raises: arbitrary strings are rejected
- test_factory_empty_type_raises: empty string is rejected
- test_factory_allowlist_covers_all_type_lookup_classes: allowlist stays
  in sync with TYPE_LOOKUP automatically

Closes #239
2026-08-28 12:42:34 -04:00
hemna c975dd85d8 fix: WatchList.stats() — remove early return that bypassed lock (#258)
stats() had a bare 'return self.data' on the first line that:
- returned the raw internal dict without holding self.lock (race condition)
- made the locked loop below unreachable dead code
- returned the wrong shape (callers expect age/old/packet/last keys, not
  the raw was_old_before_update internal key)

Remove the early return so the existing with self.lock: loop executes.

Tests added (tests/packets/test_watch_list.py):
- test_stats_empty: empty watch list returns {}
- test_stats_returns_enriched_shape: verifies the four expected keys are
  present and the raw-internal 'was_old_before_update' key is absent
- test_stats_not_raw_internal_dict: returned dict must not be wl.data itself
- setUp/tearDown: reset class-level data and initialized to prevent leakage

Closes #237
2026-08-28 12:35:58 -04:00
hemna 3f505b6043 test: add unit tests for APRSDClient._checks initialisation (#238 follow-up) (#257)
- test_checks_initialised_to_false: asserts _checks is False right after
  __init__, catching any regression that removes the initialisation
- test_keepalive_check_first_call_no_reset: verifies no AttributeError and
  no spurious reset on the very first keepalive_check() call even when the
  driver is already dead
- test_keepalive_check: remove the manual _checks = False setup that masked
  the original bug; rely on the __init__ value instead
2026-08-28 12:28:42 -04:00
hemna f9d27208f0 fix: initialise APRSDClient._checks = False in __init__ (#254)
keepalive_check() reads self._checks on its first call but the
attribute was never set in __init__, causing AttributeError which
kills the KeepAliveThread on first invocation.

Closes #238
2026-08-28 12:23:52 -04:00
hemna 4ea9e33e20 fix: APRSISDriver.is_configured() returns False when driver is disabled (#236)
The final `return True` in is_configured() should be `return False`.
When APRS-IS is not enabled, the method always returned True, so the
startup guard in server.py never fired for misconfigured instances.

Closes #2
2026-08-28 12:10:50 -04:00
hemna 3a231264b1 fix: correct PacketList.tx() docstring from 'received' to 'transmitted' (#235)
Closes #21
2026-08-28 11:51:12 -04:00
hemna 7a81be06bb fix: add aprsd/~/ to .gitignore and remove stale local directory (#234)
The aprsd/~/ directory (unexpanded tilde) contained a stale pickle
save file at aprsd/~/.config/aprsd/listen/statsstore.p. Runtime data
should never be committed. Added explicit .gitignore entry to prevent
accidental future commits.

Closes #17
2026-08-28 11:44:20 -04:00
hemna 4642035306 fix: rename 'list' variable in HelpPlugin to avoid shadowing Python builtin (#233)
* fix: rename 'list' variable in HelpPlugin to avoid shadowing Python builtin

Closes #15

aprsd/plugin.py HelpPlugin.process() used 'list' as a local variable name,
shadowing the Python built-in list type. Renamed to 'plugin_names'.

* ci: fix CI workflow for Forgejo self-hosted runners

- Switch runs-on from ubuntu-latest to docker (node:20-bookworm)
  ubuntu-latest runner has no Node.js so actions/checkout@v4 fails
- Replace actions/setup-python@v5 (broken on self-hosted) with uv
- Use tox-uv instead of pip-installed tox for faster installs

* ci: use catthehacker/ubuntu:act-latest container (matches haminfo working CI)

* ci: fix master-build.yml for Forgejo self-hosted runners

Use catthehacker/ubuntu:act-latest container + uv for tox,
matching the working pattern from haminfo. Removes broken
actions/setup-python@v2 + ubuntu-latest bare runner combo.

* ci: revert workflow changes - Forgejo-specific, not for GitHub Actions

* ci: restrict master-build to master branch and tags only

The Docker build job clones from GitHub by branch name, which fails
for feature branches (sanitized slash → name mismatch). This workflow
should only run on master pushes and version tags, not on PRs.

* docs: add unreleased changelog entries for PR #233

* docs: update changelog for 5.0.1 release
2026-08-28 11:18:26 -04:00
hemna dcd1294b2f docs: add 5.0.0 changelog entry 2026-06-11 10:26:52 -04:00
hemna daaecf5cce Update for 4.3.0 release 2025-12-11 16:29:59 -05:00
hemna cdb2ce910a update for release 2025-10-17 15:42:08 -04:00
hemna 8932524a46 update for 4.2.3 2025-10-12 16:28:30 -04:00
hemna 9bf4bfd92c Update Changelog for 4.2.1 release 2025-10-07 14:22:59 -04:00
hemna b9fea982f9 Updated Changelog for 4.2.0 2025-08-12 20:59:17 -04:00
hemna 6dba56f74d Changelog for 4.1.2 2025-03-06 17:11:57 -05:00
hemna 7ed8028307 4.1.1 release 2025-03-05 17:00:12 -05:00
hemna a3cda9f37d Update Changelog for 4.1.0 release 2025-02-20 15:15:31 -05:00
hemna 000adef6d4 Prep for 4.0.2 2025-01-25 13:29:04 -05:00
hemna 24f567224c Updated Changelog 2025-01-24 16:46:02 -05:00
hemna 934ebd236d Updated ChangeLog for 4.0.0 2025-01-24 16:38:30 -05:00
hemna bc709b377c Update Changelog for 3.4.4 2024-12-06 09:17:44 -05:00
hemna 578062648b Update Changelog for v3.4.3 2024-10-29 11:08:27 -04:00
hemna 14274c93b5 3.4.2 2024-10-18 16:08:09 -04:00
hemna 765e02f5b3 Collector cleanup 2024-10-18 12:07:02 -04:00
hemna a74a66d9c3 Update Changelog 2024-09-23 17:10:35 -04:00
hemna 9b843eead9 Update ChangeLog 2024-09-23 17:06:06 -04:00
hemna a6f84e42bc retagged v3.4.1 in prep for release 2024-09-16 11:46:53 -04:00
hemna af3d741833 Rebuild ChangeLog 2024-09-16 11:44:37 -04:00
hemna 5ebbb52a2c Renamed Changelog 2024-09-16 11:21:01 -04:00