mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 00:47:01 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Docker mysql:8.0.33 container, judge-style output grading, 12 original drills with live-verified references kept in tests only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWZewj4WS5X6AZyjWDp
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
"""Grade one query: fresh database, drill schema, user's SQL, exact compare."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING
|
|
|
|
from python_pkg.sql_drills import _engine
|
|
from python_pkg.sql_drills._normalize import diff, normalize
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
from python_pkg.sql_drills._drills import Drill
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CheckResult:
|
|
"""``status`` is ``pass``, ``fail`` (wrong output) or ``error``."""
|
|
|
|
status: str
|
|
message: str
|
|
|
|
|
|
class _StepError(Exception):
|
|
"""A pipeline step failed; the message is shown to the user."""
|
|
|
|
|
|
def _step(result: _engine.SqlResult, what: str) -> str:
|
|
"""Return stdout of a successful step or raise with its stderr."""
|
|
if result.returncode != 0:
|
|
msg = f"{what}: {result.stderr.strip()}"
|
|
raise _StepError(msg)
|
|
return result.stdout
|
|
|
|
|
|
def _execute(drill: Drill, query_file: Path) -> str:
|
|
"""Fresh DB, drill schema, then the user's SQL; return the raw output."""
|
|
if not _engine.is_ready():
|
|
msg = "MySQL is not running; run `start` first"
|
|
raise _StepError(msg)
|
|
if not query_file.is_file():
|
|
msg = f"query file not found: {query_file}"
|
|
raise _StepError(msg)
|
|
db = f"drill_{drill.id}"
|
|
reset = _engine.run_sql(f"DROP DATABASE IF EXISTS {db}; CREATE DATABASE {db};")
|
|
_step(reset, "could not reset database")
|
|
_step(_engine.run_sql(drill.schema.read_text(), database=db), "drill schema failed")
|
|
result = _engine.run_sql(query_file.read_text(), database=db)
|
|
return _step(result, "SQL error")
|
|
|
|
|
|
def run_check(drill: Drill, query_file: Path) -> CheckResult:
|
|
"""Load the drill into a fresh DB, run the query, compare to expected."""
|
|
try:
|
|
output = _execute(drill, query_file)
|
|
except _StepError as exc:
|
|
return CheckResult("error", str(exc))
|
|
expected = normalize(drill.expected.read_text())
|
|
actual = normalize(output)
|
|
if expected == actual:
|
|
return CheckResult("pass", f"PASS {drill.id} ({len(actual)} rows)")
|
|
return CheckResult(
|
|
"fail",
|
|
f"FAIL {drill.id}: expected {len(expected)} rows, got {len(actual)}\n"
|
|
f"{diff(expected, actual)}",
|
|
)
|