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

@@ -1,13 +1,13 @@
import time
from datetime import timedelta
from django.utils import timezone
from selenium.webdriver.common.by import By
from .base import FunctionalTest
from apps.applets.models import Applet
from apps.epic.models import Room, GateSlot
from apps.lyric.models import User
from apps.epic.models import Room, GateSlot, select_token
from apps.lyric.models import Token, User
class GatekeeperTest(FunctionalTest):
@@ -39,7 +39,7 @@ class GatekeeperTest(FunctionalTest):
)
# 4. Page shows room name, GATHERING status
body = self.browser.find_element(By.TAG_NAME, "body")
self.assertIn("Test Room", body.text)
self.assertIn("TEST ROOM", body.text)
self.assertIn("GATHERING GAMERS", body.text)
# 5. Six token slot circles are visible, all empty
slots = self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot")
@@ -82,7 +82,7 @@ class GatekeeperTest(FunctionalTest):
slots = self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot")
self.assertIn("filled", slots[0].get_attribute("class"))
self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".btn-confirm")), 0
len(self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot .btn-confirm")), 0
)
def test_room_appears_in_my_games_after_creation(self):
@@ -308,7 +308,7 @@ class CoinSlotTest(FunctionalTest):
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".btn-confirm")), 0
len(self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot .btn-confirm")), 0
)
slot = self.room.gate_slots.get(slot_number=1)
slot.refresh_from_db()
@@ -374,5 +374,130 @@ class CoinSlotTest(FunctionalTest):
)
)
self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".btn-confirm")), 0
len(self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot .btn-confirm")), 0
)
class TokenPriorityTest(FunctionalTest):
def setUp(self):
super().setUp()
Applet.objects.get_or_create(
slug="new-game", defaults={"name": "New Game", "context": "gameboard"}
)
Applet.objects.get_or_create(
slug="my-games", defaults={"name": "My Games", "context": "gameboard"}
)
self.create_pre_authenticated_session("gamer@test.io")
self.gamer = User.objects.get(email="gamer@test.io")
self.room = Room.objects.create(name="Token Room", owner=self.gamer)
self.gate_url = self.live_server_url + f"/gameboard/room/{self.room.id}/gate/"
self.coin = Token.objects.get(user=self.gamer, token_type=Token.COIN)
def test_coin_is_used_by_default(self):
# 1. COIN token created at signup, not yet leased to a room
self.assertEqual(self.coin.token_type, Token.COIN)
self.assertIsNone(self.coin.current_room)
# 2. Gamer drops token and confirms
self.browser.get(self.gate_url)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, "button.token-rails")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".btn-confirm")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
# 3. Coin is now leased to this room, page not refreshed
self.assertEqual(self.browser.current_url, self.gate_url)
self.coin.refresh_from_db()
self.assertEqual(self.coin.current_room, self.room)
def test_free_token_used_when_coin_in_use(self):
# 1. Coin already leased to another room
other_room = Room.objects.create(name="Other Room", owner=self.gamer)
self.coin.current_room = other_room
self.coin.save()
# 2. Gamer has one unexpired free token (signup gives one; delete it and add fresh)
Token.objects.filter(user=self.gamer, token_type=Token.FREE).delete()
Token.objects.create(
user=self.gamer,
token_type=Token.FREE,
expires_at=timezone.now() + timedelta(days=7),
)
# 3. Gamer drops token → Free Token consumed
self.browser.get(self.gate_url)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, "button.token-rails")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".btn-confirm")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
self.assertEqual(
Token.objects.filter(user=self.gamer, token_type=Token.FREE).count(), 0
)
# 4. Coin untouched, still leased to other room
self.assertEqual(self.browser.current_url, self.gate_url)
self.coin.refresh_from_db()
self.assertEqual(self.coin.current_room, other_room)
def test_tithe_token_used_when_free_tokens_exhausted(self):
# 1. Coin in use, no Free Tokens, one Tithe Token
other_room = Room.objects.create(name="Other Room", owner=self.gamer)
self.coin.current_room = 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)
# 2. Gamer drops token → tithe consumed
self.browser.get(self.gate_url)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, "button.token-rails")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".btn-confirm")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
# Tithe row deleted, page hasn't refreshed
self.assertEqual(self.browser.current_url, self.gate_url)
self.assertFalse(Token.objects.filter(pk=tithe.pk).exists())
def test_slot_blocked_when_no_tokens_available(self):
# Coin in use, no Free Tokens, no Tithe Tokens → depleted state
other_room = Room.objects.create(name="Other Room", owner=self.gamer)
self.coin.current_room = other_room
self.coin.save()
Token.objects.filter(user=self.gamer, token_type=Token.FREE).delete()
self.browser.get(self.gate_url)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".token-slot.depleted")
)
self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, "button.token-rails")), 0
)
def test_staff_backstage_pass_bypasses_token_cost(self):
# 1. Staff user has a PASS token
self.gamer.is_staff = True
self.gamer.save()
pass_token = Token.objects.create(user=self.gamer, token_type=Token.PASS)
# 2. Drops token, confirms as normal
self.browser.get(self.gate_url)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, "button.token-rails")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".btn-confirm")
).click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
# 3. Pass not consumed, coin not leased; no reload
self.assertEqual(self.browser.current_url, self.gate_url)
self.assertTrue(Token.objects.filter(pk=pass_token.pk).exists())
self.coin.refresh_from_db()
self.assertIsNone(self.coin.current_room)