mirror of
https://github.com/marcredhat/kql
synced 2026-06-10 14:01:20 +00:00
Initial commit: KQL ↔ SDL PowerQuery proof of equivalence
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
#!/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)
|
||||
Reference in New Issue
Block a user