"""Task 6: `tht sql preview --json` + `--offset` paging. Pure-logic tests (no DB needed): 1. inject_limit_offset — subquery wrapping, non-destructive. 2. preview_cmd JSON-purity — monkeypatched do_run, verifies stdout is valid JSON. """ import json import pytest from tht.execute.limit import inject_limit_offset def test_inject_limit_offset_wraps_query(): sql = "SELECT a FROM t ORDER BY a" out = inject_limit_offset(sql, limit=10, offset=20) assert "LIMIT 10" in out and "OFFSET 20" in out # original query stays as a subquery (no clobber of an existing user LIMIT) assert "SELECT a FROM t ORDER BY a" in out def test_inject_limit_offset_zero_offset_no_offset_clause(): out = inject_limit_offset("SELECT 1", limit=5, offset=0) assert "LIMIT 5" in out assert "OFFSET" not in out def test_inject_limit_offset_strips_trailing_semicolon(): out = inject_limit_offset("SELECT 1;", limit=3, offset=0) assert "LIMIT 3" in out # semicolon should not appear in the inner subquery assert ";" not in out def test_inject_limit_offset_default_offset_zero(): out = inject_limit_offset("SELECT x FROM y", limit=7) assert "LIMIT 7" in out assert "OFFSET" not in out def test_preview_json_pure_stdout(monkeypatch, tmp_path, capsys): from types import SimpleNamespace from tht.cli import sql_cmd fake = SimpleNamespace(columns=["a"], rows=[[1], [2]], execution_ms=3, truncated=False) monkeypatch.setattr(sql_cmd, "do_run", lambda *a, **k: fake) monkeypatch.setattr(sql_cmd, "validate_or_exit", lambda *a, **k: SimpleNamespace(ast=None)) monkeypatch.setattr(sql_cmd, "require_action", lambda *a, **k: None) monkeypatch.setattr( sql_cmd, "_load_config_or_exit", lambda *a, **k: SimpleNamespace(execution=SimpleNamespace(max_preview_rows=100)), ) f = tmp_path / "q.sql" f.write_text("SELECT 1") sql_cmd.preview_cmd(file=f, limit=None, offset=0, session=None, json_out=True, config=None) out = capsys.readouterr().out.strip() data = json.loads(out) # must parse: stdout pristine assert data["columns"] == ["a"] assert data["rows"] == [[1], [2]] assert data["execution_ms"] == 3 assert data["truncated"] is False assert "limit" in data assert data["offset"] == 0