from datetime import timedelta from django.db.models import Q from django.test import TestCase from django.urls import reverse from django.utils import timezone from apps.lyric.models import Token, User from apps.epic.models import GateSlot, Room, RoomInvite, debit_token, select_token class RoomCreationTest(TestCase): def test_creating_a_room_generates_six_gate_slots(self): owner = User.objects.create(email="founder@example.com") room = Room.objects.create(name="Test Room", owner=owner) self.assertEqual(GateSlot.objects.filter(room=room).count(), 6) class DebitTokenTest(TestCase): def setUp(self): self.owner = User.objects.create(email="founder@example.com") self.room = Room.objects.create( name="Test Room", owner=self.owner, renewal_period=timedelta(days=7) ) self.slot = self.room.gate_slots.get(slot_number=1) def test_debit_free_token_consumes_token_and_fills_slot(self): free_token = Token.objects.get(user=self.owner, token_type=Token.FREE) debit_token(self.owner, self.slot, free_token) self.assertFalse(Token.objects.filter(pk=free_token.pk).exists()) self.slot.refresh_from_db() self.assertEqual(self.slot.status, GateSlot.FILLED) self.assertEqual(self.slot.gamer, self.owner) def test_debit_coin_does_not_consume_token(self): coin_token = Token.objects.get(user=self.owner, token_type=Token.COIN) debit_token(self.owner, self.slot, coin_token) self.assertTrue(Token.objects.filter(pk=coin_token.pk).exists()) self.slot.refresh_from_db() self.assertEqual(self.slot.status, GateSlot.FILLED) self.assertEqual(self.slot.gamer, self.owner) def test_debit_fills_last_slot_and_opens_gate(self): for i in range(2, 7): gamer = User.objects.create(email=f"g{i}@test.io") slot = self.room.gate_slots.get(slot_number=i) slot.gamer = gamer slot.status = GateSlot.FILLED slot.save() free_token = Token.objects.get(user=self.owner, token_type=Token.FREE) debit_token(self.owner, self.slot, free_token) self.room.refresh_from_db() self.assertEqual(self.room.gate_status, Room.OPEN) class CoinTokenInUseTest(TestCase): def setUp(self): self.owner = User.objects.create(email="founder@example.com") self.room = Room.objects.create( name="Dragon's Den", owner=self.owner, renewal_period=timedelta(days=7), ) self.slot = self.room.gate_slots.get(slot_number=1) self.coin = Token.objects.get(user=self.owner, token_type=Token.COIN) debit_token(self.owner, self.slot, self.coin) self.coin.refresh_from_db() def test_coin_tooltip_expiry_shows_next_ready_date(self): expected_date = self.coin.next_ready_at.strftime("%Y-%m-%d") self.assertIn(expected_date, self.coin.tooltip_expiry()) def test_coin_tooltip_room_html_contains_anchor(self): room_url = reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) html = self.coin.tooltip_room_html() self.assertIn(f'href="{room_url}"', html) self.assertIn(self.room.name, html) class SelectTokenTest(TestCase): def setUp(self): self.user = User.objects.create(email="gamer@test.io") self.other_room = Room.objects.create(name="Other Room", owner=self.user) self.coin = Token.objects.get(user=self.user, token_type=Token.COIN) def test_returns_coin_when_available(self): token = select_token(self.user) self.assertEqual(token.token_type, Token.COIN) def test_returns_free_token_when_coin_in_use(self): self.coin.current_room = self.other_room self.coin.save() token = select_token(self.user) self.assertEqual(token.token_type, Token.FREE) def test_free_token_selection_is_fefo(self): self.coin.current_room = self.other_room self.coin.save() Token.objects.filter(user=self.user, token_type=Token.FREE).delete() soon = Token.objects.create( user=self.user, token_type=Token.FREE, expires_at=timezone.now() + timedelta(days=2), ) Token.objects.create( user=self.user, token_type=Token.FREE, expires_at=timezone.now() + timedelta(days=6), ) token = select_token(self.user) self.assertEqual(token.pk, soon.pk) def test_returns_tithe_when_coin_in_use_and_no_free_tokens(self): self.coin.current_room = self.other_room self.coin.save() Token.objects.filter(user=self.user, token_type=Token.FREE).delete() tithe = Token.objects.create(user=self.user, token_type=Token.TITHE) token = select_token(self.user) self.assertEqual(token.pk, tithe.pk) def test_returns_none_when_all_depleted(self): self.coin.current_room = self.other_room self.coin.save() Token.objects.filter(user=self.user, token_type=Token.FREE).delete() token = select_token(self.user) self.assertIsNone(token) def test_returns_pass_for_staff(self): self.user.is_staff = True self.user.save() pass_token = Token.objects.create(user=self.user, token_type=Token.PASS) token = select_token(self.user) self.assertEqual(token.token_type, Token.PASS) class RoomInviteTest(TestCase): def setUp(self): self.founder = User.objects.create(email="founder@example.com") self.room = Room.objects.create(name="Dragon's Den", owner=self.founder) def test_founder_can_invite_by_email(self): invite = RoomInvite.objects.create( room=self.room, inviter=self.founder, invitee_email="friend@example.com", ) self.assertEqual(invite.status, RoomInvite.PENDING) def test_invited_room_appears_in_my_games_queryset(self): friend = User.objects.create(email="friend@example.com") RoomInvite.objects.create( room=self.room, inviter=self.founder, invitee_email=friend.email, ) rooms = Room.objects.filter( Q(owner=friend) | Q(gate_slots__gamer=friend) | Q(invites__invitee_email=friend.email, invites__status=RoomInvite.PENDING) ).distinct() self.assertIn(self.room, rooms)