Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import MagicMock

import pytest

from hotdata_runtime import QueryResult


@pytest.fixture
def sample_result() -> QueryResult:
return QueryResult(
columns=["n"],
rows=[[1]],
row_count=1,
result_id="res_1",
query_run_id="run_1",
execution_time_ms=10,
warning=None,
error_message=None,
)


@pytest.fixture
def mock_client(sample_result: QueryResult):
client = MagicMock()
client.workspace_id = "ws_test"
client.host = "https://api.hotdata.dev"
client.session_id = "sb_1"
client.execute_sql = MagicMock(return_value=sample_result)
client.connections.return_value.list_connections.return_value = SimpleNamespace(
connections=[]
)
return client
26 changes: 0 additions & 26 deletions tests/test_architecture_guardrails.py

This file was deleted.

138 changes: 138 additions & 0 deletions tests/test_hotdata_marimo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import MagicMock, patch

import pytest

import hotdata_marimo as hm
from hotdata_runtime import HotdataClient
from hotdata_marimo.display import _option_map_with_unique_labels
from hotdata_marimo.sql_engine import HotdataMarimoEngine
from hotdata_marimo.table_browser import _connection_options
from hotdata_marimo.workspace_selector import WorkspaceSelector, workspace_selector_from_env
from marimo._types.ids import VariableName


def _selection(*, workspace_id: str, source: str, workspaces: list | None = None):
return SimpleNamespace(
workspace_id=workspace_id,
source=source,
workspaces=workspaces or [],
)


def _workspace_row(name: str, public_id: str, *, active: bool = True):
return SimpleNamespace(name=name, public_id=public_id, active=active)


@pytest.mark.parametrize(
("labels", "expected"),
[
(
[("dup", "a"), ("dup", "b"), ("dup", "c")],
{"dup": "a", "dup (2)": "b", "dup (3)": "c"},
),
(
[],
{},
),
],
)
def test_option_map_with_unique_labels(labels, expected):
assert _option_map_with_unique_labels(labels) == expected


def test_connection_options_disambiguates_duplicate_names():
conns = [
SimpleNamespace(name="Warehouse", id="conn_1"),
SimpleNamespace(name="Warehouse", id="conn_2"),
SimpleNamespace(name="Analytics", id="conn_3"),
]
assert _connection_options(conns) == {
"Warehouse": "conn_1",
"Warehouse (conn_2)": "conn_2",
"Analytics": "conn_3",
}


@pytest.mark.parametrize(
("resolve", "expect_dropdown", "expected_workspace"),
[
(
_selection(workspace_id="ws_explicit", source="explicit_env"),
False,
"ws_explicit",
),
(
_selection(
workspace_id="ws_only",
source="active",
workspaces=[_workspace_row("Only", "ws_only")],
),
False,
"ws_only",
),
(
_selection(
workspace_id="ws_a",
source="active",
workspaces=[
_workspace_row("Alpha", "ws_a"),
_workspace_row("Beta", "ws_b", active=False),
],
),
True,
"ws_b",
),
],
)
def test_workspace_selector(resolve, expect_dropdown, expected_workspace):
pick = MagicMock()
pick.value = resolve.workspace_id
with patch(
"hotdata_marimo.workspace_selector.resolve_workspace_selection",
return_value=resolve,
), patch(
"hotdata_marimo.workspace_selector.mo.ui.dropdown",
return_value=pick,
):
selector = WorkspaceSelector(api_key="k")
if expect_dropdown:
pick.value = expected_workspace
assert (selector._pick is not None) is expect_dropdown
assert selector.workspace_id == expected_workspace
assert selector.client.workspace_id == expected_workspace


def test_workspace_selector_from_env_requires_api_key(monkeypatch: pytest.MonkeyPatch):
monkeypatch.delenv("HOTDATA_API_KEY", raising=False)
with pytest.raises(RuntimeError, match="HOTDATA_API_KEY"):
workspace_selector_from_env()


def test_register_hotdata_sql_engine_is_idempotent() -> None:
from marimo._sql.get_engines import SUPPORTED_ENGINES

hm.unregister_hotdata_sql_engine()
assert SUPPORTED_ENGINES.count(HotdataMarimoEngine) == 0
try:
hm.register_hotdata_sql_engine()
hm.register_hotdata_sql_engine()
assert SUPPORTED_ENGINES.count(HotdataMarimoEngine) == 1
finally:
hm.unregister_hotdata_sql_engine()


def test_hotdata_engine_display_name_in_marimo_ui(mock_client) -> None:
hm.register_hotdata_sql_engine()
try:
engine = HotdataMarimoEngine(mock_client, engine_name=VariableName("client"))
import marimo._sql.get_engines as ge
import marimo._runtime.runner.hooks_post_execution as hpe

for module in (ge, hpe):
conn = module.engine_to_data_source_connection(VariableName("client"), engine)
assert conn.display_name == "Hotdata"
finally:
hm.unregister_hotdata_sql_engine()
7 changes: 0 additions & 7 deletions tests/test_imports.py

This file was deleted.

31 changes: 0 additions & 31 deletions tests/test_options.py

This file was deleted.

71 changes: 71 additions & 0 deletions tests/test_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from __future__ import annotations

import importlib
import re
from pathlib import Path

import pytest
from importlib.metadata import version as dist_version

import hotdata_marimo as hm


REPO_ROOT = Path(__file__).resolve().parents[1]
SOURCE_ROOT = REPO_ROOT / "hotdata_marimo"
_RUNTIME_SUBMODULE = re.compile(
r"(?m)^\s*(?:from\s+hotdata_runtime\.(client|env|result|health)\s+import"
r"|import\s+hotdata_runtime\.(client|env|result|health)(?:\s|$|,|as))"
)


def test_version_is_pep440_core():
assert re.fullmatch(r"\d+\.\d+\.\d+(\+.*)?", hm.__version__)


def test_version_matches_distribution_metadata():
assert dist_version("hotdata-marimo") == hm.__version__


@pytest.mark.parametrize("name", hm.__all__)
def test_public_export_is_importable(name: str):
assert hasattr(hm, name), f"missing export: {name}"
assert getattr(hm, name) is not None


def test_runtime_primitives_are_reexported():
from hotdata_runtime import HotdataClient, QueryResult, from_env

assert hm.HotdataClient is HotdataClient
assert hm.QueryResult is QueryResult
assert hm.from_env is from_env


@pytest.mark.parametrize(
("alias", "target"),
[
("hotdata_sql_editor", "sql_editor"),
("hotdata_table_browser", "table_browser"),
("hotdata_query_result", "query_result"),
("hotdata_connection_picker", "connection_picker"),
("hotdata_workspace_selector", "workspace_selector_from_env"),
("hotdata_recent_results", "recent_results"),
],
)
def test_mo_ui_aliases_match_public_helpers(alias: str, target: str):
assert getattr(hm, alias) is getattr(hm, target)


def test_source_uses_hotdata_runtime_root_imports():
violations: list[str] = []
for path in SOURCE_ROOT.rglob("*.py"):
if _RUNTIME_SUBMODULE.search(path.read_text(encoding="utf-8")):
violations.append(str(path.relative_to(REPO_ROOT)))
assert not violations, (
"Use `from hotdata_runtime import ...` in package source; "
f"found submodule imports in: {', '.join(violations)}"
)


def test_no_stale_submodule_surface():
with pytest.raises(ModuleNotFoundError):
importlib.import_module("hotdata_marimo.client")
45 changes: 0 additions & 45 deletions tests/test_sql_engine_registry.py

This file was deleted.

13 changes: 0 additions & 13 deletions tests/test_version.py

This file was deleted.

Loading