# Test the speak() error sanitizer: it MUST catch raw errors and MUST NOT
# false-positive on legitimate butler speech. Extract _ERROR_SIGNATURE_RE from
# wivexx_api.py (no import) and test both directions, including every genuine
# spoken response string harvested from the source.
import ast
import re
import sys

import os as _os; PROJ = _os.path.dirname(_os.path.abspath(__file__))
src = open(PROJ + r"\wivexx_api.py", encoding="utf-8").read()
tree = ast.parse(src)
rx = None
for n in ast.walk(tree):
    if isinstance(n, ast.Assign) and getattr(n.targets[0], "id", "") == "_ERROR_SIGNATURE_RE":
        rx = re.compile(n.value.args[0].value)
assert rx, "regex not found"
def looks(t): return bool(t) and bool(rx.search(t))

fails = []
# ── MUST be caught (real leaked errors) ──
ERRORS = [
    "I couldn't save that app, sir. [Errno 13] Permission denied: 'C:/x.json'",
    "Traceback (most recent call last): File \"wivexx_api.py\", line 8730, in _add",
    "KeyError: 'handle'",
    "ValueError: invalid literal for int() with base 10: 'abc'",
    "ConnectionResetError: [WinError 10054] An existing connection was forcibly closed",
    "<sqlite3.Connection object at 0x000001A2B3C4D5E6>",
    "TypeError: unsupported operand type(s) for +: 'int' and 'str'",
    "JSONDecodeError: Expecting value: line 1 column 1 (char 0)",
    "HTTPError: 403 Forbidden",
    "I couldn't add that tracker, sir — ModuleNotFoundError: no module named x.",
    "something failed 0xdeadbeef",
    'File "C:\\wivexx\\x.py", line 42, in run',
]
for e in ERRORS:
    if not looks(e):
        fails.append(("MISSED ERROR", e))

# ── MUST NOT be caught (legitimate spoken lines) ──
# harvest genuine spoken responses (contain 'sir'/'señor') from the source
legit = set()
for n in ast.walk(tree):
    s = None
    if isinstance(n, ast.Constant) and isinstance(n.value, str):
        s = n.value
    elif isinstance(n, ast.JoinedStr):
        parts = []
        for p in n.values:
            if isinstance(p, ast.Constant): parts.append(str(p.value))
            else: parts.append("Discord")            # placeholder sample
        s = "".join(parts)
    if s and 8 < len(s) < 240 and re.search(r"\bsir\b|señor", s) and "\n" not in s:
        legit.add(s)
# plus hand-picked tricky legit lines that MENTION error/problem/warning naturally
legit |= {
    "I encountered an error, sir.",
    "The social tracker ran into a problem, sir. Check the log.",
    "Warn when a drive is running low on space",
    "There was an error, but I recovered, sir.",
    "Disk-space warnings are on, sir.",
    "HTTP is a web protocol, sir.",
    "Your YouTube video is ready, sir.",
    "Something went wrong, sir.",
    "That rewrite failed, sir.",
    "The transcription failed, sir.",
    "My apologies, sir — something went wrong with that. I've noted it in the log.",
}
for s in sorted(legit):
    if looks(s):
        fails.append(("FALSE POSITIVE", s))

print(f"errors caught: {len(ERRORS)-len([f for f in fails if f[0]=='MISSED ERROR'])}/{len(ERRORS)}")
print(f"legit spoken lines checked: {len(legit)}  false positives: {len([f for f in fails if f[0]=='FALSE POSITIVE'])}")
for kind, s in fails:
    print(f"  {kind}: {s!r}")
print("=== RESULT:", "ALL PASS" if not fails else "FAIL")
sys.exit(1 if fails else 0)
