"""V1: apply_violet_ddl — splitting, one-POST-per-statement, idempotency.""" from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, "/mnt/dolphinng5_predict") import pytest from prod.clickhouse.violet.apply_violet_ddl import ( EXPECTED_TABLES, SQL_DIR, _strip_comments, iter_statements, main, ) def test_statement_splitting_strips_comments_and_trailing_semis(tmp_path): f = tmp_path / "01_x.sql" f.write_text("-- a comment\nCREATE TABLE IF NOT EXISTS d.t (x Int8)\nENGINE=Memory;\n\n-- tail\n") stmts = list(iter_statements(tmp_path)) assert len(stmts) == 1 fname, stmt = stmts[0] assert fname == "01_x.sql" assert "-- " not in stmt assert not stmt.endswith(";") def test_real_sql_dir_yields_one_create_per_file(): """Every shipped .sql file must contain exactly ONE statement (the one-POST-per-statement contract is enforceable only if files stay single-statement).""" by_file: dict[str, int] = {} for fname, stmt in iter_statements(SQL_DIR): by_file[fname] = by_file.get(fname, 0) + 1 assert stmt.upper().startswith(("CREATE TABLE IF NOT EXISTS", "CREATE DATABASE IF NOT EXISTS")), ( f"{fname}: non-idempotent or non-CREATE statement" ) assert "dolphin_pink" not in stmt, f"{fname}: leftover dolphin_pink reference" assert all(n == 1 for n in by_file.values()), f"multi-statement files: {by_file}" # every expected table has a CREATE created = { stmt.split("dolphin_violet.")[1].split("\n")[0].split(" ")[0].split("(")[0].strip("`") for _, stmt in iter_statements(SQL_DIR) if "dolphin_violet." in stmt } assert EXPECTED_TABLES <= created, f"missing CREATEs: {EXPECTED_TABLES - created}" def test_one_post_per_statement_and_double_apply_idempotent(monkeypatch): posts: list[str] = [] def fake_apply(stmt, *, url, user, password): posts.append(stmt) import prod.clickhouse.violet.apply_violet_ddl as mod monkeypatch.setattr(mod, "apply_statement", fake_apply) monkeypatch.setattr(mod, "verify", lambda **kw: []) rc1 = main(["--ch-url", "http://fake:0"]) n_first = len(posts) rc2 = main(["--ch-url", "http://fake:0"]) assert rc1 == 0 and rc2 == 0 assert len(posts) == 2 * n_first # same statements re-posted assert n_first == len(list(iter_statements(SQL_DIR))) assert all(";" not in p for p in posts) # never multi-statement bodies def test_verify_failure_path(monkeypatch): import prod.clickhouse.violet.apply_violet_ddl as mod monkeypatch.setattr(mod, "apply_statement", lambda *a, **k: None) monkeypatch.setattr(mod, "verify", lambda **kw: ["violet_feed_divergence"]) assert main(["--ch-url", "http://fake:0"]) == 1 def test_dry_run_posts_nothing(monkeypatch, capsys): import prod.clickhouse.violet.apply_violet_ddl as mod def boom(*a, **k): raise AssertionError("dry-run must not POST") monkeypatch.setattr(mod, "apply_statement", boom) assert main(["--dry-run", "--ch-url", "http://fake:0"]) == 0 out = capsys.readouterr().out assert "[dry-run]" in out if __name__ == "__main__": raise SystemExit(pytest.main([__file__, "-v"]))