from datetime import timedelta from unittest.mock import patch 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, TableSeat class RoomCreationViewTest(TestCase): def setUp(self): self.user = User.objects.create(email="founder@test.io") self.client.force_login(self.user) def test_post_creates_room_and_redirects_to_gatekeeper(self): response = self.client.post( reverse("epic:create_room"), data={"name": "Test Room"}, ) room = Room.objects.get(owner=self.user) self.assertRedirects( response, reverse( "epic:gatekeeper", args=[room.id], ) ) def test_post_requires_login(self): self.client.logout() response = self.client.post( reverse("epic:create_room"), data={"name": "Test Room"}, ) def test_create_room_get_redirects_to_gameboard(self): response = self.client.get(reverse("epic:create_room")) self.assertRedirects(response, "/gameboard/") class MyGamesContextTest(TestCase): def setUp(self): self.user = User.objects.create(email="gamer@example.com") self.client.force_login(self.user) def test_gameboard_context_includes_owned_rooms(self): room = Room.objects.create(name="Durango", owner=self.user) response = self.client.get("/gameboard/") self.assertIn(room, response.context["my_games"]) def test_gameboard_context_includes_rooms_with_filled_slot(self): other = User.objects.create(email="friend@example.com") room = Room.objects.create(name="Their Room", owner=other) slot = room.gate_slots.get(slot_number=2) slot.gamer = self.user slot.status = "FILLED" slot.save() response = self.client.get("/gameboard/") self.assertIn(room, response.context["my_games"]) class GateStatusViewTest(TestCase): def setUp(self): self.owner = User.objects.create(email="founder@test.io") self.client.force_login(self.owner) self.room = Room.objects.create(name="Test Room", owner=self.owner) def test_gate_status_returns_launch_btn_when_open(self): self.room.gate_status = Room.OPEN self.room.save() response = self.client.get(reverse("epic:gate_status", kwargs={"room_id": self.room.id})) self.assertEqual(response.status_code, 200) self.assertContains(response, "launch-game-btn") def test_gate_status_returns_partial_when_gathering(self): response = self.client.get( reverse("epic:gate_status", kwargs={"room_id": self.room.id}) ) self.assertEqual(response.status_code, 200) self.assertContains(response, "gate-modal") class DropTokenViewTest(TestCase): def setUp(self): self.gamer = User.objects.create(email="gamer@test.io") self.client.force_login(self.gamer) owner = User.objects.create(email="owner@test.io") self.room = Room.objects.create(name="Test Room", owner=owner) def test_drop_token_reserves_lowest_empty_slot(self): self.client.post(reverse("epic:drop_token", kwargs={"room_id": self.room.id})) slot = self.room.gate_slots.get(slot_number=1) self.assertEqual(slot.status, GateSlot.RESERVED) self.assertEqual(slot.gamer, self.gamer) def test_drop_token_skips_already_filled_slots(self): other = User.objects.create(email="other@test.io") slot1 = self.room.gate_slots.get(slot_number=1) slot1.gamer = other slot1.status = GateSlot.FILLED slot1.save() self.client.post(reverse("epic:drop_token", kwargs={"room_id": self.room.id})) slot2 = self.room.gate_slots.get(slot_number=2) self.assertEqual(slot2.status, GateSlot.RESERVED) self.assertEqual(slot2.gamer, self.gamer) def test_drop_token_blocked_when_another_slot_reserved(self): other = User.objects.create(email="other@test.io") slot1 = self.room.gate_slots.get(slot_number=1) slot1.gamer = other slot1.status = GateSlot.RESERVED slot1.reserved_at = timezone.now() slot1.save() self.client.post(reverse("epic:drop_token", kwargs={"room_id": self.room.id})) # Slot 2 should remain EMPTY — lock held by other user slot2 = self.room.gate_slots.get(slot_number=2) self.assertEqual(slot2.status, GateSlot.EMPTY) def test_drop_token_blocked_when_user_already_has_filled_slot(self): slot1 = self.room.gate_slots.get(slot_number=1) slot1.gamer = self.gamer slot1.status = GateSlot.FILLED slot1.save() self.client.post(reverse("epic:drop_token", kwargs={"room_id": self.room.id})) slot2 = self.room.gate_slots.get(slot_number=2) self.assertEqual(slot2.status, GateSlot.EMPTY) def test_drop_token_sets_reserved_at(self): self.client.post(reverse("epic:drop_token", kwargs={"room_id": self.room.id})) slot = self.room.gate_slots.get(slot_number=1) self.assertIsNotNone(slot.reserved_at) def test_drop_token_redirects_to_gatekeeper(self): response = self.client.post( reverse("epic:drop_token", kwargs={"room_id": self.room.id}) ) self.assertRedirects( response, reverse("epic:gatekeeper", args=[self.room.id]) ) class ConfirmTokenViewTest(TestCase): def setUp(self): self.gamer = User.objects.create(email="gamer@test.io") self.client.force_login(self.gamer) owner = User.objects.create(email="owner@test.io") self.room = Room.objects.create(name="Test Room", owner=owner) self.slot = self.room.gate_slots.get(slot_number=1) self.slot.gamer = self.gamer self.slot.status = GateSlot.RESERVED self.slot.reserved_at = timezone.now() self.slot.save() Token.objects.create(user=self.gamer, token_type=Token.FREE) def test_confirm_marks_slot_filled(self): self.client.post( reverse("epic:confirm_token", kwargs={"room_id": self.room.id}) ) self.slot.refresh_from_db() self.assertEqual(self.slot.status, GateSlot.FILLED) def test_confirm_sets_gate_open_when_all_slots_filled(self): # Fill slots 2–6 via ORM for i in range(2, 7): other = User.objects.create(email=f"g{i}@test.io") s = self.room.gate_slots.get(slot_number=i) s.gamer = other s.status = GateSlot.FILLED s.save() self.client.post( reverse("epic:confirm_token", kwargs={"room_id": self.room.id}) ) self.room.refresh_from_db() self.assertEqual(self.room.gate_status, Room.OPEN) def test_confirm_redirects_to_gatekeeper(self): response = self.client.post( reverse("epic:confirm_token", kwargs={"room_id": self.room.id}) ) self.assertRedirects( response, reverse("epic:gatekeeper", args=[self.room.id]) ) def test_confirm_does_nothing_without_reserved_slot(self): self.slot.status = GateSlot.EMPTY self.slot.gamer = None self.slot.save() self.client.post( reverse("epic:confirm_token", kwargs={"room_id": self.room.id}) ) self.slot.refresh_from_db() self.assertEqual(self.slot.status, GateSlot.EMPTY) class ReturnTokenViewTest(TestCase): def setUp(self): self.gamer = User.objects.create(email="gamer@test.io") self.client.force_login(self.gamer) owner = User.objects.create(email="owner@test.io") self.room = Room.objects.create(name="Test Room", owner=owner) self.slot = self.room.gate_slots.get(slot_number=1) self.slot.gamer = self.gamer self.slot.status = GateSlot.RESERVED self.slot.reserved_at = timezone.now() self.slot.save() def test_return_clears_reserved_slot(self): self.client.post( reverse("epic:return_token", kwargs={"room_id": self.room.id}) ) self.slot.refresh_from_db() self.assertEqual(self.slot.status, GateSlot.EMPTY) self.assertIsNone(self.slot.gamer) self.assertIsNone(self.slot.reserved_at) def test_return_after_confirm_clears_filled_slot(self): self.slot.status = GateSlot.FILLED self.slot.save() self.client.post( reverse("epic:return_token", kwargs={"room_id": self.room.id}) ) self.slot.refresh_from_db() self.assertEqual(self.slot.status, GateSlot.EMPTY) self.assertIsNone(self.slot.gamer) def test_return_redirects_to_gatekeeper(self): response = self.client.post( reverse("epic:return_token", kwargs={"room_id": self.room.id}) ) self.assertRedirects( response, reverse("epic:gatekeeper", args=[self.room.id]) ) def test_return_restores_coin_token(self): coin = Token.objects.get(user=self.gamer, token_type=Token.COIN) coin.current_room = self.room coin.next_ready_at = timezone.now() + timedelta(days=7) coin.save() self.slot.status = GateSlot.FILLED self.slot.debited_token_type = Token.COIN self.slot.save() self.client.post( reverse("epic:return_token", kwargs={"room_id": self.room.id}) ) coin.refresh_from_db() self.assertIsNone(coin.current_room) self.assertIsNone(coin.next_ready_at) def test_return_restores_free_token(self): Token.objects.filter(user=self.gamer, token_type=Token.FREE).delete() expires = timezone.now() + timedelta(days=3) self.slot.status = GateSlot.FILLED self.slot.debited_token_type = Token.FREE self.slot.debited_token_expires_at = expires self.slot.save() self.client.post( reverse("epic:return_token", kwargs={"room_id": self.room.id}) ) restored = Token.objects.filter(user=self.gamer, token_type=Token.FREE).first() self.assertIsNotNone(restored) self.assertEqual(restored.expires_at, expires) def test_return_restores_tithe_token(self): self.slot.status = GateSlot.FILLED self.slot.debited_token_type = Token.TITHE self.slot.save() self.client.post( reverse("epic:return_token", kwargs={"room_id": self.room.id}) ) self.assertTrue( Token.objects.filter(user=self.gamer, token_type=Token.TITHE).exists() ) class DropTokenAvailabilityViewTest(TestCase): def setUp(self): self.gamer = User.objects.create(email="gamer@test.io") self.client.force_login(self.gamer) owner = User.objects.create(email="owner@test.io") self.room = Room.objects.create(name="Test Room", owner=owner) self.other_room = Room.objects.create(name="Other Room", owner=owner) self.coin = Token.objects.get(user=self.gamer, token_type=Token.COIN) def test_drop_reserves_slot_when_tokens_available(self): self.client.post(reverse("epic:drop_token", kwargs={"room_id": self.room.id})) slot = self.room.gate_slots.get(slot_number=1) self.assertEqual(slot.status, GateSlot.RESERVED) # token not debited yet — that happens at confirm self.coin.refresh_from_db() self.assertIsNone(self.coin.current_room) def test_drop_returns_402_when_all_tokens_depleted(self): self.coin.current_room = self.other_room self.coin.save() Token.objects.filter(user=self.gamer, token_type=Token.FREE).delete() response = self.client.post( reverse("epic:drop_token", kwargs={"room_id": self.room.id}) ) self.assertEqual(response.status_code, 402) class ConfirmTokenPriorityViewTest(TestCase): def setUp(self): self.gamer = User.objects.create(email="gamer@test.io") self.client.force_login(self.gamer) owner = User.objects.create(email="owner@test.io") self.room = Room.objects.create(name="Test Room", owner=owner) self.other_room = Room.objects.create(name="Other Room", owner=owner) self.slot = self.room.gate_slots.get(slot_number=1) self.slot.gamer = self.gamer self.slot.status = GateSlot.RESERVED self.slot.reserved_at = timezone.now() self.slot.save() self.coin = Token.objects.get(user=self.gamer, token_type=Token.COIN) def test_confirm_leases_coin_to_room(self): self.client.post(reverse("epic:confirm_token", kwargs={"room_id": self.room.id})) self.coin.refresh_from_db() self.assertEqual(self.coin.current_room, self.room) self.assertTrue(Token.objects.filter(pk=self.coin.pk).exists()) def test_confirm_uses_free_token_when_coin_in_use(self): self.coin.current_room = self.other_room self.coin.save() self.client.post(reverse("epic:confirm_token", kwargs={"room_id": self.room.id})) self.assertEqual( Token.objects.filter(user=self.gamer, token_type=Token.FREE).count(), 0 ) self.coin.refresh_from_db() self.assertEqual(self.coin.current_room, self.other_room) def test_confirm_uses_tithe_when_free_tokens_exhausted(self): self.coin.current_room = self.other_room self.coin.save() Token.objects.filter(user=self.gamer, token_type=Token.FREE).delete() tithe = Token.objects.create(user=self.gamer, token_type=Token.TITHE) self.client.post(reverse("epic:confirm_token", kwargs={"room_id": self.room.id})) self.assertFalse(Token.objects.filter(pk=tithe.pk).exists()) def test_pass_not_consumed_and_coin_not_leased(self): self.gamer.is_staff = True self.gamer.save() pass_token = Token.objects.create(user=self.gamer, token_type=Token.PASS) self.client.post(reverse("epic:confirm_token", kwargs={"room_id": self.room.id})) self.assertTrue(Token.objects.filter(pk=pass_token.pk).exists()) self.coin.refresh_from_db() self.assertIsNone(self.coin.current_room) class RoleSelectRenderingTest(TestCase): def setUp(self): self.founder = User.objects.create(email="founder@test.io") self.room = Room.objects.create(name="Test Room", owner=self.founder) self.gamers = [self.founder] for i in range(2, 7): self.gamers.append(User.objects.create(email=f"g{i}@test.io")) for i, gamer in enumerate(self.gamers, start=1): slot = self.room.gate_slots.get(slot_number=i) slot.gamer = gamer slot.status = GateSlot.FILLED slot.save() self.room.gate_status = Room.OPEN self.room.table_status = Room.ROLE_SELECT self.room.save() for i, gamer in enumerate(self.gamers, start=1): TableSeat.objects.create(room=self.room, gamer=gamer, slot_number=i) def test_room_view_includes_card_stack_when_role_select(self): self.client.force_login(self.founder) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, "card-stack") def test_card_stack_eligible_for_slot1_gamer(self): self.client.force_login(self.founder) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, 'data-state="eligible"') def test_card_stack_ineligible_for_slot2_gamer(self): self.client.force_login(self.gamers[1]) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, 'data-state="ineligible"') def test_card_stack_ineligible_shows_fa_ban(self): self.client.force_login(self.gamers[1]) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, "fa-ban") def test_card_stack_eligible_omits_fa_ban(self): self.client.force_login(self.founder) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertNotContains(response, "fa-ban") def test_gatekeeper_overlay_absent_when_role_select(self): self.client.force_login(self.founder) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertNotContains(response, "gate-overlay") def test_six_table_seats_rendered(self): self.client.force_login(self.founder) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, "table-seat", count=6) def test_active_table_seat_has_active_class(self): self.client.force_login(self.founder) # slot 1 is active response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, 'class="table-seat active"') def test_inactive_table_seat_lacks_active_class(self): self.client.force_login(self.founder) response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) # Slots 2–6 are not active, so at least one plain table-seat exists self.assertContains(response, 'class="table-seat"') def test_card_stack_has_data_user_slots_for_eligible_gamer(self): self.client.force_login(self.founder) # founder is slot 1 only response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, 'data-user-slots="1"') def test_card_stack_has_data_user_slots_for_ineligible_gamer(self): self.client.force_login(self.gamers[1]) # slot 2 gamer response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, 'data-user-slots="2"') class PickRolesViewTest(TestCase): def setUp(self): self.founder = User.objects.create(email="founder@test.io") self.client.force_login(self.founder) self.room = Room.objects.create(name="Test Room", owner=self.founder) for i in range(1, 7): gamer = self.founder if i == 1 else 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() self.room.gate_status = Room.OPEN self.room.save() def test_pick_roles_transitions_room_to_role_select(self): self.client.post(reverse("epic:pick_roles", kwargs={"room_id": self.room.id})) self.room.refresh_from_db() self.assertEqual(self.room.table_status, Room.ROLE_SELECT) def test_pick_roles_creates_one_table_seat_per_filled_slot(self): self.client.post(reverse("epic:pick_roles", kwargs={"room_id": self.room.id})) self.assertEqual(TableSeat.objects.filter(room=self.room).count(), 6) def test_pick_roles_table_seats_carry_gamer_and_slot_number(self): self.client.post(reverse("epic:pick_roles", kwargs={"room_id": self.room.id})) seat = TableSeat.objects.get(room=self.room, slot_number=1) self.assertEqual(seat.gamer, self.founder) def test_only_open_room_can_start_role_select(self): self.room.gate_status = Room.GATHERING self.room.save() self.client.post(reverse("epic:pick_roles", kwargs={"room_id": self.room.id})) self.room.refresh_from_db() self.assertIsNone(self.room.table_status) def test_pick_roles_requires_login(self): self.client.logout() response = self.client.post( reverse("epic:pick_roles", kwargs={"room_id": self.room.id}) ) self.assertEqual(response.status_code, 302) self.assertIn("/accounts/login/", response.url) def test_pick_roles_redirects_to_room(self): response = self.client.post( reverse("epic:pick_roles", kwargs={"room_id": self.room.id}) ) self.assertRedirects( response, reverse("epic:gatekeeper", args=[self.room.id]) ) def test_pick_roles_notifies_channel_layer(self): with patch("apps.epic.views._notify_role_select_start") as mock_notify: self.client.post(reverse("epic:pick_roles", kwargs={"room_id": self.room.id})) mock_notify.assert_called_once_with(self.room.id) def test_pick_roles_idempotent_no_duplicate_seats(self): url = reverse("epic:pick_roles", kwargs={"room_id": self.room.id}) self.client.post(url) self.client.post(url) # second call must be a no-op self.assertEqual(TableSeat.objects.filter(room=self.room).count(), 6) class SelectRoleViewTest(TestCase): def setUp(self): self.founder = User.objects.create(email="founder@test.io") self.room = Room.objects.create(name="Test Room", owner=self.founder) self.gamers = [self.founder] for i in range(2, 7): self.gamers.append(User.objects.create(email=f"g{i}@test.io")) for i, gamer in enumerate(self.gamers, start=1): slot = self.room.gate_slots.get(slot_number=i) slot.gamer = gamer slot.status = GateSlot.FILLED slot.save() self.room.gate_status = Room.OPEN self.room.table_status = Room.ROLE_SELECT self.room.save() for i, gamer in enumerate(self.gamers, start=1): TableSeat.objects.create(room=self.room, gamer=gamer, slot_number=i) self.client.force_login(self.founder) def test_select_role_records_choice(self): self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "PC"}, ) seat = TableSeat.objects.get(room=self.room, slot_number=1) self.assertEqual(seat.role, "PC") def test_select_role_wrong_turn_makes_no_change(self): self.client.force_login(self.gamers[1]) # slot 2 — not their turn self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "BC"}, ) seat = TableSeat.objects.get(room=self.room, slot_number=2) self.assertIsNone(seat.role) def test_turn_advances_after_selection(self): self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "PC"}, ) next_active = TableSeat.objects.filter( room=self.room, role__isnull=True ).order_by("slot_number").first() self.assertEqual(next_active.slot_number, 2) def test_all_selected_sets_sig_select(self): roles = ["PC", "BC", "SC", "AC", "NC"] for i, role in enumerate(roles): seat = TableSeat.objects.get(room=self.room, slot_number=i + 1) seat.role = role seat.save() self.client.force_login(self.gamers[5]) # slot 6 — last self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "EC"}, ) self.room.refresh_from_db() self.assertEqual(self.room.table_status, Room.SIG_SELECT) def test_select_role_notifies_turn_changed(self): with patch("apps.epic.views._notify_turn_changed") as mock_notify: self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "PC"}, ) mock_notify.assert_called_once_with(self.room.id) def test_select_role_notifies_roles_revealed_when_last(self): roles = ["PC", "BC", "SC", "AC", "NC"] for i, role in enumerate(roles): seat = TableSeat.objects.get(room=self.room, slot_number=i + 1) seat.role = role seat.save() self.client.force_login(self.gamers[5]) with patch("apps.epic.views._notify_roles_revealed") as mock_notify: self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "EC"}, ) mock_notify.assert_called_once_with(self.room.id) def test_select_role_requires_login(self): self.client.logout() response = self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "PC"}, ) self.assertEqual(response.status_code, 302) self.assertIn("/accounts/login/", response.url) def test_select_role_returns_ok(self): response = self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "PC"}, ) self.assertEqual(response.status_code, 200) def test_select_role_returns_409_for_duplicate_role(self): TableSeat.objects.filter(room=self.room, slot_number=2).update(role="BC") response = self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "BC"}, ) self.assertEqual(response.status_code, 409) def test_select_role_redirects_when_not_role_select_phase(self): self.room.table_status = None self.room.save() response = self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "PC"}, ) self.assertRedirects( response, reverse("epic:gatekeeper", args=[self.room.id]) ) def test_select_role_redirects_for_invalid_role_code(self): response = self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "BOGUS"}, ) self.assertRedirects( response, reverse("epic:gatekeeper", args=[self.room.id]) ) def test_same_gamer_cannot_double_pick_sequentially(self): """A second POST from the active gamer — after their role has been saved — must redirect rather than assign a second role.""" self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "PC"}, ) response = self.client.post( reverse("epic:select_role", kwargs={"room_id": self.room.id}), data={"role": "BC"}, ) self.assertRedirects( response, reverse("epic:gatekeeper", args=[self.room.id]) ) self.assertEqual( TableSeat.objects.filter(room=self.room, role__isnull=False).count(), 1 ) class RevealPhaseRenderingTest(TestCase): def setUp(self): self.founder = User.objects.create(email="founder@test.io") self.room = Room.objects.create(name="Test Room", owner=self.founder) gamers = [self.founder] for i in range(2, 7): gamers.append(User.objects.create(email=f"g{i}@test.io")) roles = ["PC", "BC", "SC", "AC", "NC", "EC"] for i, (gamer, role) in enumerate(zip(gamers, roles), start=1): TableSeat.objects.create( room=self.room, gamer=gamer, slot_number=i, role=role, role_revealed=True, ) self.room.gate_status = Room.OPEN self.room.table_status = Room.SIG_SELECT self.room.save() self.client.force_login(self.founder) def test_face_up_role_cards_rendered_when_sig_select(self): response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, "face-up") def test_inv_role_card_slot_present(self): response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, "id_inv_role_card") def test_partner_indicator_present_when_sig_select(self): response = self.client.get( reverse("epic:gatekeeper", kwargs={"room_id": self.room.id}) ) self.assertContains(response, "partner-indicator") class RoomActionsViewTest(TestCase): def setUp(self): self.owner = User.objects.create(email="owner@test.io") self.gamer = User.objects.create(email="gamer@test.io") self.room = Room.objects.create(name="Test Room", owner=self.owner) self.slot = self.room.gate_slots.get(slot_number=2) self.slot.gamer = self.gamer self.slot.status = "FILLED" self.slot.save() RoomInvite.objects.create( room=self.room, inviter=self.owner, invitee_email=self.gamer.email ) def test_owner_delete_removes_room(self): self.client.force_login(self.owner) self.client.post(reverse("epic:delete_room", kwargs={"room_id": self.room.id})) self.assertFalse(Room.objects.filter(pk=self.room.pk).exists()) def test_non_owner_delete_does_not_remove_room(self): self.client.force_login(self.gamer) self.client.post(reverse("epic:delete_room", kwargs={"room_id": self.room.id})) self.assertTrue(Room.objects.filter(pk=self.room.pk).exists()) def test_delete_redirects_to_gameboard(self): self.client.force_login(self.owner) response = self.client.post( reverse("epic:delete_room", kwargs={"room_id": self.room.id}) ) self.assertRedirects(response, "/gameboard/") def test_abandon_clears_slot(self): self.client.force_login(self.gamer) self.client.post(reverse("epic:abandon_room", kwargs={"room_id": self.room.id})) self.slot.refresh_from_db() self.assertEqual(self.slot.status, "EMPTY") self.assertIsNone(self.slot.gamer) def test_abandon_deletes_pending_invite(self): self.client.force_login(self.gamer) self.client.post(reverse("epic:abandon_room", kwargs={"room_id": self.room.id})) self.assertFalse( RoomInvite.objects.filter( room=self.room, invitee_email=self.gamer.email ).exists() ) def test_abandon_redirects_to_gameboard(self): self.client.force_login(self.gamer) response = self.client.post( reverse("epic:abandon_room", kwargs={"room_id": self.room.id}) ) self.assertRedirects(response, "/gameboard/") class ReleaseSlotViewTest(TestCase): def setUp(self): self.gamer = User.objects.create(email="gamer@test.io") self.client.force_login(self.gamer) owner = User.objects.create(email="owner@test.io") self.room = Room.objects.create(name="Test Room", owner=owner) self.slot = self.room.gate_slots.get(slot_number=1) self.slot.gamer = self.gamer self.slot.status = GateSlot.FILLED self.slot.debited_token_type = Token.CARTE self.slot.save() def test_release_slot_downgrades_open_room_to_gathering(self): self.room.gate_status = Room.OPEN self.room.save() self.client.post( reverse("epic:release_slot", kwargs={"room_id": self.room.id}), data={"slot_number": self.slot.slot_number}, ) self.room.refresh_from_db() self.assertEqual(self.room.gate_status, Room.GATHERING)