Files
marcredhat-kql/harness/verify_pq_runs.py
T

93 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Independent post-export verification.
Reads every file in `pq/` AS WRITTEN ON DISK (no template substitution,
no scope prefix, no harness magic) and POSTs it to /api/powerQuery on
the configured tenant. The script asserts each file:
* parses cleanly (no 'error/client/badParam' status),
* returns a syntactically valid response (status='success').
It does NOT assert that the query returns any rows — empty results are
fine. The purpose is to catch syntax / field / function errors so the
published .pq files are guaranteed runnable by anyone who copies them.
"""
from __future__ import annotations
import re
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from harness.sdl_client import power_query # noqa: E402
PQ_DIR = ROOT / "pq"
files = sorted(PQ_DIR.glob("*.pq"))
def strip_comments(text: str) -> str:
return "\n".join(l for l in text.splitlines()
if not l.lstrip().startswith("//")).strip()
def collapse_whitespace(body: str) -> str:
"""Single-line form: same query, all whitespace collapsed to one space.
This simulates what happens when a user pastes the query into a web
textbox that strips newlines. A correctly-formatted PQ must survive
this transformation — every `|` between stages must be present.
"""
return re.sub(r"\s+", " ", body).strip()
print(f"Verifying {len(files)} .pq files run cleanly on SDL ...")
print("(Each file tested in TWO forms: as-written and whitespace-collapsed.)")
print()
passed: list[str] = []
failed: list[tuple[str, str, str]] = [] # (file, variant, reason)
def run(name: str, variant: str, body: str) -> bool:
t0 = time.time()
try:
r = power_query(body, start_time="2h")
except Exception as e:
failed.append((name, variant, f"exception: {e}"))
return False
elapsed = time.time() - t0
status = r.get("status", "")
if status == "success":
matching = r.get("matchingEvents", 0)
print(f"{name:<48} [{variant:<9}] "
f"matching={matching} ({elapsed:.1f}s)")
return True
msg = r.get("message", "")[:200]
print(f"{name:<48} [{variant:<9}] {status} :: {msg}")
failed.append((name, variant, f"{status}: {msg}"))
return False
for f in files:
text = f.read_text()
body = strip_comments(text)
if not body:
failed.append((f.name, "as-written", "empty after stripping comments"))
continue
ok1 = run(f.name, "as-written", body)
ok2 = run(f.name, "collapsed", collapse_whitespace(body))
if ok1 and ok2:
passed.append(f.name)
print()
print(f"PASS: {len(passed)} FAIL: {len(failed)}")
if failed:
print()
print("Failed queries:")
for name, variant, why in failed:
print(f" {name} [{variant}]: {why}")
sys.exit(1)