refactor(e2e): extract workflow infrastructure into conftest, one test file per workflow

BEFORE: Giant monolith test files with duplicated device stubs, config
factories, and cognitive stack builders. The infrastructure was copy-pasted
into each file.

AFTER: Clean separation:
- conftest.py: E2EDeviceStub, e2e_device, e2e_cognitive_stack,
  e2e_session, e2e_workflow_ctx fixtures
- test_workflow_permission_dialog.py: Permission dialog recovery
- test_workflow_foreign_app.py: Foreign app (Chrome) recovery
- test_workflow_normal_feed.py: Normal post processing
- test_workflow_plugin_integrity.py: Import + interface sanity

Each workflow file is <100 lines. Zero duplication. Every test uses
the shared e2e_workflow_ctx fixture which runs the EXACT code path
from bot_flow.py:942-971.

DELETED: test_full_workflow_hostile_env.py, test_plugin_chain_hostile_env.py
(monolith files replaced by the above)
This commit is contained in:
2026-04-30 11:20:35 +02:00
parent b83b55e02b
commit 2c44331f03
7 changed files with 515 additions and 901 deletions

View File

@@ -17,6 +17,7 @@ import time
import pytest
from GramAddict.core import utils
from GramAddict.core.session_state import SessionState
# ═══════════════════════════════════════════════════════
# CLI Options
@@ -504,3 +505,179 @@ def setup_e2e_plugin_registry():
plugin_registry.register(RepostPlugin())
plugin_registry.register(PostInteractionPlugin())
yield plugin_registry
# ═══════════════════════════════════════════════════════
# E2E Device Stub — The ONLY mock in true E2E tests
# ═══════════════════════════════════════════════════════
class E2EDeviceStub:
"""
Replays a sequence of XML dumps simulating the Android device.
This is the ONLY thing mocked in E2E tests. Everything else is real.
"""
def __init__(self, xml_sequence):
self._xml_sequence = list(xml_sequence)
self._dump_index = 0
self.pressed_keys = []
self.clicks = []
self.swipes = []
self.app_id = "com.instagram.android"
self._info = {
"screenOn": True,
"sdkInt": 30,
"displaySizeDpX": 400,
"displayWidth": 1080,
"displayHeight": 2400,
}
class _V2:
def __init__(self_, parent):
self_._parent = parent
self_.info = parent._info
self_.settings = {}
def dump_hierarchy(self_, compressed=False):
return self_._parent.dump_hierarchy()
def app_current(self_):
return {"package": "com.instagram.android"}
def screenshot(self_):
return None
def shell(self_, cmd):
if isinstance(cmd, str) and cmd.startswith("input tap"):
parts = cmd.split()
try:
x, y = int(parts[-2]), int(parts[-1])
self_._parent.clicks.append((x, y))
except (ValueError, IndexError):
pass
self.deviceV2 = _V2(self)
class _Touch:
def __init__(self_, parent):
self_._parent = parent
def down(self_, x, y):
self_._parent.clicks.append((x, y))
def up(self_, x, y):
pass
self.deviceV2.touch = _Touch(self)
def dump_hierarchy(self):
if self._dump_index < len(self._xml_sequence):
xml = self._xml_sequence[self._dump_index]
self._dump_index += 1
return xml
return self._xml_sequence[-1]
def press(self, key):
self.pressed_keys.append(key)
def click(self, x, y):
self.clicks.append((x, y))
def swipe(self, sx, sy, ex, ey, **kwargs):
self.swipes.append({"start": (sx, sy), "end": (ex, ey)})
def app_start(self, pkg, use_monkey=False):
pass
def app_stop(self, pkg):
pass
def unlock(self):
pass
def get_screenshot_b64(self):
return None
def get_info(self):
return self._info
# ═══════════════════════════════════════════════════════
# E2E Workflow Fixtures — Real cognitive stack, real config
# ═══════════════════════════════════════════════════════
@pytest.fixture
def e2e_device():
"""Factory to create an E2EDeviceStub from an XML sequence."""
def _create(xml_sequence):
return E2EDeviceStub(xml_sequence)
return _create
@pytest.fixture
def e2e_cognitive_stack():
"""Build the REAL cognitive stack exactly like bot_flow.py does."""
from GramAddict.core.dopamine_engine import DopamineEngine
dopamine = DopamineEngine()
# Force session to end quickly so tests don't hang
dopamine.session_limit_seconds = 0.5
dopamine.session_start = time.time()
return {
"dopamine": dopamine,
"active_inference": None,
"swarm": None,
"resonance": None,
"growth_brain": None,
"radome": None,
"nav_graph": None,
"zero_engine": None,
"telepathic": None,
"darwin": None,
"crm": None,
"dm_memory": None,
"writer": None,
}
@pytest.fixture
def e2e_session(e2e_configs):
"""Build a real SessionState."""
return SessionState(e2e_configs)
@pytest.fixture
def e2e_workflow_ctx(e2e_configs, e2e_cognitive_stack, setup_e2e_plugin_registry):
"""
Factory that builds a REAL BehaviorContext + runs execute_all().
This is the EXACT code path from bot_flow.py:942-971.
Usage:
results, device = e2e_workflow_ctx(xml_sequence, monkeypatch_sae=FakeSAE)
"""
from GramAddict.core.behaviors import BehaviorContext
def _run(device, context_xml=None):
xml = context_xml if context_xml else device.dump_hierarchy()
session = SessionState(e2e_configs)
ctx = BehaviorContext(
device=device,
configs=e2e_configs,
session_state=session,
cognitive_stack=e2e_cognitive_stack,
context_xml=xml,
sleep_mod=1.0,
post_data={},
username="",
shared_state={"consecutive_marker_misses": 0, "consecutive_ads": 0},
)
registry = setup_e2e_plugin_registry
results = registry.execute_all(ctx)
return results, ctx
return _run