mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-16 15:40:12 +01:00
75 lines
3.4 KiB
Python
75 lines
3.4 KiB
Python
import os
|
|
import time
|
|
import json
|
|
from code.web.services.theme_preview import get_theme_preview, preview_metrics, bust_preview_cache
|
|
|
|
|
|
def test_colors_filter_constraint_green_subset():
|
|
"""colors=G should only return cards whose color identities are subset of {G} or colorless ('' list)."""
|
|
payload = get_theme_preview('Blink', limit=8, colors='G') # pick any theme; data-driven
|
|
for card in payload['sample']:
|
|
if not card['colors']:
|
|
continue
|
|
assert set(card['colors']).issubset({'G'}), f"Card {card['name']} had colors {card['colors']} outside filter"
|
|
|
|
|
|
def test_synthetic_placeholder_fill_present_when_short():
|
|
# Force scarcity via impossible color filter letter ensuring empty real pool -> synthetic placeholders
|
|
payload = get_theme_preview('Blink', limit=50, colors='Z')
|
|
# All real cards filtered out; placeholders must appear
|
|
synthetic_roles = [c for c in payload['sample'] if 'synthetic' in (c.get('roles') or [])]
|
|
assert synthetic_roles, 'Expected at least one synthetic placeholder entry under restrictive color filter'
|
|
assert any('synthetic_synergy_placeholder' in (c.get('reasons') or []) for c in synthetic_roles), 'Missing synthetic placeholder reason'
|
|
|
|
|
|
def test_cache_hit_timing_and_log(monkeypatch, capsys):
|
|
os.environ['WEB_THEME_PREVIEW_LOG'] = '1'
|
|
# Force fresh build
|
|
bust_preview_cache()
|
|
payload1 = get_theme_preview('Blink', limit=6)
|
|
assert payload1['cache_hit'] is False
|
|
# Second call should hit cache
|
|
payload2 = get_theme_preview('Blink', limit=6)
|
|
assert payload2['cache_hit'] is True
|
|
captured = capsys.readouterr().out.splitlines()
|
|
assert any('theme_preview_build' in line for line in captured), 'Missing build log'
|
|
assert any('theme_preview_cache_hit' in line for line in captured), 'Missing cache hit log'
|
|
|
|
|
|
def test_per_theme_percentiles_and_raw_counts():
|
|
bust_preview_cache()
|
|
for _ in range(5):
|
|
get_theme_preview('Blink', limit=6)
|
|
metrics = preview_metrics()
|
|
per = metrics['per_theme']
|
|
assert 'blink' in per, 'Expected theme slug in per_theme metrics'
|
|
blink_stats = per['blink']
|
|
assert 'p50_ms' in blink_stats and 'p95_ms' in blink_stats, 'Missing percentile metrics'
|
|
assert 'curated_total' in blink_stats and 'sampled_total' in blink_stats, 'Missing raw curated/sample per-theme totals'
|
|
|
|
|
|
def test_structured_log_contains_new_fields(capsys):
|
|
os.environ['WEB_THEME_PREVIEW_LOG'] = '1'
|
|
bust_preview_cache()
|
|
get_theme_preview('Blink', limit=5)
|
|
out_lines = capsys.readouterr().out.splitlines()
|
|
build_lines = [line for line in out_lines if 'theme_preview_build' in line]
|
|
assert build_lines, 'No build log lines found'
|
|
parsed = [json.loads(line) for line in build_lines]
|
|
obj = parsed[-1]
|
|
assert 'curated_total' in obj and 'sampled_total' in obj and 'role_counts' in obj, 'Missing expected structured log fields'
|
|
|
|
|
|
def test_warm_index_latency_reduction():
|
|
bust_preview_cache()
|
|
t0 = time.time()
|
|
get_theme_preview('Blink', limit=6)
|
|
cold = time.time() - t0
|
|
t1 = time.time()
|
|
get_theme_preview('Blink', limit=6)
|
|
warm = time.time() - t1
|
|
# Warm path should generally be faster; allow flakiness with generous factor
|
|
# If cold time is extremely small (timer resolution), skip strict assertion
|
|
if cold < 0.0005: # <0.5ms treat as indistinguishable; skip to avoid flaky failure
|
|
return
|
|
assert warm <= cold * 1.2, f"Expected warm path faster or near equal (cold={cold}, warm={warm})"
|