new model fields & migrations for apps.epic & apps.lyric; new FTs, ITs & UTs passing

; some styling changes effected primarily to _gatekeetper.html modal
This commit is contained in:
Disco DeDisco
2026-03-14 22:00:16 -04:00
parent 26b6d4e7db
commit 4baaa63430
13 changed files with 410 additions and 28 deletions

View File

@@ -2,9 +2,10 @@ 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
from apps.epic.models import GateSlot, Room, RoomInvite, debit_token, select_token
class RoomCreationTest(TestCase):
@@ -77,6 +78,60 @@ class CoinTokenInUseTest(TestCase):
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")

View File

@@ -229,6 +229,81 @@ class RejectTokenViewTest(TestCase):
)
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 RoomActionsViewTest(TestCase):
def setUp(self):
self.owner = User.objects.create(email="owner@test.io")