100 lines
3.2 KiB
Python
100 lines
3.2 KiB
Python
"""Shared fixtures for the whole suite (plan Step 6).
|
|
|
|
Determinism here is a property of the interface, not of the harness. Tests get
|
|
`frozen_today` and pass it to scripts as `--today`; nothing monkeypatches
|
|
`datetime`. The difference matters: a script that only behaves deterministically
|
|
because a test patched the clock is a script whose callers cannot make it
|
|
behave deterministically at all, and `dagens` (build-brief 5.8) has to be
|
|
reproducible from the command line.
|
|
|
|
The autouse `fixtures_are_pristine` guard exists because the failure it catches
|
|
does not announce itself. A test that writes into `tests/fixtures/` leaves
|
|
every later run reading a corpus that no longer matches what it says it is, and
|
|
the eventual failure surfaces in some unrelated test hours later. Catching it
|
|
at the end of the offending test names the culprit.
|
|
"""
|
|
|
|
import os
|
|
import shutil
|
|
|
|
import pytest
|
|
|
|
from helpers import golden as golden_helper
|
|
from helpers import workspace as workspace_helper
|
|
|
|
from jobbsok_lib import paths
|
|
|
|
FIXTURES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures")
|
|
|
|
#: Every date in the corpus is relative to this. 2026-09-15 is a Tuesday, so
|
|
#: weekday-sensitive output does not accidentally land on a weekend.
|
|
FROZEN_TODAY = workspace_helper.FROZEN_TODAY
|
|
|
|
|
|
def pytest_addoption(parser):
|
|
parser.addoption(
|
|
"--update-golden",
|
|
action="store_true",
|
|
default=False,
|
|
help="rewrite golden files instead of comparing against them",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def fixtures_dir():
|
|
"""Absolute path to the read-only fixture corpus."""
|
|
return FIXTURES_DIR
|
|
|
|
|
|
@pytest.fixture
|
|
def frozen_today():
|
|
"""The date the suite reasons from, passed to scripts as ``--today``."""
|
|
return FROZEN_TODAY
|
|
|
|
|
|
@pytest.fixture
|
|
def empty_workspace(tmp_path):
|
|
"""A scaffolded but empty build-brief section 5 workspace."""
|
|
root = str(tmp_path / "workspace")
|
|
paths.scaffold(root)
|
|
return root
|
|
|
|
|
|
@pytest.fixture
|
|
def workspace(empty_workspace):
|
|
"""A workspace with the fixture corpus copied in, when there is one.
|
|
|
|
The populated corpus arrives in plan Step 19; until then this is the
|
|
scaffolded tree, so tests written against it now keep working when the
|
|
corpus lands rather than being rewritten then.
|
|
"""
|
|
source = os.path.join(FIXTURES_DIR, "workspace")
|
|
if os.path.isdir(source):
|
|
shutil.copytree(source, empty_workspace, dirs_exist_ok=True)
|
|
return empty_workspace
|
|
|
|
|
|
@pytest.fixture
|
|
def golden(request):
|
|
"""`assert_golden` with the ``--update-golden`` flag already bound."""
|
|
update = request.config.getoption("--update-golden")
|
|
|
|
def compare(path, actual):
|
|
return golden_helper.assert_golden(path, actual, update=update)
|
|
|
|
return compare
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def fixtures_are_pristine(request):
|
|
"""Fail the test that mutated the read-only corpus, at that test."""
|
|
if not os.path.isdir(FIXTURES_DIR):
|
|
yield
|
|
return
|
|
if request.config.getoption("--update-golden"):
|
|
# Golden regeneration is the one sanctioned write into the corpus.
|
|
yield
|
|
return
|
|
before = workspace_helper.snapshot_tree(FIXTURES_DIR)
|
|
yield
|
|
workspace_helper.assert_tree_unchanged(FIXTURES_DIR, before)
|