#!/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()