"""Tests for core/services.py — business logic services.""" import pytest from core.models import Bounty, RoomData, TrackingData, TrackedBounty from core.services import RoomBountyService, TrackingService class MockRoomStorage: """Mock implementation of RoomStorage for testing.""" def __init__(self): self._rooms: dict[int, RoomData] = {} def load(self, room_id: int) -> RoomData | None: return self._rooms.get(room_id) def save(self, room_data: RoomData) -> None: self._rooms[room_data.room_id] = room_data def add_bounty(self, room_id: int, bounty: Bounty) -> None: if room_id not in self._rooms: self._rooms[room_id] = RoomData(room_id=room_id, bounties=[], next_id=1) self._rooms[room_id].bounties.append(bounty) if bounty.id >= self._rooms[room_id].next_id: self._rooms[room_id].next_id = bounty.id + 1 def update_bounty(self, room_id: int, bounty: Bounty) -> None: if room_id in self._rooms: for i, b in enumerate(self._rooms[room_id].bounties): if b.id == bounty.id: self._rooms[room_id].bounties[i] = bounty break def delete_bounty(self, room_id: int, bounty_id: int) -> None: if room_id in self._rooms: self._rooms[room_id].bounties = [ b for b in self._rooms[room_id].bounties if b.id != bounty_id ] def get_bounty(self, room_id: int, bounty_id: int) -> Bounty | None: if room_id in self._rooms: for b in self._rooms[room_id].bounties: if b.id == bounty_id: return b return None class MockTrackingStorage: """Mock implementation of TrackingStorage for testing.""" def __init__(self): self._tracking: dict[tuple[int, int], TrackingData] = {} def load(self, room_id: int, user_id: int) -> TrackingData | None: return self._tracking.get((room_id, user_id)) def save(self, tracking_data: TrackingData) -> None: self._tracking[(tracking_data.room_id, tracking_data.user_id)] = tracking_data def track_bounty(self, room_id: int, user_id: int, tracked: TrackedBounty) -> None: key = (room_id, user_id) if key not in self._tracking: self._tracking[key] = TrackingData( room_id=room_id, user_id=user_id, tracked=[] ) self._tracking[key].tracked.append(tracked) def untrack_bounty(self, room_id: int, user_id: int, bounty_id: int) -> None: key = (room_id, user_id) if key in self._tracking: self._tracking[key].tracked = [ t for t in self._tracking[key].tracked if t.bounty_id != bounty_id ] class TestRoomBountyService: def test_create_bounty(self): storage = MockRoomStorage() service = RoomBountyService(storage) bounty = service.create_bounty( room_id=-1001, text="Test bounty", link="https://example.com", due_date_ts=1234567890, created_by_user_id=123, ) assert bounty.id == 1 assert bounty.text == "Test bounty" assert bounty.link == "https://example.com" assert bounty.created_by_user_id == 123 def test_create_bounty_increments_id(self): storage = MockRoomStorage() service = RoomBountyService(storage) b1 = service.create_bounty(-1001, "B1", None, None, 123) b2 = service.create_bounty(-1001, "B2", None, None, 123) assert b2.id == 2 def test_get_bounty(self): storage = MockRoomStorage() service = RoomBountyService(storage) created = service.create_bounty(-1001, "Test", None, None, 123) result = service.get_bounty(-1001, created.id) assert result is not None assert result.text == "Test" def test_get_bounty_not_found(self): storage = MockRoomStorage() service = RoomBountyService(storage) result = service.get_bounty(-1001, 999) assert result is None def test_list_bounties(self): storage = MockRoomStorage() service = RoomBountyService(storage) service.create_bounty(-1001, "B1", None, None, 123) service.create_bounty(-1001, "B2", None, None, 123) bounties = service.list_bounties(-1001) assert len(bounties) == 2 def test_list_bounties_empty_room(self): storage = MockRoomStorage() service = RoomBountyService(storage) bounties = service.list_bounties(-1001) assert bounties == [] def test_update_bounty(self): storage = MockRoomStorage() service = RoomBountyService(storage) created = service.create_bounty(-1001, "Original", None, None, 123) result = service.update_bounty(-1001, created.id, text="Updated") assert result is True updated = service.get_bounty(-1001, created.id) assert updated is not None assert updated.text == "Updated" def test_update_bounty_not_found(self): storage = MockRoomStorage() service = RoomBountyService(storage) result = service.update_bounty(-1001, 999, text="Updated") assert result is False def test_delete_bounty(self): storage = MockRoomStorage() service = RoomBountyService(storage) created = service.create_bounty(-1001, "Test", None, None, 123) result = service.delete_bounty(-1001, created.id) assert result is True assert service.get_bounty(-1001, created.id) is None def test_delete_bounty_not_found(self): storage = MockRoomStorage() service = RoomBountyService(storage) result = service.delete_bounty(-1001, 999) assert result is False class TestTrackingService: def test_track_bounty(self): storage = MockTrackingStorage() service = TrackingService(storage) result = service.track_bounty(-1001, 123456, 1) assert result is True def test_track_bounty_duplicate(self): storage = MockTrackingStorage() service = TrackingService(storage) service.track_bounty(-1001, 123456, 1) result = service.track_bounty(-1001, 123456, 1) assert result is False def test_untrack_bounty(self): storage = MockTrackingStorage() service = TrackingService(storage) service.track_bounty(-1001, 123456, 1) result = service.untrack_bounty(-1001, 123456, 1) assert result is True def test_untrack_bounty_not_tracking(self): storage = MockTrackingStorage() service = TrackingService(storage) result = service.untrack_bounty(-1001, 123456, 1) assert result is False def test_list_tracked(self): storage = MockTrackingStorage() service = TrackingService(storage) service.track_bounty(-1001, 123456, 1) service.track_bounty(-1001, 123456, 2) tracked = service.list_tracked(-1001, 123456) assert len(tracked) == 2 def test_list_tracked_empty(self): storage = MockTrackingStorage() service = TrackingService(storage) tracked = service.list_tracked(-1001, 123456) assert tracked == []