Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
"""Tests for the har-derived-api-client optional skill.
|
||||
|
||||
Two layers, both stdlib + pytest, no network:
|
||||
1. Structural / frontmatter contract on SKILL.md (matches the maintainer
|
||||
review checklist for optional skills).
|
||||
2. Behavioral: run the real har_to_client.py logic against a synthetic HAR
|
||||
fixture and assert it derives the endpoint, collapses id path segments,
|
||||
filters static assets, and surfaces the User-Agent replay hint.
|
||||
"""
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SKILL_DIR = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "optional-skills"
|
||||
/ "web-development"
|
||||
/ "har-derived-api-client"
|
||||
)
|
||||
SKILL_MD = SKILL_DIR / "SKILL.md"
|
||||
CAPTURE = SKILL_DIR / "scripts" / "har_capture.py"
|
||||
CAPTURE_CDP = SKILL_DIR / "scripts" / "har_capture_cdp.py"
|
||||
DERIVE = SKILL_DIR / "scripts" / "har_to_client.py"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def skill_text() -> str:
|
||||
return SKILL_MD.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _load_module(path: Path, name: str):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
assert spec and spec.loader
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
# --- structural contract ---------------------------------------------------
|
||||
|
||||
|
||||
def test_skill_files_exist():
|
||||
assert SKILL_MD.is_file()
|
||||
assert CAPTURE.is_file()
|
||||
assert CAPTURE_CDP.is_file()
|
||||
assert DERIVE.is_file()
|
||||
|
||||
|
||||
def test_frontmatter_present(skill_text: str):
|
||||
assert skill_text.startswith("---\n")
|
||||
assert skill_text.count("---") >= 2
|
||||
|
||||
|
||||
def test_description_under_sixty_chars(skill_text: str):
|
||||
m = re.search(r"^description: (.*)$", skill_text, re.MULTILINE)
|
||||
assert m, "no description field"
|
||||
desc = m.group(1).strip()
|
||||
assert len(desc) <= 60, f"description is {len(desc)} chars (>60): {desc!r}"
|
||||
assert desc.endswith("."), "description should end with a period"
|
||||
|
||||
|
||||
def test_required_sections_present(skill_text: str):
|
||||
for heading in (
|
||||
"## When to Use",
|
||||
"## Prerequisites",
|
||||
"## How to Run",
|
||||
"## Quick Reference",
|
||||
"## Procedure",
|
||||
"## Pitfalls",
|
||||
"## Verification",
|
||||
):
|
||||
assert heading in skill_text, f"missing section: {heading}"
|
||||
|
||||
|
||||
# --- behavioral: derivation logic -----------------------------------------
|
||||
|
||||
|
||||
def _make_har() -> dict:
|
||||
return {
|
||||
"log": {
|
||||
"entries": [
|
||||
{ # a JSON API call we want derived, with an id path segment
|
||||
"_resourceType": "fetch",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://api.example.com/v1/items/12345/reviews?limit=5",
|
||||
"queryString": [{"name": "limit", "value": "5"}],
|
||||
"headers": [
|
||||
{"name": "User-Agent", "value": "Mozilla/5.0 TestBrowser/1.0"},
|
||||
{"name": "accept", "value": "application/json"},
|
||||
{"name": "referer", "value": "https://example.com/"},
|
||||
],
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"content": {
|
||||
"mimeType": "application/json",
|
||||
"text": '{"reviews":[{"id":1}]}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ # a static asset we must filter out by default
|
||||
"_resourceType": "script",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://cdn.example.com/app.js",
|
||||
"queryString": [],
|
||||
"headers": [{"name": "User-Agent", "value": "Mozilla/5.0 TestBrowser/1.0"}],
|
||||
},
|
||||
"response": {"status": 200, "content": {"mimeType": "application/javascript"}},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_derives_endpoint_and_filters_static(tmp_path, capsys):
|
||||
mod = _load_module(DERIVE, "har_to_client_undertest")
|
||||
har = tmp_path / "t.har"
|
||||
har.write_text(json.dumps(_make_har()), encoding="utf-8")
|
||||
|
||||
import sys
|
||||
|
||||
argv = sys.argv
|
||||
try:
|
||||
sys.argv = ["har_to_client.py", str(har), "--host", "example.com"]
|
||||
rc = mod.main()
|
||||
finally:
|
||||
sys.argv = argv
|
||||
out = capsys.readouterr().out
|
||||
|
||||
assert rc == 0
|
||||
# id path segment collapsed to {id}
|
||||
assert "GET https://api.example.com/v1/items/{id}/reviews" in out
|
||||
# query param surfaced
|
||||
assert "limit = 5" in out
|
||||
# static JS filtered out
|
||||
assert "app.js" not in out
|
||||
# boring header dropped, useful one absent from list but UA promoted to hints
|
||||
assert "referer" not in out
|
||||
# replay hint carries the browser UA
|
||||
assert "User-Agent (send this): Mozilla/5.0 TestBrowser/1.0" in out
|
||||
|
||||
|
||||
def test_path_template_collapses_ids():
|
||||
mod = _load_module(DERIVE, "har_to_client_undertest2")
|
||||
assert mod.path_template("/v1/items/12345/x") == "/v1/items/{id}/x"
|
||||
assert mod.path_template("/v1/items/abc/x") == "/v1/items/abc/x"
|
||||
|
||||
|
||||
def test_capture_actions_parse_ok():
|
||||
# har_capture imports playwright at module top; only assert the file is
|
||||
# syntactically valid and exposes run_action without importing playwright.
|
||||
src = CAPTURE.read_text(encoding="utf-8")
|
||||
compile(src, str(CAPTURE), "exec")
|
||||
assert "def run_action(" in src
|
||||
assert 'record_har_content="embed"' in src
|
||||
|
||||
|
||||
def test_cdp_capture_is_valid_and_attaches_not_launches():
|
||||
# Covers the CDP pathway (cloud backends / /browser connect). Syntax-check
|
||||
# without importing playwright, and assert it attaches (connect_over_cdp)
|
||||
# and does NOT close a browser it doesn't own.
|
||||
src = CAPTURE_CDP.read_text(encoding="utf-8")
|
||||
compile(src, str(CAPTURE_CDP), "exec")
|
||||
assert "connect_over_cdp(" in src
|
||||
assert 'page.on("request"' in src and 'page.on("response"' in src
|
||||
# must not tear down a browser it merely attached to
|
||||
assert "browser.close()" not in src
|
||||
|
||||
|
||||
def test_skill_documents_all_browser_pathways(skill_text: str):
|
||||
# The skill must route every Hermes browser backend to the right capturer.
|
||||
for token in ("Browserbase", "Browser-Use", "Firecrawl", "browser connect",
|
||||
"har_capture_cdp.py", "connect_over_cdp"):
|
||||
assert token in skill_text, f"pathway coverage missing: {token}"
|
||||
Reference in New Issue
Block a user