feat: REPL and CLI wiring; end-to-end capture-to-spec smoke test
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
from auto_reverse.cli import _parse_args
|
||||
|
||||
|
||||
def test_parse_minimal():
|
||||
cfg = _parse_args(["https://app.example.com"])
|
||||
assert cfg.target_url == "https://app.example.com"
|
||||
assert cfg.model == "claude-opus-4-8"
|
||||
assert cfg.headless is False
|
||||
|
||||
|
||||
def test_parse_scope_and_flags():
|
||||
cfg = _parse_args(["https://x.com", "--scope", "a.com,b.com", "--headless", "--gen-client"])
|
||||
assert cfg.scope_hosts == {"a.com", "b.com"}
|
||||
assert cfg.headless is True
|
||||
assert cfg.gen_client is True
|
||||
@@ -0,0 +1,57 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
playwright = pytest.importorskip("playwright.sync_api")
|
||||
|
||||
from auto_reverse.browser import Browser # noqa: E402
|
||||
from auto_reverse.doc.engine import DocEngine # noqa: E402
|
||||
from auto_reverse.proxy import ProxyServer # noqa: E402
|
||||
from auto_reverse.store import FlowStore, ScopeFilter # noqa: E402
|
||||
|
||||
|
||||
def test_capture_to_spec_end_to_end(tmp_path: Path, fixture_site: str):
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
host = urlsplit(fixture_site).hostname
|
||||
port = urlsplit(fixture_site).port
|
||||
scope = ScopeFilter(target_hosts={f"{host}:{port}", host})
|
||||
|
||||
engine_holder: dict = {}
|
||||
|
||||
def on_new(sig):
|
||||
engine_holder["engine"].document(sig)
|
||||
|
||||
store = FlowStore(scope, on_new_signature=on_new)
|
||||
engine = DocEngine(store, out_dir=tmp_path, title="fixture", use_llm=False)
|
||||
engine_holder["engine"] = engine
|
||||
|
||||
proxy = ProxyServer(store, archive_path=tmp_path / "archive.log", port=0)
|
||||
try:
|
||||
proxy.start()
|
||||
except Exception as exc:
|
||||
pytest.skip(f"proxy unavailable: {exc}")
|
||||
|
||||
try:
|
||||
browser = Browser(proxy_port=proxy.port, headless=True)
|
||||
browser.start()
|
||||
except Exception as exc:
|
||||
proxy.stop()
|
||||
pytest.skip(f"browser unavailable: {exc}")
|
||||
|
||||
try:
|
||||
browser.navigate(fixture_site + "/") # triggers fetch('/api/users')
|
||||
# allow capture to settle
|
||||
threading.Event().wait(1.0)
|
||||
assert any(
|
||||
"/api/users" in r.signature.path_template for r in store.endpoints()
|
||||
)
|
||||
finally:
|
||||
browser.stop()
|
||||
proxy.stop()
|
||||
@@ -0,0 +1,32 @@
|
||||
from auto_reverse.models import CapturedFlow
|
||||
from auto_reverse.repl import Repl
|
||||
from auto_reverse.store import FlowStore, ScopeFilter
|
||||
|
||||
|
||||
class FakeAgent:
|
||||
def run_turn(self, msg):
|
||||
return f"agent saw: {msg}"
|
||||
|
||||
|
||||
def _store():
|
||||
s = FlowStore(ScopeFilter(target_hosts={"ex.com"}))
|
||||
s.ingest(CapturedFlow(
|
||||
method="GET", host="ex.com", path="/api/users", query={}, req_headers={},
|
||||
req_body=None, status=200, resp_headers={}, resp_body=None, timestamp=0.0,
|
||||
))
|
||||
return s
|
||||
|
||||
|
||||
def test_quit_returns_none():
|
||||
repl = Repl(FakeAgent(), _store(), "openapi.yaml")
|
||||
assert repl.handle("/quit") is None
|
||||
|
||||
|
||||
def test_flows_lists_endpoints():
|
||||
repl = Repl(FakeAgent(), _store(), "openapi.yaml")
|
||||
assert "/api/users" in repl.handle("/flows")
|
||||
|
||||
|
||||
def test_plain_text_goes_to_agent():
|
||||
repl = Repl(FakeAgent(), _store(), "openapi.yaml")
|
||||
assert repl.handle("map users") == "agent saw: map users"
|
||||
Reference in New Issue
Block a user