Files
python-tdd/src/apps/lyric/tests/integrated/test_models.py

342 lines
13 KiB
Python
Raw Normal View History

import uuid
from django.contrib import auth
from django.test import TestCase
from django.utils import timezone
from apps.drama.models import Note
from apps.lyric.models import LoginToken, PaymentMethod, Token, User, Wallet
class UserModelTest(TestCase):
def test_model_is_configured_for_django_auth(self):
self.assertEqual(auth.get_user_model(), User)
def test_user_is_valid_with_email_only(self):
user = User(email="a@b.cde")
user.set_unusable_password()
user.full_clean() # should not raise
def test_id_is_primary_key(self):
user = User(id="123")
self.assertEqual(user.pk, "123")
def test_user_can_have_a_username(self):
user = User.objects.create(email="a@b.cde")
user.username = "stardust"
user.save()
self.assertEqual(User.objects.get(pk=user.pk).username, "stardust")
def test_searchable_defaults_to_false(self):
user = User.objects.create(email="a@b.cde")
self.assertFalse(user.searchable)
def test_active_title_is_null_by_default(self):
user = User.objects.create(email="a@b.cde")
self.assertIsNone(user.active_title)
def test_active_title_can_be_set_to_a_note(self):
user = User.objects.create(email="a@b.cde")
note = Note.objects.create(user=user, slug="stargazer", earned_at=timezone.now())
user.active_title = note
user.save(update_fields=["active_title"])
user.refresh_from_db()
self.assertEqual(user.active_title, note)
def test_clearing_note_nullifies_active_title(self):
user = User.objects.create(email="a@b.cde")
note = Note.objects.create(user=user, slug="stargazer", earned_at=timezone.now())
user.active_title = note
user.save(update_fields=["active_title"])
note.delete()
user.refresh_from_db()
self.assertIsNone(user.active_title)
class LoginTokenModelTest(TestCase):
def test_links_user_with_autogen_uid(self):
login_token1 = LoginToken.objects.create(email="a@b.cde")
login_token2 = LoginToken.objects.create(email="v@w.xyz")
self.assertNotEqual(login_token1.pk, login_token2.pk)
self.assertIsInstance(login_token1.pk, uuid.UUID)
class UserManagerTest(TestCase):
def test_create_superuser_sets_is_staff_and_is_superuser(self):
user = User.objects.create_superuser(
email="admin@example.com",
password="correct-password",
)
self.assertTrue(user.is_staff)
self.assertTrue(user.is_superuser)
def test_create_superuser_sets_usable_password(self):
user = User.objects.create_superuser(
email="admin@example.com",
password="correct-password",
)
self.assertTrue(user.check_password("correct-password"))
class UserPaletteTest(TestCase):
def test_palette_field_defaults_to_palette_default(self):
user = User.objects.create(email="a@b.cde")
self.assertEqual(user.palette, "palette-default")
pronouns: per-user pronouns ideology + Pronouns applet on Game Kit; provenance prose uses actor.pronouns at render time — TDD - User.pronouns CharField w. choices=[pluralism (default), bawlmorese, misogyny, misandry, misanthropy] + pronoun_subj/obj/poss properties; PRONOUN_TABLE single source of truth in apps.lyric.models; mig 0002_user_pronouns - drama.GameEvent.to_prose() drops module-level PRONOUN_* constants; SIG_READY/SIG_UNREADY/ROLE_SELECTED now resolve poss/subj from self.actor.pronouns at render time, so flipping a user's preference rewrites all their existing scroll prose; default actor → "their" - SIG_READY prose strips a leading "The " from card_name so "the The Wanderer" reads "the Wanderer" and "the Engraven The Nomad" reads "the Engraven Nomad"; minor arcana ("Maid of Brands") untouched - new applets/0005 seeds 'pronouns' applet (3x3, game-kit, default visible); _game_kit_sections.html grows a #id_gk_pronouns block w. 5 .gk-pronoun-card items labeled by ideology slug (italic) and tagged data-pronoun + data-trio - card click → window.showGuard(card, "Set pronoun preference?<span class='guard-pronoun-trio'>{trio}</span>", commitCb); on OK fetches POST /dashboard/set-pronouns w. CSRF cookie + reloads so .active class moves and provenance prose re-renders; NVM dismisses - dashboard.set_pronouns view (POST-only, login_required, 204/400/405) at /dashboard/set-pronouns; rejects choices not in PRONOUN_TABLE - _game-kit.scss extends shared card rule to .gk-pronoun-card w. .active fill state + italic ideology label; #id_guard_portal .guard-pronoun-trio styled small/dim/centered under the question - billscroll aperture: padding-right 0.75rem on #id_drama_scroll inside .applet-scroll so the timestamp column no longer sits beneath the scrollbar Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:11:40 -04:00
class UserPronounsTest(TestCase):
def test_pronouns_default_to_pluralism(self):
user = User.objects.create(email="they@test.io")
self.assertEqual(user.pronouns, "pluralism")
def test_pronouns_choices_include_all_five_options(self):
from apps.lyric.models import PRONOUN_CHOICES
keys = [k for (k, _) in PRONOUN_CHOICES]
self.assertEqual(
keys,
["pluralism", "bawlmorese", "misogyny", "misandry", "misanthropy"],
)
def test_pluralism_resolves_to_they_them_their(self):
user = User.objects.create(email="plural@test.io")
self.assertEqual(user.pronoun_subj, "they")
self.assertEqual(user.pronoun_obj, "them")
self.assertEqual(user.pronoun_poss, "their")
def test_bawlmorese_resolves_to_yo_yo_yos(self):
user = User.objects.create(email="yo@test.io", pronouns="bawlmorese")
self.assertEqual(user.pronoun_subj, "yo")
self.assertEqual(user.pronoun_obj, "yo")
self.assertEqual(user.pronoun_poss, "yos")
def test_misogyny_resolves_to_he_him_his(self):
user = User.objects.create(email="he@test.io", pronouns="misogyny")
self.assertEqual(user.pronoun_subj, "he")
self.assertEqual(user.pronoun_obj, "him")
self.assertEqual(user.pronoun_poss, "his")
def test_misandry_resolves_to_she_her_hers(self):
user = User.objects.create(email="she@test.io", pronouns="misandry")
self.assertEqual(user.pronoun_subj, "she")
self.assertEqual(user.pronoun_obj, "her")
self.assertEqual(user.pronoun_poss, "hers")
def test_misanthropy_resolves_to_it_it_its(self):
user = User.objects.create(email="it@test.io", pronouns="misanthropy")
self.assertEqual(user.pronoun_subj, "it")
self.assertEqual(user.pronoun_obj, "it")
self.assertEqual(user.pronoun_poss, "its")
class WalletCreationTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="capman@test.io")
def test_wallet_is_created_for_new_user(self):
self.assertTrue(Wallet.objects.filter(user=self.user).exists())
def test_new_wallet_has_144_writs(self):
wallet = Wallet.objects.get(user = self.user)
self.assertEqual(wallet.writs, 144)
def test_new_wallet_has_0_esteem(self):
wallet = Wallet.objects.get(user=self.user)
self.assertEqual(wallet.esteem, 0)
class TokenCreationTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="capman@test.io")
def test_coin_on_a_string_created_for_new_user(self):
self.assertTrue(
Token.objects.filter(user=self.user, token_type=Token.COIN).exists()
)
def test_free_token_created_for_new_user(self):
self.assertTrue(
Token.objects.filter(user=self.user, token_type=Token.FREE).exists()
)
def test_coin_on_a_string_has_no_expiry(self):
coin = Token.objects.get(user=self.user, token_type=Token.COIN)
self.assertIsNone(coin.expires_at)
def test_free_token_has_expiry_within_7_days(self):
free = Token.objects.get(user=self.user, token_type=Token.FREE)
self.assertIsNotNone(free.expires_at)
delta = free.expires_at - timezone.now()
self.assertLessEqual(delta.days, 7)
self.assertGreater(delta.total_seconds(), 0)
def test_no_pass_token_for_regular_user(self):
self.assertFalse(
Token.objects.filter(user=self.user, token_type=Token.PASS).exists()
)
class SuperuserTokenCreationTest(TestCase):
def setUp(self):
self.user = User.objects.create_superuser(
email="admin@test.io", password="secret"
)
def test_pass_token_created_for_superuser(self):
self.assertTrue(
Token.objects.filter(user=self.user, token_type=Token.PASS).exists()
)
def test_superuser_also_gets_coin_and_free_token(self):
self.assertTrue(
Token.objects.filter(user=self.user, token_type=Token.COIN).exists()
)
self.assertTrue(
Token.objects.filter(user=self.user, token_type=Token.FREE).exists()
)
class SuperuserNoteGrantTest(TestCase):
def setUp(self):
self.user = User.objects.create_superuser(
email="admin@test.io", password="secret"
)
def test_superuser_gets_super_schizo_note(self):
from apps.drama.models import Note
self.assertTrue(Note.objects.filter(user=self.user, slug="super-schizo").exists())
def test_superuser_gets_super_nomad_note(self):
from apps.drama.models import Note
self.assertTrue(Note.objects.filter(user=self.user, slug="super-nomad").exists())
def test_regular_user_does_not_get_super_notes(self):
from apps.drama.models import Note
regular = User.objects.create(email="regular@test.io")
self.assertFalse(Note.objects.filter(user=regular, slug="super-schizo").exists())
self.assertFalse(Note.objects.filter(user=regular, slug="super-nomad").exists())
class WalletTooltipTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="wallet@test.io")
self.wallet = Wallet.objects.get(user=self.user)
def test_tooltip_name(self):
self.assertEqual(self.wallet.tooltip_name(), "Wallet")
def test_tooltip_description(self):
self.wallet.writs = 144
self.wallet.esteem = 12
self.assertEqual(self.wallet.tooltip_description(), "144 writs · 12 esteem")
def test_tooltip_shoptalk_returns_none(self):
self.assertIsNone(self.wallet.tooltip_shoptalk())
def test_tooltip_expiry_returns_none(self):
self.assertIsNone(self.wallet.tooltip_expiry())
def test_tooltip_text(self):
self.assertEqual(self.wallet.tooltip_text(), "Wallet: 144 writs · 0 esteem")
class TokenTooltipTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="tokens@test.io")
def test_tithe_tooltip_description(self):
tithe = Token.objects.create(user=self.user, token_type=Token.TITHE)
self.assertEqual(tithe.tooltip_description(), "+ Writ bonus")
def test_tooltip_description_empty_fallback(self):
# token_type other than COIN/FREE/TITHE hits the bare return ""
token = Token(user=self.user, token_type="")
self.assertEqual(token.tooltip_description(), "")
def test_tooltip_expiry_empty_when_no_expiry_and_not_coin(self):
free = Token.objects.get(user=self.user, token_type=Token.FREE)
free.expires_at = None
self.assertEqual(free.tooltip_expiry(), "")
def test_tooltip_shoptalk_none_for_free_coin(self):
free = Token.objects.get(user=self.user, token_type=Token.FREE)
self.assertEqual(free.tooltip_shoptalk(), "a spot of good fortune")
def test_tooltip_room_html_returns_empty_when_no_room(self):
token = Token.objects.get(user=self.user, token_type=Token.COIN)
self.assertEqual(token.tooltip_room_html(), "")
class EquippedTrinketTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="equip@test.io", is_staff=True)
self.pass_token = self.user.tokens.get(token_type=Token.PASS)
def test_normal_user_equipped_trinket_defaults_to_coin(self):
user = User.objects.create(email="noequip@test.io")
coin = user.tokens.get(token_type=Token.COIN)
self.assertEqual(user.equipped_trinket, coin)
def test_staff_user_equipped_trinket_defaults_to_pass(self):
self.assertEqual(self.user.equipped_trinket, self.pass_token)
def test_equipped_trinket_can_be_set_to_pass(self):
self.user.equipped_trinket = self.pass_token
self.user.save(update_fields=["equipped_trinket"])
self.assertEqual(
User.objects.get(pk=self.user.pk).equipped_trinket, self.pass_token
)
def test_equipped_trinket_can_be_set_to_carte(self):
carte = Token.objects.create(user=self.user, token_type=Token.CARTE)
self.user.equipped_trinket = carte
self.user.save(update_fields=["equipped_trinket"])
self.assertEqual(
User.objects.get(pk=self.user.pk).equipped_trinket, carte
)
def test_equipped_trinket_can_be_cleared(self):
self.user.equipped_trinket = self.pass_token
self.user.save(update_fields=["equipped_trinket"])
self.user.equipped_trinket = None
self.user.save(update_fields=["equipped_trinket"])
self.assertIsNone(User.objects.get(pk=self.user.pk).equipped_trinket)
class CarteTokenCreationTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="carte@test.io")
def test_carte_token_can_be_created(self):
token = Token.objects.create(user=self.user, token_type=Token.CARTE)
self.assertEqual(Token.objects.get(pk=token.pk).token_type, Token.CARTE)
def test_carte_has_no_expiry(self):
token = Token.objects.create(user=self.user, token_type=Token.CARTE)
self.assertIsNone(token.expires_at)
class EquippedDeckTest(TestCase):
def test_new_user_gets_earthman_as_default_deck(self):
from apps.epic.models import DeckVariant
earthman = DeckVariant.objects.get(slug="earthman")
user = User.objects.create(email="deck@test.io")
user.refresh_from_db()
self.assertEqual(user.equipped_deck, earthman)
def test_fiorentine_is_not_auto_assigned_to_new_users(self):
from apps.epic.models import DeckVariant
fiorentine = DeckVariant.objects.get(slug="fiorentine-minchiate")
user = User.objects.create(email="deck2@test.io")
user.refresh_from_db()
self.assertNotEqual(user.equipped_deck, fiorentine)
def test_equipped_deck_can_be_switched(self):
from apps.epic.models import DeckVariant
fiorentine = DeckVariant.objects.get(slug="fiorentine-minchiate")
user = User.objects.create(email="deck3@test.io")
user.equipped_deck = fiorentine
user.save(update_fields=["equipped_deck"])
self.assertEqual(User.objects.get(pk=user.pk).equipped_deck, fiorentine)
class PaymentMethodTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="pay@test.io")
def test_str(self):
pm = PaymentMethod(user=self.user, stripe_pm_id="pm_123", last4="4242", brand="Visa")
self.assertEqual(str(pm), "Visa ....4242")