At F4 (schema_linking) a model asking for a whole table family, e.g. `fact_sost_impianto_*`, used to hit a bare "tabella non nel catalogo" and stall without recovering. Now a pattern (containing * ? [) resolves to every matching catalog table and returns their columns (JSON becomes an array of per-table objects); exact names keep the original single-object contract. A non-glob miss also suggests sibling tables sharing the leading segment, to aid recovery. Verified live: `fact_sost_impianto_*` resolves the 20-table family on the psd catalog. Harness suite green (811 passed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
100 lines
3.6 KiB
Python
100 lines
3.6 KiB
Python
import json
|
|
from datetime import datetime
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from tht.cli import app # the root Typer app that mounts schema_app as "schema"
|
|
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"),
|
|
"nome": ColumnPhysical(type="text", comment="Nome"),
|
|
},
|
|
)
|
|
},
|
|
)
|
|
out = tmp_path / "artifacts" / "mschema" / "physical.yaml"
|
|
phys.to_yaml(out)
|
|
return out
|
|
|
|
|
|
def test_schema_columns_json(tmp_path, monkeypatch):
|
|
_write_catalog(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"
|
|
)
|
|
res = CliRunner().invoke(app, ["schema", "columns", "dim_patient", "--json", "-c", str(cfg)])
|
|
assert res.exit_code == 0, res.output
|
|
data = json.loads(res.output)
|
|
assert data["table"] == "dim_patient"
|
|
assert data["description"] == "Anagrafica"
|
|
assert {"name": "cod_paz", "description": "Codice paziente", "type": "bigint", "pk": True} in data["columns"]
|
|
|
|
|
|
def _write_family_catalog(tmp_path):
|
|
def _t(comment):
|
|
return TablePhysical(
|
|
comment=comment,
|
|
columns={"cod_paz": ColumnPhysical(type="bigint", pk=True, comment="Codice paziente")},
|
|
)
|
|
|
|
phys = PhysicalSchema(
|
|
database="d", schema="s", introspected_at=datetime(2026, 1, 1),
|
|
tables={
|
|
"fact_sost_impianto_pmk": _t("Sost PMK"),
|
|
"fact_sost_impianto_crt_d": _t("Sost CRT-D"),
|
|
"fact_impianto_pacemaker": _t("Impianto PMK"),
|
|
},
|
|
)
|
|
out = tmp_path / "artifacts" / "mschema" / "physical.yaml"
|
|
phys.to_yaml(out)
|
|
return out
|
|
|
|
|
|
def _cfg(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_schema_columns_glob_resolves_the_matching_family(tmp_path):
|
|
_write_family_catalog(tmp_path)
|
|
res = CliRunner().invoke(
|
|
app, ["schema", "columns", "fact_sost_impianto_*", "--json", "-c", str(_cfg(tmp_path))]
|
|
)
|
|
assert res.exit_code == 0, res.output
|
|
data = json.loads(res.output)
|
|
assert isinstance(data, list) # a pattern yields an array of per-table objects
|
|
assert sorted(d["table"] for d in data) == ["fact_sost_impianto_crt_d", "fact_sost_impianto_pmk"]
|
|
|
|
|
|
def test_schema_columns_glob_without_matches_errors(tmp_path):
|
|
_write_family_catalog(tmp_path)
|
|
res = CliRunner().invoke(
|
|
app, ["schema", "columns", "fact_assente_*", "-c", str(_cfg(tmp_path))]
|
|
)
|
|
assert res.exit_code == 1
|
|
assert "nessuna tabella corrisponde al pattern" in res.output
|
|
|
|
|
|
def test_schema_columns_exact_miss_suggests_siblings(tmp_path):
|
|
_write_family_catalog(tmp_path)
|
|
res = CliRunner().invoke(
|
|
app, ["schema", "columns", "fact_sost_impianto_xyz", "-c", str(_cfg(tmp_path))]
|
|
)
|
|
assert res.exit_code == 1
|
|
assert "tabella non nel catalogo" in res.output
|
|
assert "fact_sost_impianto_crt_d" in res.output # sibling suggested to aid recovery
|