mirror of
https://github.com/calesthio/OpenMontage.git
synced 2026-08-16 13:36:26 +08:00
132 lines
4.3 KiB
Python
132 lines
4.3 KiB
Python
"""Session-wide test safety net.
|
|
|
|
**No test may open a network connection.** Provider tools bill per call, so a
|
|
test that reaches a real endpoint costs the developer money — silently, and
|
|
every time CI runs. This blocks outbound sockets for the whole test session.
|
|
|
|
The guard is at the socket layer on purpose. Patching `requests` only covers
|
|
tools that use `requests`; the fleet also talks to vendor SDKs (google-cloud,
|
|
openai, boto3), `httpx`, and raw `urllib`. Everything bottoms out in
|
|
`socket.connect`, so that is where the wall goes.
|
|
|
|
Loopback is still allowed — local servers, ffmpeg RPC, and Backlot fixtures need it.
|
|
|
|
To write a test that genuinely hits a live API:
|
|
|
|
@pytest.mark.live_api
|
|
def test_real_call():
|
|
...
|
|
|
|
Marked tests are **skipped by default** and only run with the env flag set:
|
|
|
|
OPENMONTAGE_ALLOW_NETWORK=1 pytest -m live_api
|
|
|
|
Limitation: this guards the pytest process. A test that shells out to a
|
|
subprocess (node, ffmpeg, npx) is outside its reach — don't call paid APIs
|
|
from a subprocess in tests.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import socket
|
|
|
|
import pytest
|
|
|
|
_ALLOW_ENV_FLAG = "OPENMONTAGE_ALLOW_NETWORK"
|
|
|
|
_LOOPBACK_HOSTS = {"localhost", "127.0.0.1", "::1", "0.0.0.0", ""}
|
|
|
|
_real_connect = socket.socket.connect
|
|
_real_connect_ex = socket.socket.connect_ex
|
|
_real_create_connection = socket.create_connection
|
|
|
|
|
|
class NetworkCallInTestError(RuntimeError):
|
|
"""Raised when a test tries to open a non-loopback connection."""
|
|
|
|
|
|
def _network_allowed() -> bool:
|
|
return os.environ.get(_ALLOW_ENV_FLAG, "").strip().lower() in {"1", "true", "yes"}
|
|
|
|
|
|
def _is_loopback(address) -> bool:
|
|
"""True for loopback TCP/UDP targets and for AF_UNIX socket paths."""
|
|
if isinstance(address, (str, bytes)):
|
|
return True # AF_UNIX / abstract socket — local by definition
|
|
if not isinstance(address, (tuple, list)) or not address:
|
|
return True # unrecognised shape; let the real call decide
|
|
host = address[0]
|
|
if isinstance(host, bytes):
|
|
host = host.decode("utf-8", "replace")
|
|
if not isinstance(host, str):
|
|
return False
|
|
host = host.strip("[]").lower()
|
|
if host in _LOOPBACK_HOSTS:
|
|
return True
|
|
return host.startswith("127.")
|
|
|
|
|
|
def _blocked(address) -> NetworkCallInTestError:
|
|
return NetworkCallInTestError(
|
|
f"Blocked a network connection to {address!r} during a test.\n"
|
|
f"\n"
|
|
f"Tests must not call real endpoints — provider APIs bill per request.\n"
|
|
f"Mock the transport instead (see tests/tools/test_atlas_video.py for the\n"
|
|
f"fake-`requests` pattern), or mark the test @pytest.mark.live_api and run\n"
|
|
f"it deliberately with {_ALLOW_ENV_FLAG}=1."
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
|
def _block_network():
|
|
"""Refuse non-loopback sockets for the entire session."""
|
|
if _network_allowed():
|
|
yield
|
|
return
|
|
|
|
def guarded_connect(self, address, *args, **kwargs):
|
|
if not _is_loopback(address):
|
|
raise _blocked(address)
|
|
return _real_connect(self, address, *args, **kwargs)
|
|
|
|
def guarded_connect_ex(self, address, *args, **kwargs):
|
|
if not _is_loopback(address):
|
|
raise _blocked(address)
|
|
return _real_connect_ex(self, address, *args, **kwargs)
|
|
|
|
def guarded_create_connection(address, *args, **kwargs):
|
|
if not _is_loopback(address):
|
|
raise _blocked(address)
|
|
return _real_create_connection(address, *args, **kwargs)
|
|
|
|
socket.socket.connect = guarded_connect
|
|
socket.socket.connect_ex = guarded_connect_ex
|
|
socket.create_connection = guarded_create_connection
|
|
try:
|
|
yield
|
|
finally:
|
|
socket.socket.connect = _real_connect
|
|
socket.socket.connect_ex = _real_connect_ex
|
|
socket.create_connection = _real_create_connection
|
|
|
|
|
|
def pytest_configure(config):
|
|
config.addinivalue_line(
|
|
"markers",
|
|
"live_api: test performs a real, billable API call. Skipped unless "
|
|
f"{_ALLOW_ENV_FLAG}=1 is set.",
|
|
)
|
|
|
|
|
|
def pytest_collection_modifyitems(config, items):
|
|
"""Skip live_api tests unless the operator explicitly opted in."""
|
|
if _network_allowed():
|
|
return
|
|
skip = pytest.mark.skip(
|
|
reason=f"live API test — costs money; set {_ALLOW_ENV_FLAG}=1 to run"
|
|
)
|
|
for item in items:
|
|
if "live_api" in item.keywords:
|
|
item.add_marker(skip)
|