mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-16 23:50:12 +01:00
bugfix: update editorial_governance to use small smaple set of themes instead of looking for the full library
This commit is contained in:
parent
f68f8949e8
commit
7679176414
15 changed files with 716 additions and 9 deletions
41
code/config/themes/catalog/graveyard.yml
Normal file
41
code/config/themes/catalog/graveyard.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
id: graveyard-fixture
|
||||
display_name: Reanimate
|
||||
curated_synergies:
|
||||
- Graveyard Loops
|
||||
- Sacrifice
|
||||
- Big Threats
|
||||
enforced_synergies:
|
||||
- Tutor
|
||||
inferred_synergies:
|
||||
- Recursion
|
||||
synergies:
|
||||
- Graveyard Loops
|
||||
- Sacrifice
|
||||
- Big Threats
|
||||
- Tutor
|
||||
primary_color: Black
|
||||
secondary_color: Blue
|
||||
notes: Fixture entry for editorial governance CI checks.
|
||||
example_commanders:
|
||||
- Meren of Clan Nel Toth
|
||||
- Chainer, Dementia Master
|
||||
- The Scarab God
|
||||
- Sheoldred, Whispering One
|
||||
- Araumi of the Dead Tide
|
||||
example_cards:
|
||||
- Reanimate
|
||||
- Animate Dead
|
||||
- Victimize
|
||||
- Entomb
|
||||
- Buried Alive
|
||||
synergy_commanders:
|
||||
- Gisa and Geralf - Synergy (Graveyard Loops)
|
||||
- Kess, Dissident Mage - Synergy (Tutor)
|
||||
- Syr Konrad, the Grim - Synergy (Sacrifice)
|
||||
deck_archetype: Control
|
||||
popularity_bucket: Common
|
||||
description: Loads high-impact cards into the graveyard early and reanimates them for explosive tempo or combo loops.
|
||||
editorial_quality: reviewed
|
||||
metadata_info:
|
||||
script: build_theme_catalog.py
|
||||
last_backfill: '2024-09-03T00:00:00Z'
|
||||
41
code/config/themes/catalog/tokens.yml
Normal file
41
code/config/themes/catalog/tokens.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
id: tokens-fixture
|
||||
display_name: Tokens Matter
|
||||
curated_synergies:
|
||||
- Go Wide
|
||||
- Anthem
|
||||
- Sacrifice Fodder
|
||||
enforced_synergies:
|
||||
- Card Draw
|
||||
inferred_synergies:
|
||||
- Populate
|
||||
synergies:
|
||||
- Go Wide
|
||||
- Anthem
|
||||
- Sacrifice Fodder
|
||||
- Populate
|
||||
primary_color: White
|
||||
secondary_color: Green
|
||||
notes: Fixture entry to ensure metadata coverage for tests.
|
||||
example_commanders:
|
||||
- Rhys the Redeemed
|
||||
- Trostani, Selesnya's Voice
|
||||
- Adeline, Resplendent Cathar
|
||||
- Jetmir, Nexus of Revels
|
||||
- Kyler, Sigardian Emissary
|
||||
example_cards:
|
||||
- Anointed Procession
|
||||
- Parallel Lives
|
||||
- Heroic Reinforcements
|
||||
- Secure the Wastes
|
||||
- Beastmaster Ascension
|
||||
synergy_commanders:
|
||||
- Chatterfang, Squirrel General - Synergy (Sacrifice Fodder)
|
||||
- Emmara, Soul of the Accord - Synergy (Go Wide)
|
||||
- Tana, the Bloodsower - Synergy (Populate)
|
||||
deck_archetype: Midrange
|
||||
popularity_bucket: Very Common
|
||||
description: Goes wide with creature tokens then converts mass into damage, draw, drain, or sacrifice engines.
|
||||
editorial_quality: reviewed
|
||||
metadata_info:
|
||||
script: build_theme_catalog.py
|
||||
last_backfill: '2024-09-02T00:00:00Z'
|
||||
41
code/config/themes/catalog/treasure.yml
Normal file
41
code/config/themes/catalog/treasure.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
id: treasure-fixture
|
||||
display_name: Treasure
|
||||
curated_synergies:
|
||||
- Tokens
|
||||
- Artifacts
|
||||
- Sacrifice
|
||||
enforced_synergies:
|
||||
- Ramp
|
||||
inferred_synergies:
|
||||
- Combo
|
||||
synergies:
|
||||
- Tokens
|
||||
- Artifacts
|
||||
- Sacrifice
|
||||
- Ramp
|
||||
primary_color: Red
|
||||
secondary_color: Black
|
||||
notes: Sample fixture catalog entry for CI tests.
|
||||
example_commanders:
|
||||
- Prosper, Tome-Bound
|
||||
- Magda, Brazen Outlaw
|
||||
- Kalain, Reclusive Painter
|
||||
- Marionette Master
|
||||
- Jolene, the Plunder Queen
|
||||
example_cards:
|
||||
- Dockside Extortionist
|
||||
- Professional Face-Breaker
|
||||
- Brass's Bounty
|
||||
- Revel in Riches
|
||||
- Pitiless Plunderer
|
||||
synergy_commanders:
|
||||
- Brudiclad, Telchor Engineer - Synergy (Tokens)
|
||||
- Ruthless Technomancer - Synergy (Sacrifice)
|
||||
- Galazeth Prismari - Synergy (Artifacts)
|
||||
deck_archetype: Combo
|
||||
popularity_bucket: Common
|
||||
description: Generates Treasure tokens as flexible ramp and combo fuel enabling explosive payoff turns.
|
||||
editorial_quality: reviewed
|
||||
metadata_info:
|
||||
script: build_theme_catalog.py
|
||||
last_backfill: '2024-09-01T00:00:00Z'
|
||||
40
code/tests/editorial_test_utils.py
Normal file
40
code/tests/editorial_test_utils.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
THEMES_DIR = ROOT / 'config' / 'themes'
|
||||
CATALOG_DIR = THEMES_DIR / 'catalog'
|
||||
FIXTURE_ROOT = Path(__file__).resolve().parent / 'fixtures' / 'editorial_catalog'
|
||||
|
||||
|
||||
def ensure_editorial_fixtures(force: bool | None = None) -> None:
|
||||
"""Populate minimal editorial catalog fixtures when real data is absent.
|
||||
|
||||
The repository intentionally does not track `config/themes/catalog` because the
|
||||
production catalog is generated dynamically. For CI we stage a small curated
|
||||
sample so governance tests can exercise logic without requiring the full data
|
||||
dump. Existing files are left untouched to avoid clobbering local updates.
|
||||
"""
|
||||
if force is None:
|
||||
flag = os.environ.get('EDITORIAL_TEST_USE_FIXTURES', '').strip().lower()
|
||||
force = flag in {'1', 'true', 'yes', 'on'}
|
||||
|
||||
if not FIXTURE_ROOT.exists():
|
||||
return
|
||||
|
||||
catalog_fixture = FIXTURE_ROOT / 'catalog'
|
||||
if catalog_fixture.exists():
|
||||
CATALOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
for src in catalog_fixture.glob('*.yml'):
|
||||
dest = CATALOG_DIR / src.name
|
||||
if force or not dest.exists():
|
||||
shutil.copy(src, dest)
|
||||
|
||||
theme_list_fixture = FIXTURE_ROOT / 'theme_list.json'
|
||||
theme_list_target = THEMES_DIR / 'theme_list.json'
|
||||
if theme_list_fixture.exists() and (force or not theme_list_target.exists()):
|
||||
theme_list_target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy(theme_list_fixture, theme_list_target)
|
||||
41
code/tests/fixtures/editorial_catalog/catalog/graveyard.yml
vendored
Normal file
41
code/tests/fixtures/editorial_catalog/catalog/graveyard.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
id: graveyard-fixture
|
||||
display_name: Reanimate
|
||||
curated_synergies:
|
||||
- Graveyard Loops
|
||||
- Sacrifice
|
||||
- Big Threats
|
||||
enforced_synergies:
|
||||
- Tutor
|
||||
inferred_synergies:
|
||||
- Recursion
|
||||
synergies:
|
||||
- Graveyard Loops
|
||||
- Sacrifice
|
||||
- Big Threats
|
||||
- Tutor
|
||||
primary_color: Black
|
||||
secondary_color: Blue
|
||||
notes: Fixture entry for editorial governance CI checks.
|
||||
example_commanders:
|
||||
- Meren of Clan Nel Toth
|
||||
- Chainer, Dementia Master
|
||||
- The Scarab God
|
||||
- Sheoldred, Whispering One
|
||||
- Araumi of the Dead Tide
|
||||
example_cards:
|
||||
- Reanimate
|
||||
- Animate Dead
|
||||
- Victimize
|
||||
- Entomb
|
||||
- Buried Alive
|
||||
synergy_commanders:
|
||||
- Gisa and Geralf - Synergy (Graveyard Loops)
|
||||
- Kess, Dissident Mage - Synergy (Tutor)
|
||||
- Syr Konrad, the Grim - Synergy (Sacrifice)
|
||||
deck_archetype: Control
|
||||
popularity_bucket: Common
|
||||
description: Loads high-impact cards into the graveyard early and reanimates them for explosive tempo or combo loops.
|
||||
editorial_quality: reviewed
|
||||
metadata_info:
|
||||
script: build_theme_catalog.py
|
||||
last_backfill: '2024-09-03T00:00:00Z'
|
||||
41
code/tests/fixtures/editorial_catalog/catalog/tokens.yml
vendored
Normal file
41
code/tests/fixtures/editorial_catalog/catalog/tokens.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
id: tokens-fixture
|
||||
display_name: Tokens Matter
|
||||
curated_synergies:
|
||||
- Go Wide
|
||||
- Anthem
|
||||
- Sacrifice Fodder
|
||||
enforced_synergies:
|
||||
- Card Draw
|
||||
inferred_synergies:
|
||||
- Populate
|
||||
synergies:
|
||||
- Go Wide
|
||||
- Anthem
|
||||
- Sacrifice Fodder
|
||||
- Populate
|
||||
primary_color: White
|
||||
secondary_color: Green
|
||||
notes: Fixture entry to ensure metadata coverage for tests.
|
||||
example_commanders:
|
||||
- Rhys the Redeemed
|
||||
- Trostani, Selesnya's Voice
|
||||
- Adeline, Resplendent Cathar
|
||||
- Jetmir, Nexus of Revels
|
||||
- Kyler, Sigardian Emissary
|
||||
example_cards:
|
||||
- Anointed Procession
|
||||
- Parallel Lives
|
||||
- Heroic Reinforcements
|
||||
- Secure the Wastes
|
||||
- Beastmaster Ascension
|
||||
synergy_commanders:
|
||||
- Chatterfang, Squirrel General - Synergy (Sacrifice Fodder)
|
||||
- Emmara, Soul of the Accord - Synergy (Go Wide)
|
||||
- Tana, the Bloodsower - Synergy (Populate)
|
||||
deck_archetype: Midrange
|
||||
popularity_bucket: Very Common
|
||||
description: Goes wide with creature tokens then converts mass into damage, draw, drain, or sacrifice engines.
|
||||
editorial_quality: reviewed
|
||||
metadata_info:
|
||||
script: build_theme_catalog.py
|
||||
last_backfill: '2024-09-02T00:00:00Z'
|
||||
41
code/tests/fixtures/editorial_catalog/catalog/treasure.yml
vendored
Normal file
41
code/tests/fixtures/editorial_catalog/catalog/treasure.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
id: treasure-fixture
|
||||
display_name: Treasure
|
||||
curated_synergies:
|
||||
- Tokens
|
||||
- Artifacts
|
||||
- Sacrifice
|
||||
enforced_synergies:
|
||||
- Ramp
|
||||
inferred_synergies:
|
||||
- Combo
|
||||
synergies:
|
||||
- Tokens
|
||||
- Artifacts
|
||||
- Sacrifice
|
||||
- Ramp
|
||||
primary_color: Red
|
||||
secondary_color: Black
|
||||
notes: Sample fixture catalog entry for CI tests.
|
||||
example_commanders:
|
||||
- Prosper, Tome-Bound
|
||||
- Magda, Brazen Outlaw
|
||||
- Kalain, Reclusive Painter
|
||||
- Marionette Master
|
||||
- Jolene, the Plunder Queen
|
||||
example_cards:
|
||||
- Dockside Extortionist
|
||||
- Professional Face-Breaker
|
||||
- Brass's Bounty
|
||||
- Revel in Riches
|
||||
- Pitiless Plunderer
|
||||
synergy_commanders:
|
||||
- Brudiclad, Telchor Engineer - Synergy (Tokens)
|
||||
- Ruthless Technomancer - Synergy (Sacrifice)
|
||||
- Galazeth Prismari - Synergy (Artifacts)
|
||||
deck_archetype: Combo
|
||||
popularity_bucket: Common
|
||||
description: Generates Treasure tokens as flexible ramp and combo fuel enabling explosive payoff turns.
|
||||
editorial_quality: reviewed
|
||||
metadata_info:
|
||||
script: build_theme_catalog.py
|
||||
last_backfill: '2024-09-01T00:00:00Z'
|
||||
|
|
@ -20,6 +20,10 @@ from pathlib import Path
|
|||
from datetime import datetime
|
||||
from typing import Dict, Any, List, Set
|
||||
|
||||
import pytest
|
||||
|
||||
from code.tests.editorial_test_utils import ensure_editorial_fixtures
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
THEMES_DIR = ROOT / 'config' / 'themes'
|
||||
|
|
@ -28,6 +32,14 @@ CATALOG_DIR = THEMES_DIR / 'catalog'
|
|||
HISTORY = THEMES_DIR / 'description_fallback_history.jsonl'
|
||||
MAPPING = THEMES_DIR / 'description_mapping.yml'
|
||||
|
||||
USE_FIXTURES = (
|
||||
os.environ.get('EDITORIAL_TEST_USE_FIXTURES', '').strip().lower() in {'1', 'true', 'yes', 'on'}
|
||||
or not CATALOG_DIR.exists()
|
||||
or not any(CATALOG_DIR.glob('*.yml'))
|
||||
)
|
||||
|
||||
ensure_editorial_fixtures(force=USE_FIXTURES)
|
||||
|
||||
|
||||
def _load_catalog() -> Dict[str, Any]:
|
||||
data = json.loads(CATALOG_JSON.read_text(encoding='utf-8'))
|
||||
|
|
@ -70,7 +82,8 @@ def test_kpi_history_integrity():
|
|||
|
||||
def test_metadata_info_block_coverage():
|
||||
import yaml # type: ignore
|
||||
assert CATALOG_DIR.exists(), "Catalog YAML directory missing"
|
||||
if not CATALOG_DIR.exists() or not any(CATALOG_DIR.glob('*.yml')):
|
||||
pytest.skip('Catalog YAML directory missing; editorial fixtures not staged.')
|
||||
total = 0
|
||||
with_prov = 0
|
||||
for p in CATALOG_DIR.glob('*.yml'):
|
||||
|
|
|
|||
|
|
@ -3,10 +3,22 @@ import os
|
|||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from code.tests.editorial_test_utils import ensure_editorial_fixtures
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SCRIPT = ROOT / 'code' / 'scripts' / 'build_theme_catalog.py'
|
||||
CATALOG_DIR = ROOT / 'config' / 'themes' / 'catalog'
|
||||
|
||||
USE_FIXTURES = (
|
||||
os.environ.get('EDITORIAL_TEST_USE_FIXTURES', '').strip().lower() in {'1', 'true', 'yes', 'on'}
|
||||
or not CATALOG_DIR.exists()
|
||||
or not any(CATALOG_DIR.glob('*.yml'))
|
||||
)
|
||||
|
||||
ensure_editorial_fixtures(force=USE_FIXTURES)
|
||||
|
||||
|
||||
def run(cmd, env=None):
|
||||
env_vars = os.environ.copy()
|
||||
|
|
@ -53,7 +65,8 @@ def test_synergy_pairs_fallback_and_metadata_info(tmp_path):
|
|||
run(['python', str(SCRIPT), '--force-backfill-yaml', '--backfill-yaml'], env={'EDITORIAL_INCLUDE_FALLBACK_SUMMARY': '1'})
|
||||
# Locate YAML and verify metadata_info (or legacy provenance) inserted
|
||||
yaml_path = CATALOG_DIR / f"{candidate.lower().replace(' ', '-')}.yml"
|
||||
if yaml_path.exists():
|
||||
raw = yaml_path.read_text(encoding='utf-8').splitlines()
|
||||
if not yaml_path.exists():
|
||||
pytest.skip('Catalog YAML directory missing expected theme; fixture was not staged.')
|
||||
raw = yaml_path.read_text(encoding='utf-8').splitlines()
|
||||
has_meta = any(line.strip().startswith(('metadata_info:','provenance:')) for line in raw)
|
||||
assert has_meta, 'metadata_info block missing after forced backfill'
|
||||
|
|
@ -9,18 +9,35 @@ below the policy threshold after Phase D close-out.
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from code.tests.editorial_test_utils import ensure_editorial_fixtures
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
CATALOG = ROOT / 'config' / 'themes' / 'theme_list.json'
|
||||
THEMES_DIR = ROOT / 'config' / 'themes'
|
||||
CATALOG_DIR = THEMES_DIR / 'catalog'
|
||||
CATALOG = THEMES_DIR / 'theme_list.json'
|
||||
FIXTURE_THEME_LIST = Path(__file__).resolve().parent / 'fixtures' / 'editorial_catalog' / 'theme_list.json'
|
||||
|
||||
USE_FIXTURES = (
|
||||
os.environ.get('EDITORIAL_TEST_USE_FIXTURES', '').strip().lower() in {'1', 'true', 'yes', 'on'}
|
||||
or not CATALOG_DIR.exists()
|
||||
or not any(CATALOG_DIR.glob('*.yml'))
|
||||
)
|
||||
|
||||
ensure_editorial_fixtures(force=USE_FIXTURES)
|
||||
|
||||
|
||||
def test_all_themes_meet_minimum_examples():
|
||||
os.environ['EDITORIAL_MIN_EXAMPLES_ENFORCE'] = '1'
|
||||
min_required = int(os.environ.get('EDITORIAL_MIN_EXAMPLES', '5'))
|
||||
assert CATALOG.exists(), 'theme_list.json missing (run build script before tests)'
|
||||
data = json.loads(CATALOG.read_text(encoding='utf-8'))
|
||||
source = FIXTURE_THEME_LIST if USE_FIXTURES else CATALOG
|
||||
if not source.exists():
|
||||
pytest.skip('theme list unavailable; editorial fixtures not staged.')
|
||||
data = json.loads(source.read_text(encoding='utf-8'))
|
||||
assert 'themes' in data
|
||||
short = []
|
||||
for entry in data['themes']:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue