Address PR #20 feedback:

- Removed PersonalStorage (redundant - RoomStorage handles both via room_id)
- Added ensure_room() and ensure_tracking() methods for explicit creation
- Added @runtime_checkable to Protocols for isinstance checks
- Added tests/test_ports.py with 11 unit tests for storage protocols
This commit is contained in:
shokollm
2026-04-02 23:57:49 +00:00
parent 5450d12400
commit 43603659de
3 changed files with 214 additions and 39 deletions

View File

@@ -1,14 +1,19 @@
"""Abstract storage interfaces (Ports) for JIGAIDO storage adapters."""
from typing import Protocol
from typing import Protocol, runtime_checkable
from core.models import Bounty, RoomData, TrackingData, TrackedBounty
@runtime_checkable
class RoomStorage(Protocol):
"""Storage port for room (group) bounties.
"""Storage port for room bounties.
Implement this protocol to provide room bounty storage capability.
A room is identified by room_id:
- Negative room_id: Telegram group (e.g., -1001)
- Positive room_id: DM/personal context (user's Telegram ID)
This single port handles both group and personal bounties.
"""
def load(self, room_id: int) -> RoomData | None:
@@ -19,8 +24,12 @@ class RoomStorage(Protocol):
"""Save all data for a room."""
...
def ensure_room(self, room_id: int) -> RoomData:
"""Ensure a room exists, creating it if necessary. Returns RoomData."""
...
def add_bounty(self, room_id: int, bounty: Bounty) -> None:
"""Add a new bounty to a room."""
"""Add a new bounty to a room. Creates room if it doesn't exist."""
...
def update_bounty(self, room_id: int, bounty: Bounty) -> None:
@@ -36,42 +45,12 @@ class RoomStorage(Protocol):
...
class PersonalStorage(Protocol):
"""Storage port for personal (DM) bounties.
Personal bounties are stored in RoomData with a positive room_id (user's ID).
This port provides the same operations as RoomStorage but for personal context.
"""
def load(self, user_id: int) -> RoomData | None:
"""Load personal bounty data for a user. Returns None if not found."""
...
def save(self, room_data: RoomData) -> None:
"""Save personal bounty data for a user."""
...
def add_bounty(self, user_id: int, bounty: Bounty) -> None:
"""Add a new bounty to a user's personal storage."""
...
def update_bounty(self, user_id: int, bounty: Bounty) -> None:
"""Update an existing bounty in personal storage."""
...
def delete_bounty(self, user_id: int, bounty_id: int) -> None:
"""Delete a bounty from personal storage."""
...
def get_bounty(self, user_id: int, bounty_id: int) -> Bounty | None:
"""Get a specific bounty from personal storage by ID."""
...
@runtime_checkable
class TrackingStorage(Protocol):
"""Storage port for tracking data.
Tracks which bounties a user is tracking in a specific room.
Use ensure_tracking() to create a new tracking entry before tracking bounties.
"""
def load(self, room_id: int, user_id: int) -> TrackingData | None:
@@ -82,8 +61,12 @@ class TrackingStorage(Protocol):
"""Save tracking data."""
...
def ensure_tracking(self, room_id: int, user_id: int) -> TrackingData:
"""Ensure tracking exists for user in room, creating if necessary. Returns TrackingData."""
...
def track_bounty(self, room_id: int, user_id: int, tracked: TrackedBounty) -> None:
"""Add a bounty to a user's tracking list."""
"""Add a bounty to a user's tracking list. Creates tracking if needed."""
...
def untrack_bounty(self, room_id: int, user_id: int, bounty_id: int) -> None: