+52 IT/UT to close IT/UT-only coverage gaps (93% → 96%) — full suite 983 tests in 47s ; UTs in epic/tests/unit/test_models.py — TarotCardEmanationForTest (4) covers emanation_for(polarity) w. levity/gravity overrides + fallback to name_title for cards w.o a polarity split (cards 48-49 are the only polarity-split cards in the deck so this method is sparsely exercised by ITs); TarotCardReversalForTest (4) covers reversal_for(polarity) w. polarity-split + reversal_qualifier fallback + further fallthrough to emanation_for; TarotCardNameSplitTest (4) covers name_group/name_title colon-split parsing (prefix-w-colon / suffix / no-colon edge); TarotCardCautionsJsonTest (2) covers the cautions_json JSON serialiser ; UTs in epic/tests/unit/test_utils.py — PlanetHouseFallbackTest +1 happy-path test (degree=15 lands in house 1 w. sequential cusps) for the normal cusp-match branch alongside the existing pathological fallback test; TopCapacitorsTest (6) covers all top_capacitors() branches — empty dict / None / all-zero counts (the L56 max(counts.values()) <= 0 fallback that was uncovered) / single-winner / tie-clockwise-order / enriched dict {"count":N} input shape ; ITs in epic/tests/integrated/test_models.py — TarotDeckDrawTest extended w. 5 tests for remaining_count (happy + no-deck-variant fallback to 0) + draw() happy-path (returns n tuples of (TarotCard, bool) / appends to drawn_card_ids / never repeats cards across consecutive draws); existing ValueError + shuffle tests preserved ; ITs in epic/tests/integrated/test_views.py — SigEventRetractionTest (4 tests) covers the three data["retracted"] = True paths that the FT test_game_room_select_sig.py walks transitively but no IT pins directly: sig_unready retracts prior SIG_READY (L937), sig_ready retracts prior SIG_UNREADY (L907), sig_reserve action=release while ready retracts prior SIG_READY + records fresh SIG_UNREADY (L823); SigReserveInvalidCardIdTest (1) covers TarotCard.DoesNotExist → 400 (L840-841) ; SigSelectGravityContextTest (3) covers the user_polarity = 'gravity' branch (L322) + the gravity_sig_cards lookup (L357) — all existing SIG_SELECT context tests use the founder-as-PC-levity setup so these branches sat uncovered; logs in as gamers[5] (BC role) + asserts user_polarity + sig_cards match gravity_sig_cards() output ; SeaDeckViewTest (7) mirrors the test_game_room_select_sea.py FT but isolates the JSON contract — covers 403 when unseated, empty halves when seat has no deck_variant (L1255-1256 early-out), two-halves shape, ~even split, card_dict keys (id/name/arcana/corner_rank/suit_icon/name_group/name_title/reversed/qualifiers), reversed field is bool, claimed-significator exclusion via room.table_seats.exclude(significator__isnull=True) ; ITs in dashboard/tests/integrated/test_views.py — ProfileViewTest +2 (reserved-handle "adman" rejection — L116-117: username stays unchanged + redirect to /); KitBagViewTest (3) covers the kit_bag view's panel render w. TITHE-sort branch (L169-175) + login guard ; ITs in dashboard/tests/integrated/test_sky_views.py — SkyViewTest +2 (saved birth datetime renders in user's sky_birth_tz via astimezone L300-306 — 16:00 UTC → 12:00 EDT; invalid-tz string triggers ZoneInfoNotFoundError → swallowed pass → UTC fallback at 16:00) ; ITs in gameboard/tests/integrated/test_views.py — EquipTrinketViewTest +2 (POST equips trinket + returns 204 — L83-85; non-owner POST returns 404 via get_object_or_404); UnequipTrinketViewTest +2 (POST clears matching equipped_trinket — L107-110; POST of non-matching token is a 204 no-op, the implicit else branch) ; .coveragerc omit gains */reset_staging_db.py per user — mgmt cmd was the only 0%-stmt module that wasn't exercised by tests at all + we agreed it's deliberately untested staging-side code ; palette-monochrome-dark rebalance in rootvars.scss — --quiUser/--sixUser/--sepUser remapped to (secAg / quaAg / priPt) instead of (quaAg / terAg / secAg), shifting the secondary/subtle/deep-subtle anchors up the silver gradient so the palette reads more cleanly under the new sig-stage card colours from 3242873 ; uncovered remnants from earlier analysis intentionally left in place — consumers.py at 68% (channels-tag tests excluded; would need --tag=channels run), Carte Blanche slot navigation + sky_dice + tarot_deck preview view paths (the "bigger investments" tier from session triage; FT-covered + the IT setup is heavier than the immediate value), defensive except fallbacks that need contrived inputs to fire, and a handful of __str__s/pass branches not worth a test apiece — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -235,6 +235,7 @@ class UnequipTrinketViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="gamer@test.io")
|
||||
self.client.force_login(self.user)
|
||||
self.token = Token.objects.filter(user=self.user, token_type=Token.COIN).first()
|
||||
|
||||
def test_get_returns_405(self):
|
||||
from apps.lyric.models import Token
|
||||
@@ -244,6 +245,30 @@ class UnequipTrinketViewTest(TestCase):
|
||||
response = self.client.get(reverse("unequip_trinket", kwargs={"token_id": token.pk}))
|
||||
self.assertEqual(response.status_code, 405)
|
||||
|
||||
def test_post_clears_equipped_trinket_when_matching(self):
|
||||
self.user.equipped_trinket = self.token
|
||||
self.user.save(update_fields=["equipped_trinket"])
|
||||
response = self.client.post(
|
||||
reverse("unequip_trinket", kwargs={"token_id": self.token.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.user.refresh_from_db()
|
||||
self.assertIsNone(self.user.equipped_trinket)
|
||||
|
||||
def test_post_ignores_non_matching_trinket(self):
|
||||
"""POSTing a token that's not the currently-equipped one is a 204 no-op
|
||||
— equipped_trinket is unchanged. Covers the implicit `else` of the
|
||||
`if request.user.equipped_trinket_id == token.pk` branch."""
|
||||
other_token = Token.objects.create(user=self.user, token_type=Token.TITHE)
|
||||
self.user.equipped_trinket = self.token # COIN is equipped
|
||||
self.user.save(update_fields=["equipped_trinket"])
|
||||
response = self.client.post(
|
||||
reverse("unequip_trinket", kwargs={"token_id": other_token.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.equipped_trinket, self.token)
|
||||
|
||||
|
||||
class GameKitViewTest(TestCase):
|
||||
def setUp(self):
|
||||
@@ -383,6 +408,23 @@ class EquipTrinketViewTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "apps/gameboard/_partials/_equip_trinket_btn.html")
|
||||
|
||||
def test_post_equips_trinket_and_returns_204(self):
|
||||
response = self.client.post(
|
||||
reverse("equip_trinket", kwargs={"token_id": self.token.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.equipped_trinket, self.token)
|
||||
|
||||
def test_post_requires_token_owner(self):
|
||||
outsider = User.objects.create(email="outsider@test.io")
|
||||
self.client.force_login(outsider)
|
||||
response = self.client.post(
|
||||
reverse("equip_trinket", kwargs={"token_id": self.token.pk})
|
||||
)
|
||||
# get_object_or_404 — the token belongs to self.user, not outsider
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
class TarotFanViewTest(TestCase):
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user