refactor: extract TIER_NAMES/TIER_COLORS to shared constants module (#146)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 12s

Closes #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-04-08 00:03:46 -05:00
parent 224250b03d
commit 21bad7af51
5 changed files with 68 additions and 62 deletions

View File

@ -60,6 +60,7 @@ from helpers import (
)
from utilities.buttons import ask_with_buttons
from utilities.autocomplete import cardset_autocomplete, player_autocomplete
from helpers.refractor_constants import TIER_NAMES as REFRACTOR_TIER_NAMES
logger = logging.getLogger("discord_app")
@ -471,15 +472,6 @@ def get_record_embed(team: dict, results: dict, league: str):
return embed
REFRACTOR_TIER_NAMES = {
0: "Base Card",
1: "Base Chrome",
2: "Refractor",
3: "Gold Refractor",
4: "Superfractor",
}
async def _build_refractor_response(
player_name: str,
player_id: int,

View File

@ -21,19 +21,12 @@ from discord.ext import commands
from api_calls import db_get
from helpers.discord_utils import get_team_embed
from helpers.main import get_team_by_owner
from helpers.refractor_constants import TIER_NAMES, STATUS_TIER_COLORS as TIER_COLORS
logger = logging.getLogger("discord_app")
PAGE_SIZE = 10
TIER_NAMES = {
0: "Base Card",
1: "Base Chrome",
2: "Refractor",
3: "Gold Refractor",
4: "Superfractor",
}
# Tier-specific labels for the status display.
TIER_SYMBOLS = {
0: "Base", # Base Card — used in summary only, not in per-card display
@ -45,15 +38,6 @@ TIER_SYMBOLS = {
_FULL_BAR = "" * 12
# Embed accent colors per tier (used for single-tier filtered views).
TIER_COLORS = {
0: 0x95A5A6, # slate grey
1: 0xBDC3C7, # silver/chrome
2: 0x3498DB, # refractor blue
3: 0xF1C40F, # gold
4: 0x1ABC9C, # teal superfractor
}
def render_progress_bar(current: int, threshold: int, width: int = 12) -> str:
"""

View File

@ -0,0 +1,36 @@
"""
Shared Refractor Constants
Single source of truth for tier names and colors used across the refractor
system. All consumers (status view, notifications, player view) import from
here to prevent silent divergence.
"""
# Human-readable display names for each tier number.
TIER_NAMES = {
0: "Base Card",
1: "Base Chrome",
2: "Refractor",
3: "Gold Refractor",
4: "Superfractor",
}
# Embed accent colors for the /refractor status view.
# These use muted/metallic tones suited to a card-list display.
STATUS_TIER_COLORS = {
0: 0x95A5A6, # slate grey
1: 0xBDC3C7, # silver/chrome
2: 0x3498DB, # refractor blue
3: 0xF1C40F, # gold
4: 0x1ABC9C, # teal superfractor
}
# Embed accent colors for tier-up notification embeds.
# These use brighter/more celebratory tones to signal a milestone event.
# T2 is gold (not blue) to feel like an achievement unlock, not a status indicator.
NOTIF_TIER_COLORS = {
1: 0x2ECC71, # green
2: 0xF1C40F, # gold
3: 0x9B59B6, # purple
4: 0x1ABC9C, # teal (superfractor)
}

View File

@ -12,25 +12,10 @@ import logging
import discord
from helpers.refractor_constants import TIER_NAMES, NOTIF_TIER_COLORS as TIER_COLORS
logger = logging.getLogger("discord_app")
# Human-readable display names for each tier number.
TIER_NAMES = {
0: "Base Card",
1: "Base Chrome",
2: "Refractor",
3: "Gold Refractor",
4: "Superfractor",
}
# Tier-specific embed colors.
TIER_COLORS = {
1: 0x2ECC71, # green
2: 0xF1C40F, # gold
3: 0x9B59B6, # purple
4: 0x1ABC9C, # teal (superfractor)
}
FOOTER_TEXT = "Paper Dynasty Refractor"

View File

@ -415,47 +415,51 @@ def mock_interaction():
class TestTierNamesDivergenceCheck:
"""
T1-6: Assert that TIER_NAMES in cogs.refractor and helpers.refractor_notifs
are identical (same keys, same values).
T1-6: Assert that TIER_NAMES in all three consumers (cogs.refractor,
helpers.refractor_notifs, cogs.players) is identical.
Why: TIER_NAMES is duplicated in two modules. If one is updated and the
other is not (e.g. a tier is renamed or a new tier is added), tier labels
in the /refractor status embed and the tier-up notification embed will
diverge silently. This test acts as a divergence tripwire it will fail
the moment the two copies fall out of sync, forcing an explicit fix.
All three consumers now import from helpers.refractor_constants, so this
test acts as a tripwire against accidental re-localization of the constant.
If any consumer re-declares a local copy that diverges, these tests will
catch it.
"""
def test_tier_names_are_identical_across_modules(self):
"""
Import TIER_NAMES from both modules and assert deep equality.
Import TIER_NAMES from all three consumers and assert deep equality.
The test imports the name at call-time rather than at module level to
ensure it always reads the current definition and is not affected by
module-level caching or monkeypatching in other tests.
The test imports at call-time rather than module level to ensure it
always reads the current definition and is not affected by caching or
monkeypatching in other tests.
"""
from cogs.refractor import TIER_NAMES as cog_tier_names
from helpers.refractor_notifs import TIER_NAMES as notifs_tier_names
from helpers.refractor_constants import TIER_NAMES as constants_tier_names
assert cog_tier_names == notifs_tier_names, (
"TIER_NAMES differs between cogs.refractor and helpers.refractor_notifs. "
"Both copies must be kept in sync. "
assert cog_tier_names == notifs_tier_names == constants_tier_names, (
"TIER_NAMES differs across consumers. "
f"cogs.refractor: {cog_tier_names!r} "
f"helpers.refractor_notifs: {notifs_tier_names!r}"
f"helpers.refractor_notifs: {notifs_tier_names!r} "
f"helpers.refractor_constants: {constants_tier_names!r}"
)
def test_tier_names_have_same_keys(self):
"""Keys (tier numbers) must be identical in both modules."""
"""Keys (tier numbers) must be identical in all consumers."""
from cogs.refractor import TIER_NAMES as cog_tier_names
from helpers.refractor_notifs import TIER_NAMES as notifs_tier_names
from cogs.players import REFRACTOR_TIER_NAMES
assert set(cog_tier_names.keys()) == set(notifs_tier_names.keys()), (
"TIER_NAMES key sets differ between modules."
)
assert (
set(cog_tier_names.keys())
== set(notifs_tier_names.keys())
== set(REFRACTOR_TIER_NAMES.keys())
), "TIER_NAMES key sets differ between consumers."
def test_tier_names_have_same_values(self):
"""Display strings (values) must be identical for every shared key."""
from cogs.refractor import TIER_NAMES as cog_tier_names
from helpers.refractor_notifs import TIER_NAMES as notifs_tier_names
from cogs.players import REFRACTOR_TIER_NAMES
for tier, name in cog_tier_names.items():
assert notifs_tier_names.get(tier) == name, (
@ -463,6 +467,11 @@ class TestTierNamesDivergenceCheck:
f"cogs.refractor={name!r}, "
f"helpers.refractor_notifs={notifs_tier_names.get(tier)!r}"
)
assert REFRACTOR_TIER_NAMES.get(tier) == name, (
f"Tier {tier} name mismatch: "
f"cogs.refractor={name!r}, "
f"cogs.players.REFRACTOR_TIER_NAMES={REFRACTOR_TIER_NAMES.get(tier)!r}"
)
# ---------------------------------------------------------------------------