Transcript analysis (session 2026-07-06-175012, GLM 5.2) showed F1 at 567s: 182s wasted on a useless `tht schema introspect` (re-introspecting the remote DWH although physical.yaml was already materialized) plus ~220s of model thinking inflated by ~7 exploratory turns (--help/find/cat). The actual searches cost ~15s; reviewer gates (~145s, untouched) are the quality contract. - schema_cmd.py: introspect now exits 0 with "OK (cache)" in ~1s when physical.yaml exists; --refresh forces the real re-introspection. Deterministic cross-model guarantee, verified live on psd (163 tables, 1.2s). - SKILL.md: F1 toolbox (only `tht search find` + `tht schema render`; no introspect/--help/filesystem browsing; batch all searches in one turn); F4 step 1 is render-only with a one-shot introspect fallback. - tht-gate.js: `tht schema introspect ... --refresh` added to FORBIDDEN (maintenance stays shell-only, never in-session). - tests: 4 new pytest cases (cache hit placement proven with fake credentials, refresh bypass, corrupt-catalog fall-through, render fallback message) and 2 gate anti-bypass JS cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
from datetime import datetime
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from tht.cli import app
|
|
from tht.mschema.models import ColumnPhysical, PhysicalSchema, TablePhysical
|
|
|
|
|
|
def _write_catalog(tmp_path):
|
|
phys = PhysicalSchema(
|
|
database="d", schema="s", introspected_at=datetime(2026, 1, 1),
|
|
tables={
|
|
"dim_patient": TablePhysical(
|
|
comment="Anagrafica",
|
|
columns={
|
|
"cod_paz": ColumnPhysical(type="bigint", pk=True, comment="Codice paziente"),
|
|
},
|
|
)
|
|
},
|
|
)
|
|
out = tmp_path / "artifacts" / "mschema" / "physical.yaml"
|
|
phys.to_yaml(out)
|
|
return out
|
|
|
|
|
|
def _write_config(tmp_path):
|
|
cfg = tmp_path / "workspace.yaml"
|
|
cfg.write_text(
|
|
"database: {database: d, schema: s, user: u, password: p, transport: direct}\n"
|
|
f"paths: {{artifacts: {tmp_path/'artifacts'}, indexes: {tmp_path/'i'}, sessions: {tmp_path/'s'}}}\n"
|
|
)
|
|
return cfg
|
|
|
|
|
|
def test_introspect_cache_hit_skips_dwh(tmp_path):
|
|
# Le credenziali sono fasulle: se la guardia non scattasse PRIMA del branch
|
|
# transport, il comando tenterebbe la connessione e fallirebbe.
|
|
catalog = _write_catalog(tmp_path)
|
|
before = catalog.read_bytes()
|
|
cfg = _write_config(tmp_path)
|
|
res = CliRunner().invoke(app, ["schema", "introspect", "-c", str(cfg)])
|
|
assert res.exit_code == 0, res.output
|
|
assert "OK (cache)" in res.output
|
|
assert "1 tabelle" in res.output
|
|
assert catalog.read_bytes() == before
|
|
|
|
|
|
def test_introspect_refresh_bypasses_cache(tmp_path):
|
|
_write_catalog(tmp_path)
|
|
cfg = _write_config(tmp_path)
|
|
res = CliRunner().invoke(app, ["schema", "introspect", "-c", str(cfg), "--refresh"])
|
|
assert res.exit_code != 0
|
|
assert "OK (cache)" not in res.output
|
|
|
|
|
|
def test_introspect_corrupt_catalog_falls_through(tmp_path):
|
|
catalog = tmp_path / "artifacts" / "mschema" / "physical.yaml"
|
|
catalog.parent.mkdir(parents=True)
|
|
catalog.write_text("{not: [valid")
|
|
cfg = _write_config(tmp_path)
|
|
res = CliRunner().invoke(app, ["schema", "introspect", "-c", str(cfg)])
|
|
assert res.exit_code != 0
|
|
assert "OK (cache)" not in res.output
|
|
|
|
|
|
def test_render_without_catalog_guides_fallback(tmp_path):
|
|
cfg = _write_config(tmp_path)
|
|
res = CliRunner().invoke(app, ["schema", "render", "-c", str(cfg)])
|
|
assert res.exit_code == 1
|
|
assert "Esegui prima" in res.output
|