mirror of
https://github.com/Heretek-AI/AetherTable.git
synced 2026-11-07 17:15:54 -10:00
Signup accepts only player/spectator — staff roles come from the
VTT_ADMIN_EMAILS allowlist (rotatable without restart). Gateway aborts
startup without AUTH_SECRET/VTT_ENGINE_SECRET, mirroring engine semantics.
GET /lobbies/{id} member-or-staff only with no existence oracle; invite
codes stay visible to members. 20 tests red-first; pytest 672 passed.
Co-Authored-By: Claude <[email protected]>
75 lines
3.0 KiB
Python
75 lines
3.0 KiB
Python
"""Shared pytest configuration for the orchestrator test suite.
|
|
|
|
Implements the opt-in gate for the LIVE LLM suite (``pytest.mark.live``):
|
|
|
|
A test marked ``@pytest.mark.live`` runs ONLY when BOTH hold:
|
|
1. ``LLM_KEY`` is set in the environment (a real external endpoint is
|
|
configured -- see ``LLMConfig`` in
|
|
``vtt_orchestrator/routing/llm_client.py``), AND
|
|
2. ``RUN_LIVE_LLM=1`` is exported explicitly.
|
|
|
|
CI never exports ``RUN_LIVE_LLM``, so live tests can never execute there;
|
|
locally they are skipped (not failed) unless the operator opts in. The skip
|
|
decision lives here so every test in the live module gets it for free and no
|
|
individual test has to duplicate the guard.
|
|
"""
|
|
|
|
import os
|
|
|
|
# The gateway now REFUSES to start without a signing secret (audit F6b: the
|
|
# old hardcoded dev fallback made session tokens forgeable). Production and
|
|
# every launcher script export AUTH_SECRET explicitly; the suite pins the
|
|
# well-known dev value here for exactly the same reason — deliberately, not
|
|
# by silent fallback inside server.py.
|
|
os.environ.setdefault("AUTH_SECRET", "aethertable-dev-secret")
|
|
|
|
import pytest
|
|
|
|
|
|
def _live_llm_enabled() -> bool:
|
|
"""True only when a key is configured AND the opt-in flag is exported."""
|
|
if not os.environ.get("LLM_KEY"):
|
|
return False
|
|
return os.environ.get("RUN_LIVE_LLM", "0").strip().lower() == "1"
|
|
|
|
|
|
def pytest_collection_modifyitems(config, items):
|
|
if _live_llm_enabled():
|
|
return
|
|
skip_live = pytest.mark.skip(
|
|
reason="live LLM test: requires LLM_KEY set AND RUN_LIVE_LLM=1 "
|
|
"(opt-in; never runs in CI)"
|
|
)
|
|
for item in items:
|
|
if "live" in item.keywords:
|
|
item.add_marker(skip_live)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolate_rate_limiter_windows():
|
|
"""Reset the orchestrator's in-process sliding-window rate limiter around
|
|
EVERY test.
|
|
|
|
``vtt_orchestrator.server`` keeps its 60 s windows in module state
|
|
(``_rate_windows``), keyed by ``(client_ip, bucket)``. Every TestClient in
|
|
the suite shares one source address ("testclient"), and several modules
|
|
hammer the auth bucket, whose limit is only 30 requests / 60 s. Without a
|
|
reset owned by the harness itself, whether a given module passes depends on
|
|
how many auth requests OTHER modules happened to make within the last
|
|
minute of wall clock — a nondeterministic flake that no single test can fix
|
|
from inside.
|
|
|
|
Two modules (test_campaign_sim, test_ai_companion) already carried their
|
|
own copies of this fixture; it lives here so no module's green-ness depends
|
|
on another module remembering to clean up. Per-test scope is deliberate:
|
|
clearing costs a dict clear (~microseconds), while module-scope would still
|
|
let one long test starve its siblings inside the same file.
|
|
"""
|
|
from vtt_orchestrator import server as server_module
|
|
|
|
server_module._rate_windows.clear()
|
|
yield
|
|
# Also clear on exit so hits recorded by a test never leak into ambient
|
|
# state (e.g. a later test asserting on window contents).
|
|
server_module._rate_windows.clear()
|