Skip to content

Add unit tests for osism/utils semaphore, redlock and task locks#2349

Open
berendt wants to merge 3 commits into
mainfrom
implement/issue-2230-utils-locks-tests
Open

Add unit tests for osism/utils semaphore, redlock and task locks#2349
berendt wants to merge 3 commits into
mainfrom
implement/issue-2230-utils-locks-tests

Conversation

@berendt

@berendt berendt commented Jun 12, 2026

Copy link
Copy Markdown
Member

Implements #2230.

Adds tests/unit/utils/test_init_locks.py, the third companion to
test_init_connections.py and test_init_task_output.py, covering the
concurrency primitives and global task-lock helpers in
osism/utils/__init__.py. No production code is changed — this is a
pure test addition. 40 test functions (41 cases) following the
established mocker / loguru_logs conventions of the existing suite.

Change set (commit order)

  1. Add RedisSemaphore unit tests__init__ attribute storage and
    the unconditional semaphore: key prefix (including the
    already-prefixed input → double prefix); acquire() first-try
    success, full-window timeout returning False, per-iteration
    zremrangebyscore cleanup, the default-10s and explicit-timeout
    fallbacks, and a fresh uuid.uuid4() per call; release()
    idempotency; and the __enter__/__exit__ context-manager protocol
    (returns self, raises TimeoutError containing the key, always
    releases, never swallows exceptions). time.time/time.sleep/
    uuid.uuid4 are patched so the acquisition loop is deterministic.
  2. Add create_redlock and create_netbox_semaphore unit tests
    create_redlock returns the pottery.Redlock built with
    key/masters={redis}/auto_release_time (default 3600 and a
    custom value), forces the "pottery" logger to CRITICAL, and
    suppresses construction-time stdout/stderr (asserted via capsys).
    create_netbox_semaphore falls back to
    settings.NETBOX_MAX_CONNECTIONS or takes an explicit value, builds
    a per-URL md5-derived key with timeout=30 and the shared redis
    client, and produces a stable key per URL.
  3. Add task-lock helper unit testsset_task_lock,
    remove_task_lock, is_task_locked and check_task_lock_and_exit,
    including the operator-user/reason fallbacks, the JSON payload shape
    (regex-matched ISO timestamp), the .decode("utf-8") path, and the
    error-logging branches. builtins.exit and is_task_locked are
    patched for check_task_lock_and_exit.

Notes for the reviewer

  • pottery.Redlock is patched on the pottery module (not imported
    into osism.utils) because production imports it lazily inside
    create_redlock — matching the issue's mocking hint.
  • logging.getLogger is patched in the create_redlock tests so they
    never mutate the process-global pottery logger level (test
    isolation), addressing the issue's note about that mutable state.
  • freezegun is not a dev dependency, so the set_task_lock timestamp
    is matched with a regex (^\d{4}-\d{2}-\d{2}T...) rather than frozen,
    as the issue allows.

Local verification

Per repository practice the test suite, flake8, mypy and
python-black run only in the pipenv venv / Zuul CI (the dev tools and
pottery are not installed in the base interpreter), so they were not
run locally. python -m py_compile passes. The Zuul
python-osism-unit-tests job will run the suite. The tests exercise
every branch of the targeted helpers, satisfying the ≥90 % coverage
criterion for them.

Closes #2230

Assisted-by: Claude:claude-opus-4-8

berendt added 3 commits June 12, 2026 12:54
Cover the RedisSemaphore distributed-semaphore class in
osism/utils/__init__.py: attribute storage and unconditional
"semaphore:" key prefixing, acquire() success and timeout paths
(deterministic time/uuid mocking), per-iteration expired-holder
cleanup, the default and explicit timeout fallbacks, release()
idempotency and the context-manager protocol.

Part of #2230

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Christian Berendt <berendt@osism.tech>
Cover create_redlock: the returned pottery Redlock is built with the
given key, masters={redis} and auto_release_time (default 3600 and a
custom value), the "pottery" logger is forced to CRITICAL, and
construction-time stdout/stderr is suppressed. pottery.Redlock is
patched on the module because it is imported lazily inside the
function.

Cover create_netbox_semaphore: max_connections falls back to
settings.NETBOX_MAX_CONNECTIONS or is taken explicitly, the semaphore
is built with a per-URL md5-derived key, timeout=30 and the shared
redis client, and the key is stable per URL.

Part of #2230

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Christian Berendt <berendt@osism.tech>
Cover the global task-lock helpers in osism/utils/__init__.py:

- set_task_lock: user falls back to settings.OPERATOR_USER or is
  taken explicitly, reason=None is serialised as JSON null, the
  payload carries locked=True plus an ISO-8601 timestamp, and a
  redis failure returns False and logs the error.
- remove_task_lock: deletes osism:task_lock and returns True; a
  failure returns False and logs.
- is_task_locked: returns None when unset, byte-decodes and parses
  the stored JSON, and returns None while logging on redis errors
  and JSON-decode errors.
- check_task_lock_and_exit: no-ops when unlocked, otherwise logs the
  user/timestamp/reason and calls exit(1), skips the reason line
  when reason is None, and defaults missing fields to "unknown".

Closes #2230

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Christian Berendt <berendt@osism.tech>
@berendt berendt marked this pull request as ready for review June 12, 2026 11:21

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@berendt berendt requested a review from ideaship June 12, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

Unit tests for osism/utils/__init__.py — semaphore, redlock, task locks

2 participants