fix(preprocess): initialize fresh DWH writers safely

This commit is contained in:
2026-07-12 07:07:31 +02:00
parent 22e806a41b
commit 4cf0adbdd2
5 changed files with 190 additions and 70 deletions
+55
View File
@@ -97,6 +97,61 @@ def test_writer_rejects_unbound_legacy_artifacts_before_building(tmp_path):
assert not (tmp_path / ".tht-dwh" / "OWNER.json").exists()
def test_writer_rejects_dangling_legacy_symlinks_before_claim_or_callback(tmp_path):
import pytest
legacy = tmp_path / "artifacts" / "mschema" / "physical.yaml"
legacy.parent.mkdir(parents=True)
legacy.symlink_to(tmp_path / "missing-catalog")
calls = []
pipeline = DwhPreprocessPipeline(
workspace_id="demo", workspace_root=tmp_path,
config_fingerprint=FP, input_fingerprint=FP,
introspect=lambda output: calls.append("called"),
build_lsh=lambda physical, output: None,
current_physical=legacy,
)
with pytest.raises(Exception, match="legacy artifacts are unbound"):
pipeline.run()
assert calls == []
assert not (tmp_path / ".tht-dwh" / "OWNER.json").exists()
def test_owner_publication_remains_on_locked_root_when_path_is_swapped(
monkeypatch, tmp_path,
):
import os
import pytest
import tht.jobs.dwh_pipeline as module
real_replace = module.os.replace
moved = tmp_path / "locked-root"
replacement = tmp_path / ".tht-dwh"
swapped = False
def swapping_replace(source, destination, *args, **kwargs):
nonlocal swapped
if destination == "OWNER.json" and kwargs.get("dst_dir_fd") is not None:
swapped = True
replacement.rename(moved)
replacement.mkdir(mode=0o700)
return real_replace(source, destination, *args, **kwargs)
monkeypatch.setattr(module.os, "replace", swapping_replace)
pipeline = DwhPreprocessPipeline(
workspace_id="demo", workspace_root=tmp_path,
config_fingerprint=FP, input_fingerprint=FP,
introspect=lambda output: (_ for _ in ()).throw(AssertionError("callback called")),
build_lsh=lambda physical, output: None,
)
with pytest.raises(Exception):
pipeline.run()
assert swapped
assert (moved / "OWNER.json").is_file()
assert not (replacement / "OWNER.json").exists()
assert os.stat(moved / "generation.lock").st_ino != os.stat(replacement).st_ino
def test_owner_requires_exact_read_only_owner_mode_and_active_requires_binding(tmp_path):
import pytest
+51 -6
View File
@@ -34,17 +34,62 @@ def _write_config(tmp_path):
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.
def test_introspect_rejects_unbound_legacy_cache(tmp_path):
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 == 1
assert "legacy artifacts are unbound" in res.output
assert catalog.exists()
def test_introspect_fresh_root_initializes_through_writer_job(tmp_path, monkeypatch):
import tht.cli.schema_cmd as module
cfg = _write_config(tmp_path)
physical = PhysicalSchema(
database="d", schema="s", introspected_at=datetime(2026, 1, 1),
tables={"dim_patient": TablePhysical(columns={"id": ColumnPhysical(type="bigint")})},
)
def refresh(_cfg, *, output_path=None, **_kwargs):
physical.to_yaml(output_path)
return physical
monkeypatch.setattr(module, "refresh_catalog", refresh)
res = CliRunner().invoke(app, ["schema", "introspect", "-c", str(cfg)])
assert res.exit_code == 0, res.output
assert "OK (cache)" in res.output
assert (tmp_path / ".tht-dwh" / "OWNER.json").is_file()
assert "1 tabelle" in res.output
assert catalog.read_bytes() == before
def test_lsh_build_fresh_root_initializes_introspection_and_lsh(tmp_path, monkeypatch):
import tht.cli.lsh_cmd as lsh_module
import tht.cli.schema_cmd as schema_module
import tht.lshindex as lshindex_module
cfg = _write_config(tmp_path)
physical = PhysicalSchema(
database="d", schema="s", introspected_at=datetime(2026, 1, 1),
tables={"dim_patient": TablePhysical(columns={"id": ColumnPhysical(type="bigint")})},
)
def refresh(_cfg, *, output_path=None, **_kwargs):
physical.to_yaml(output_path)
return physical
def build(_cfg, *, physical_file, output_dir, **_kwargs):
assert physical_file.is_file()
for name in ("s_lsh.pkl", "s_minhashes.pkl", "s_meta.json"):
(output_dir / name).write_text("index")
return {}, [], [], {}
monkeypatch.setattr(schema_module, "refresh_catalog", refresh)
monkeypatch.setattr(lsh_module, "build_lsh_artifacts", build)
monkeypatch.setattr(lshindex_module, "load_index", lambda *_args, **_kwargs: (None, {}, None))
res = CliRunner().invoke(app, ["lsh", "build", "-c", str(cfg)])
assert res.exit_code == 0, res.output
assert (tmp_path / ".tht-dwh" / "OWNER.json").is_file()
def test_introspect_refresh_bypasses_cache(tmp_path):