File size: 2,159 Bytes
414dc55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
"""The shipped pre-baked case pool must be valid and fair: every file parses as a CaseFile,
IDs are unique and stable, the solver passes, and each case reads well (human suspects with a
gender mix, a real culprit). These cases are served instantly on New Case, so a broken one
would reach players directly.
"""

from __future__ import annotations

import re

import pytest

from case_zero.persistence.case_store import load_case
from case_zero.persistence.paths import prebaked_cases_dir
from case_zero.solver.checker import check

_POOL = sorted(prebaked_cases_dir().glob("*.json")) if prebaked_cases_dir().is_dir() else []
_BAD_ROLE = re.compile(
    r"\b(detective|officer|investigator|police|inspector|sergeant|constable|cop|agent)\b",
    re.IGNORECASE,
)


@pytest.mark.skipif(not _POOL, reason="no pre-baked pool present")
def test_pool_ids_unique_and_stable() -> None:
    cases = [load_case(p) for p in _POOL]
    ids = [c.case_id for c in cases]
    assert len(set(ids)) == len(ids), "duplicate case IDs in the pool"
    # The stable scheme the player shares: CASE-0001, CASE-0002, ...
    for c, p in zip(cases, _POOL):
        assert c.case_id == p.stem, f"file {p.name} does not match its case_id {c.case_id}"


@pytest.mark.skipif(not _POOL, reason="no pre-baked pool present")
@pytest.mark.parametrize("path", _POOL, ids=lambda p: p.stem)
def test_pool_case_is_solvable_and_well_formed(path) -> None:
    case = load_case(path)
    assert check(case).ok, f"{path.name} is not solvable"
    assert case.title and len(case.title.strip()) >= 4
    assert case.victim.name.strip()
    names = [s.name.strip() for s in case.suspects]
    assert len(set(n.lower() for n in names)) == len(names), "duplicate suspect names"
    for s in case.suspects:
        assert not _BAD_ROLE.search(s.role), f"detective-like suspect role: {s.role}"
        assert not _BAD_ROLE.search(s.name), f"detective-like suspect name: {s.name}"
    assert any((s.visual.gender or "").lower().startswith("f") for s in case.suspects), (
        "no female suspect in the cast"
    )
    assert (case.culprit.method_narrative or "").strip(), "culprit has no method narrative"