chore(v2): lint pass — black + ruff auto-fix + mypy regressions (#29 v1.1)

Pre-merge lint hygiene on the PR #30 touched files:

- `black` reformatted 9 files (cassette_transport, crud, models, tasks,
  views_api, nip44, all 3 cassette test files, migrations). Cosmetic:
  line lengths, trailing commas, multi-line argument layout.
- `ruff check --fix` cleared 176 of 202 errors auto-fixed. Mostly
  `UP006` `typing.Optional` → `| None` modernization, `I001` import
  sort order, `UP035` typing-extensions cleanup.
- Two new mypy regressions introduced by the migration commit dcb7de0
  fixed:
  - `crud.py:apply_bootstrap_state` — annotated `existing_first: dict
    | None` on the dedup fetch.
  - `tasks.py:_cassette_consumer_tick` — `# type: ignore[arg-type]` on
    the `nostr_client.relay_manager.add_subscription` call; nostrclient's
    upstream typing declares `list[str]` for filters but the actual
    Nostr protocol takes `list[<filter-dict>]`. The runtime accepts it
    (live smoke at 13:43Z dispatched `nip44_decrypt` cleanly through
    this subscription); the typing mismatch is upstream's.

Remaining lint state, intentionally not addressed in this commit
(all pre-existing baseline, not regressions):
- 8 mypy errors in `calculations.py` + the unchanged-by-this-PR parts
  of `crud.py` — pre-existing on v2-bitspire.
- 26 ruff style warnings: 14 are N805 false-positives on Pydantic
  validators (`cls` first-arg is correct for `@validator`-decorated
  methods); 4 are N818 exception-name-suffix preferences on my new
  exception classes (renaming would touch many call sites; keep
  `OperatorIdentityMissing` / `SignerUnavailable` / `RelayUnavailable`
  / `_NostrclientUnavailable` as-is for clarity); 5 are E501 line-too-
  long on docstrings (the long lines are formatted for clarity);
  1 RUF002 unicode-minus in a docstring.

Tests: 155 passed, 1 pre-existing async-plugin failure unchanged.
Live smoke (both publish + consume directions through the bunker)
unaffected — this is purely a code-style pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-05-31 15:50:14 +02:00
commit d448fab0d2
10 changed files with 249 additions and 352 deletions

View file

@ -30,7 +30,6 @@ from ..models import (
UpsertCassetteConfigData,
)
# =============================================================================
# PublishCassettesPayload — wire-shape validators
# =============================================================================
@ -95,49 +94,35 @@ class TestPublishCassettesPayload:
def test_rejects_non_int_position_key(self):
with pytest.raises(ValueError) as exc:
PublishCassettesPayload(
positions={"abc": {"denomination": 20, "count": 1}}
)
PublishCassettesPayload(positions={"abc": {"denomination": 20, "count": 1}})
assert "is not an int" in str(exc.value)
def test_rejects_non_positive_position(self):
with pytest.raises(ValueError) as exc:
PublishCassettesPayload(
positions={"0": {"denomination": 20, "count": 1}}
)
PublishCassettesPayload(positions={"0": {"denomination": 20, "count": 1}})
assert "position must be > 0" in str(exc.value)
def test_rejects_negative_position(self):
with pytest.raises(ValueError) as exc:
PublishCassettesPayload(
positions={"-1": {"denomination": 20, "count": 1}}
)
PublishCassettesPayload(positions={"-1": {"denomination": 20, "count": 1}})
assert "position must be > 0" in str(exc.value)
def test_rejects_negative_count(self):
with pytest.raises(ValueError):
PublishCassettesPayload(
positions={"1": {"denomination": 20, "count": -1}}
)
PublishCassettesPayload(positions={"1": {"denomination": 20, "count": -1}})
def test_rejects_zero_denomination(self):
with pytest.raises(ValueError):
PublishCassettesPayload(
positions={"1": {"denomination": 0, "count": 49}}
)
PublishCassettesPayload(positions={"1": {"denomination": 0, "count": 49}})
def test_rejects_negative_denomination(self):
with pytest.raises(ValueError):
PublishCassettesPayload(
positions={"1": {"denomination": -20, "count": 49}}
)
PublishCassettesPayload(positions={"1": {"denomination": -20, "count": 49}})
def test_allows_zero_count(self):
"""An empty cassette is a legal state — operator must be able to
record `count=0` after a dispatcher pulled the cassette mid-day."""
p = PublishCassettesPayload(
positions={"1": {"denomination": 20, "count": 0}}
)
p = PublishCassettesPayload(positions={"1": {"denomination": 20, "count": 0}})
assert p.positions[1].count == 0
@ -221,17 +206,13 @@ class TestShouldApplyBootstrapState:
assert _should_apply_bootstrap_state(None, "new-event-id") is True
def test_applies_when_existing_event_id_differs(self):
assert (
_should_apply_bootstrap_state("old-event-id", "new-event-id") is True
)
assert _should_apply_bootstrap_state("old-event-id", "new-event-id") is True
def test_skips_when_existing_event_id_matches(self):
"""The same bootstrap event re-delivered after a relay reconnect
or satmachineadmin restart should no-op, not re-upsert the same
rows (which would clobber any operator edits since)."""
assert (
_should_apply_bootstrap_state("same-event", "same-event") is False
)
assert _should_apply_bootstrap_state("same-event", "same-event") is False
def test_applies_when_existing_is_empty_string_and_incoming_is_id(self):
"""Defensive — a sentinel empty-string existing_state_event_id