The moment a real MASKANX_OPERATOR_TOKENS got set in Settings >
Environments (this session, for the first time on this machine), 28 tests
in test_campaign_launch.py started failing with 401 instead of the status
each was actually testing.
Cause: adclaw/__init__.py calls load_envs_into_environ() at import time,
so envs.json — the exact file the Settings UI writes — becomes ambient
environment for every test process, not only the ones that opt in.
Most campaign tests deliberately exercise the unauthenticated path and
never send an operator header; once a real token map exists,
require_operator starts demanding one they don't send.
This was already named as a deferred minor in the Phase 2 ledger
("test_campaign_api client fixture does not delenv MASKANX_OPERATOR_TOKENS")
but scoped to one file. It affects nine. An autouse fixture in
tests/conftest.py clears it before every test; a test that wants a token
still gets one, since its own monkeypatch.setenv runs after and overrides
it. Confirmed narrowly scoped: adding only this one delenv brought the
suite from 28 failed back to 1212 passed with nothing left over — no need
to guess at a wider blast radius.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
95 lines
3.4 KiB
Python
95 lines
3.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Shared fixtures for AOM tests."""
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _no_ambient_operator_tokens(monkeypatch):
|
|
"""Never let a developer's real envs.json reach a test process.
|
|
|
|
`adclaw/__init__.py` calls `load_envs_into_environ()` at import time, so
|
|
whatever is saved in Settings > Environments on this machine becomes
|
|
ambient environment for every test in the suite — not just the ones
|
|
that opted in. Most campaign tests deliberately exercise the
|
|
*unauthenticated* path (MASKANX_OPERATOR_TOKENS unset), so once a real
|
|
operator token is configured for actual use, require_operator starts
|
|
demanding a header those tests never send, and they fail with 401
|
|
instead of the status the test is actually about. 28 tests broke this
|
|
way the first time a real token was set.
|
|
|
|
A test that wants a token still gets one: its own monkeypatch.setenv
|
|
runs after this fixture in the same test and overrides it.
|
|
"""
|
|
monkeypatch.delenv("MASKANX_OPERATOR_TOKENS", raising=False)
|
|
|
|
from adclaw.memory_agent.embeddings import FakeEmbeddingPipeline
|
|
from adclaw.memory_agent.models import AOMConfig
|
|
from adclaw.memory_agent.store import MemoryStore
|
|
from adclaw.config.config import PersonaConfig, Config, AgentsConfig
|
|
|
|
|
|
@pytest.fixture
|
|
async def aom_store():
|
|
"""In-memory SQLite store — zero I/O."""
|
|
store = MemoryStore(":memory:", dimensions=32)
|
|
await store.initialize()
|
|
yield store
|
|
await store.close()
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_embedder():
|
|
"""Deterministic embedding pipeline for tests."""
|
|
return FakeEmbeddingPipeline(dimensions=32)
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_llm_caller():
|
|
"""Canned LLM: extraction → JSON, consolidation → insight text."""
|
|
|
|
async def caller(prompt: str) -> str:
|
|
if "extract" in prompt.lower():
|
|
return '{"entities": ["test_entity"], "topics": ["test_topic"], "importance": 0.7}'
|
|
if "consolidat" in prompt.lower() or "synthesiz" in prompt.lower() or "cluster" in prompt.lower():
|
|
return "INSIGHT: Test insight about the data.\nIMPORTANCE: 0.8"
|
|
if "memory" in prompt.lower() or "question" in prompt.lower():
|
|
return "Based on the memories, the answer is test. [Memory #abc123]"
|
|
return "Test response"
|
|
|
|
return caller
|
|
|
|
|
|
@pytest.fixture
|
|
def aom_config():
|
|
"""Default AOM configuration for tests."""
|
|
return AOMConfig(
|
|
enabled=True,
|
|
embedding_backend="local",
|
|
embedding_dimensions=32,
|
|
importance_threshold=0.3,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_personas():
|
|
"""Three personas: coordinator + researcher + writer."""
|
|
return [
|
|
PersonaConfig(id="coordinator", name="Coordinator", is_coordinator=True, soul_md="## Role\nOrchestrate the team."),
|
|
PersonaConfig(id="researcher", name="Mike", soul_md="## Role\nResearch and analyze."),
|
|
PersonaConfig(id="content-writer", name="Mira", soul_md="## Role\nWrite content."),
|
|
]
|
|
|
|
@pytest.fixture
|
|
def persona_manager(sample_personas, tmp_path):
|
|
"""PersonaManager with 3 personas and temp working dir."""
|
|
from adclaw.agents.persona_manager import PersonaManager
|
|
mgr = PersonaManager(working_dir=str(tmp_path), personas=sample_personas)
|
|
mgr.ensure_dirs()
|
|
return mgr
|
|
|
|
@pytest.fixture
|
|
def config_with_personas(sample_personas):
|
|
"""Config object with 3 personas."""
|
|
return Config(agents=AgentsConfig(personas=sample_personas))
|