"""Shared pytest fixtures for e2e tests.""" import os import pytest import pytest_asyncio from .testharness import E2ETestContext, is_inprocess_transport # Host-side auth resolution ranks HMAC above the GitHub token, so an ambient # COPILOT_HMAC_KEY (CI sets one as a job-level credential) would be picked over # the token the replay snapshots expect, yielding 401s. For the in-process # transport the runtime is hosted in this test process and can capture the key as # early as client construction, so neutralize it at module load — the analogue of # .NET's InProcessEnvIsolation [ModuleInitializer] and Node's module-init guard. # Out-of-process children resolve auth in their own process where the token already # outranks HMAC. See https://github.com/github/copilot-sdk/issues/1934. if is_inprocess_transport(): os.environ.pop("COPILOT_HMAC_KEY", None) os.environ.pop("CAPI_HMAC_KEY", None) @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): """Track test failures to avoid writing corrupted snapshots.""" outcome = yield rep = outcome.get_result() if rep.when == "call" and rep.failed: # Store on the item's stash so the fixture can access it item.session.stash.setdefault("any_test_failed", False) item.session.stash["any_test_failed"] = True @pytest_asyncio.fixture(scope="module", loop_scope="module") async def ctx(request): """Create and teardown a test context shared across all tests in this module.""" context = E2ETestContext() await context.setup() yield context any_failed = request.session.stash.get("any_test_failed", False) skip_writing_cache = any_failed or bool(os.environ.get("GITHUB_ACTIONS")) await context.teardown(test_failed=skip_writing_cache) @pytest_asyncio.fixture(autouse=True, loop_scope="module") async def configure_test(request, ctx): """Automatically configure the proxy for each test.""" # Extract test file name from module # (e.g., "test_session" -> "session", "test_session_e2e" -> "session") module_name = request.module.__name__.split(".")[-1] if module_name.startswith("test_"): test_file = module_name[5:] # Remove "test_" prefix else: test_file = module_name if test_file.endswith("_e2e"): test_file = test_file[:-4] # Remove "_e2e" suffix for snapshot folder compatibility # Extract test name (e.g., "test_should_create_sessions" -> "should_create_sessions") test_name = request.node.name if test_name.startswith("test_"): test_name = test_name[5:] # Remove "test_" prefix await ctx.configure_for_test(test_file, test_name) yield