1"""Phase 00's exit criterion, as a test.
3The whole point of this phase is that ``import feedtwin`` works from two very
4different environments -- the feed-twin API container and EngineDesign's
5optimizer process -- and that the physics stack it needs is actually resolvable
6in both. That is not a formality: an optimizer that discovers a missing CoolProp
715 minutes into a Layer X run has wasted the run.
10from __future__
import annotations
13import importlib.metadata
23REQUIRED_STACK = [
"numpy",
"scipy",
"CoolProp",
"fluids",
"ht"]
27 assert feedtwin.__version__
31 """``__init__.py`` is the only place the version is written.
33 pyproject.toml reads it from there via ``[tool.setuptools.dynamic]``, so the
34 two cannot disagree in a fresh install. They can still disagree after
35 bumping ``__version__`` without reinstalling, and that matters: run
36 provenance stamps the attribute while anything resolving the distribution
37 sees the metadata. This turns that into a failed test rather than two
38 versions of the truth in a results file.
40 installed = importlib.metadata.version(
"feedtwin")
41 assert installed == feedtwin.__version__, (
42 f
"installed metadata says {installed}, feedtwin.__version__ says "
43 f
"{feedtwin.__version__} -- reinstall with `pip install -e lib/feedtwin`"
48 """The library must stay importable in a process with no web stack.
50 EngineDesign's optimizer will import this package directly (ADR-0001). If a
51 FastAPI import ever creeps into the physics -- through a Pydantic model that
52 drags in starlette, most likely -- that call path acquires a dependency it
53 has no use for, and the separation this package exists to maintain is gone.
55 Checked in a subprocess, not against this process's ``sys.modules``: pytest
56 itself, or a sibling test, may legitimately have imported a web framework
57 already, and asserting on a shared interpreter would make this pass or fail
58 on test ordering rather than on what feedtwin actually pulls in.
61 "import sys; import feedtwin; "
62 "leaked = {'fastapi', 'starlette', 'uvicorn'} & set(sys.modules); "
63 "print(','.join(sorted(leaked)))"
65 result = subprocess.run(
66 [sys.executable,
"-c", probe],
72 leaked = result.stdout.strip()
73 assert not leaked, f
"importing feedtwin pulled in a web framework: {leaked}"
76@pytest.mark.parametrize("module", REQUIRED_STACK)
78 """Each dependency resolves in whatever environment is running these tests."""
79 assert importlib.import_module(module)
is not None
83 """Run provenance covers the whole stack, not just feedtwin's own version."""
84 versions = feedtwin.stack_versions()
86 assert versions[
"feedtwin"] == feedtwin.__version__
87 assert versions[
"python"]
89 missing = [name
for name
in REQUIRED_STACK
if versions.get(name)
is None]
90 assert not missing, f
"stack_versions() could not report versions for {missing}"
None test_physics_stack_is_importable(str module)
None test_stack_versions_reports_every_dependency()
None test_version_matches_installed_metadata()
None test_package_carries_no_web_framework()
None test_version_is_declared()