new-site/scripts/formation/test_name_search.py
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

175 lines
5.5 KiB
Python

#!/usr/bin/env python3
"""
Test name search automation for any state.
Usage:
python -m scripts.formation.test_name_search WY "Test Business LLC"
python -m scripts.formation.test_name_search CO "Acme Holdings"
python -m scripts.formation.test_name_search DE "First State Corp"
python -m scripts.formation.test_name_search --all "Test Business LLC"
Reports:
- Whether name search succeeded
- Whether the portal was reachable
- What selectors worked
- What barriers were encountered (CAPTCHA, login, WAF, etc.)
"""
from __future__ import annotations
import asyncio
import json
import logging
import sys
import time
from datetime import datetime
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(name)s] %(levelname)s %(message)s",
)
LOG = logging.getLogger("test_name_search")
async def test_state(state_code: str, name: str) -> dict:
"""Test name search for a single state."""
from scripts.formation.states import get_adapter, get_config, STATES
state_code = state_code.upper()
if state_code not in STATES:
return {"state": state_code, "status": "ERROR", "error": f"Unknown state: {state_code}"}
config = get_config(state_code)
state_name = STATES[state_code]["name"]
search_method = config.get("search_method", "playwright")
LOG.info("Testing %s (%s) — method: %s, name: '%s'", state_code, state_name, search_method, name)
start = time.time()
result = {
"state": state_code,
"state_name": state_name,
"search_method": search_method,
"portal_url": config.get("name_search_url", ""),
"test_name": name,
"started_at": datetime.utcnow().isoformat(),
}
try:
adapter = get_adapter(state_code)
search_result = await adapter.search_name(name)
result["status"] = "SUCCESS" if search_result.searched_name else "PARTIAL"
result["available"] = search_result.available
result["exact_match"] = search_result.exact_match
result["similar_names"] = search_result.similar_names[:5]
result["raw_response_length"] = len(search_result.raw_response)
if "not yet configured" in search_result.raw_response.lower():
result["status"] = "NOT_CONFIGURED"
result["barrier"] = "Selectors not configured in config.py"
elif "captcha" in search_result.raw_response.lower():
result["status"] = "BLOCKED_CAPTCHA"
result["barrier"] = "CAPTCHA detected"
elif "error" in search_result.raw_response.lower() and not search_result.available:
result["status"] = "ERROR"
result["error"] = search_result.raw_response[:200]
except Exception as e:
result["status"] = "ERROR"
result["error"] = str(e)
LOG.error("Test failed for %s: %s", state_code, e)
elapsed = time.time() - start
result["elapsed_seconds"] = round(elapsed, 2)
result["finished_at"] = datetime.utcnow().isoformat()
# Status emoji for readability
status_icon = {
"SUCCESS": "",
"PARTIAL": "~",
"NOT_CONFIGURED": "",
"BLOCKED_CAPTCHA": "",
"BLOCKED_LOGIN": "🔒",
"ERROR": "",
}.get(result["status"], "?")
LOG.info(
"%s %s (%s): %s — available=%s, elapsed=%.1fs",
status_icon, state_code, state_name, result["status"],
result.get("available", "?"), elapsed,
)
return result
async def test_all_states(name: str) -> list[dict]:
"""Test name search for all 51 states sequentially."""
from scripts.formation.states import STATES
results = []
for code in sorted(STATES.keys()):
result = await test_state(code, name)
results.append(result)
# Brief pause between states
if result["status"] not in ("NOT_CONFIGURED", "ERROR"):
await asyncio.sleep(2)
return results
def print_summary(results: list[dict]):
"""Print a summary table of test results."""
print("\n" + "=" * 80)
print("STATE NAME SEARCH TEST RESULTS")
print("=" * 80)
print(f"{'State':<6} {'Name':<25} {'Status':<20} {'Available':<10} {'Time':<8} {'Barrier'}")
print("-" * 80)
for r in results:
barrier = r.get("barrier", r.get("error", ""))[:30]
print(
f"{r['state']:<6} {r.get('state_name','?'):<25} {r['status']:<20} "
f"{str(r.get('available','?')):<10} {r.get('elapsed_seconds',0):>6.1f}s {barrier}"
)
# Summary counts
statuses = {}
for r in results:
s = r["status"]
statuses[s] = statuses.get(s, 0) + 1
print("-" * 80)
print("Summary:", " | ".join(f"{k}: {v}" for k, v in sorted(statuses.items())))
print(f"Total: {len(results)} states tested")
def main():
args = sys.argv[1:]
if len(args) < 2:
print("Usage:")
print(" python -m scripts.formation.test_name_search WY 'Test Business LLC'")
print(" python -m scripts.formation.test_name_search --all 'Test Business LLC'")
sys.exit(1)
if args[0] == "--all":
name = " ".join(args[1:])
results = asyncio.run(test_all_states(name))
print_summary(results)
# Save results to JSON
output_path = "/tmp/name_search_test_results.json"
with open(output_path, "w") as f:
json.dump(results, f, indent=2)
print(f"\nFull results saved to: {output_path}")
else:
state_code = args[0]
name = " ".join(args[1:])
result = asyncio.run(test_state(state_code, name))
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()