Skip to content

Commit 87deab3

Browse files
authored
Merge pull request #2 from hotdata-dev/feat/use-runtime-workspace-selection
2 parents 538b292 + 66915c3 commit 87deab3

8 files changed

Lines changed: 244 additions & 122 deletions

tests/conftest.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from __future__ import annotations
2+
3+
from types import SimpleNamespace
4+
from unittest.mock import MagicMock
5+
6+
import pytest
7+
8+
from hotdata_runtime import QueryResult
9+
10+
11+
@pytest.fixture
12+
def sample_result() -> QueryResult:
13+
return QueryResult(
14+
columns=["n"],
15+
rows=[[1]],
16+
row_count=1,
17+
result_id="res_1",
18+
query_run_id="run_1",
19+
execution_time_ms=10,
20+
warning=None,
21+
error_message=None,
22+
)
23+
24+
25+
@pytest.fixture
26+
def mock_client(sample_result: QueryResult):
27+
client = MagicMock()
28+
client.workspace_id = "ws_test"
29+
client.host = "https://api.hotdata.dev"
30+
client.session_id = "sb_1"
31+
client.execute_sql = MagicMock(return_value=sample_result)
32+
client.connections.return_value.list_connections.return_value = SimpleNamespace(
33+
connections=[]
34+
)
35+
return client

tests/test_architecture_guardrails.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

tests/test_hotdata_marimo.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
from __future__ import annotations
2+
3+
from types import SimpleNamespace
4+
from unittest.mock import MagicMock, patch
5+
6+
import pytest
7+
8+
import hotdata_marimo as hm
9+
from hotdata_runtime import HotdataClient
10+
from hotdata_marimo.display import _option_map_with_unique_labels
11+
from hotdata_marimo.sql_engine import HotdataMarimoEngine
12+
from hotdata_marimo.table_browser import _connection_options
13+
from hotdata_marimo.workspace_selector import WorkspaceSelector, workspace_selector_from_env
14+
from marimo._types.ids import VariableName
15+
16+
17+
def _selection(*, workspace_id: str, source: str, workspaces: list | None = None):
18+
return SimpleNamespace(
19+
workspace_id=workspace_id,
20+
source=source,
21+
workspaces=workspaces or [],
22+
)
23+
24+
25+
def _workspace_row(name: str, public_id: str, *, active: bool = True):
26+
return SimpleNamespace(name=name, public_id=public_id, active=active)
27+
28+
29+
@pytest.mark.parametrize(
30+
("labels", "expected"),
31+
[
32+
(
33+
[("dup", "a"), ("dup", "b"), ("dup", "c")],
34+
{"dup": "a", "dup (2)": "b", "dup (3)": "c"},
35+
),
36+
(
37+
[],
38+
{},
39+
),
40+
],
41+
)
42+
def test_option_map_with_unique_labels(labels, expected):
43+
assert _option_map_with_unique_labels(labels) == expected
44+
45+
46+
def test_connection_options_disambiguates_duplicate_names():
47+
conns = [
48+
SimpleNamespace(name="Warehouse", id="conn_1"),
49+
SimpleNamespace(name="Warehouse", id="conn_2"),
50+
SimpleNamespace(name="Analytics", id="conn_3"),
51+
]
52+
assert _connection_options(conns) == {
53+
"Warehouse": "conn_1",
54+
"Warehouse (conn_2)": "conn_2",
55+
"Analytics": "conn_3",
56+
}
57+
58+
59+
@pytest.mark.parametrize(
60+
("resolve", "expect_dropdown", "expected_workspace"),
61+
[
62+
(
63+
_selection(workspace_id="ws_explicit", source="explicit_env"),
64+
False,
65+
"ws_explicit",
66+
),
67+
(
68+
_selection(
69+
workspace_id="ws_only",
70+
source="active",
71+
workspaces=[_workspace_row("Only", "ws_only")],
72+
),
73+
False,
74+
"ws_only",
75+
),
76+
(
77+
_selection(
78+
workspace_id="ws_a",
79+
source="active",
80+
workspaces=[
81+
_workspace_row("Alpha", "ws_a"),
82+
_workspace_row("Beta", "ws_b", active=False),
83+
],
84+
),
85+
True,
86+
"ws_b",
87+
),
88+
],
89+
)
90+
def test_workspace_selector(resolve, expect_dropdown, expected_workspace):
91+
pick = MagicMock()
92+
pick.value = resolve.workspace_id
93+
with patch(
94+
"hotdata_marimo.workspace_selector.resolve_workspace_selection",
95+
return_value=resolve,
96+
), patch(
97+
"hotdata_marimo.workspace_selector.mo.ui.dropdown",
98+
return_value=pick,
99+
):
100+
selector = WorkspaceSelector(api_key="k")
101+
if expect_dropdown:
102+
pick.value = expected_workspace
103+
assert (selector._pick is not None) is expect_dropdown
104+
assert selector.workspace_id == expected_workspace
105+
assert selector.client.workspace_id == expected_workspace
106+
107+
108+
def test_workspace_selector_from_env_requires_api_key(monkeypatch: pytest.MonkeyPatch):
109+
monkeypatch.delenv("HOTDATA_API_KEY", raising=False)
110+
with pytest.raises(RuntimeError, match="HOTDATA_API_KEY"):
111+
workspace_selector_from_env()
112+
113+
114+
def test_register_hotdata_sql_engine_is_idempotent() -> None:
115+
from marimo._sql.get_engines import SUPPORTED_ENGINES
116+
117+
hm.unregister_hotdata_sql_engine()
118+
assert SUPPORTED_ENGINES.count(HotdataMarimoEngine) == 0
119+
try:
120+
hm.register_hotdata_sql_engine()
121+
hm.register_hotdata_sql_engine()
122+
assert SUPPORTED_ENGINES.count(HotdataMarimoEngine) == 1
123+
finally:
124+
hm.unregister_hotdata_sql_engine()
125+
126+
127+
def test_hotdata_engine_display_name_in_marimo_ui(mock_client) -> None:
128+
hm.register_hotdata_sql_engine()
129+
try:
130+
engine = HotdataMarimoEngine(mock_client, engine_name=VariableName("client"))
131+
import marimo._sql.get_engines as ge
132+
import marimo._runtime.runner.hooks_post_execution as hpe
133+
134+
for module in (ge, hpe):
135+
conn = module.engine_to_data_source_connection(VariableName("client"), engine)
136+
assert conn.display_name == "Hotdata"
137+
finally:
138+
hm.unregister_hotdata_sql_engine()

tests/test_imports.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

tests/test_options.py

Lines changed: 0 additions & 31 deletions
This file was deleted.

tests/test_package.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from __future__ import annotations
2+
3+
import importlib
4+
import re
5+
from pathlib import Path
6+
7+
import pytest
8+
from importlib.metadata import version as dist_version
9+
10+
import hotdata_marimo as hm
11+
12+
13+
REPO_ROOT = Path(__file__).resolve().parents[1]
14+
SOURCE_ROOT = REPO_ROOT / "hotdata_marimo"
15+
_RUNTIME_SUBMODULE = re.compile(
16+
r"(?m)^\s*(?:from\s+hotdata_runtime\.(client|env|result|health)\s+import"
17+
r"|import\s+hotdata_runtime\.(client|env|result|health)(?:\s|$|,|as))"
18+
)
19+
20+
21+
def test_version_is_pep440_core():
22+
assert re.fullmatch(r"\d+\.\d+\.\d+(\+.*)?", hm.__version__)
23+
24+
25+
def test_version_matches_distribution_metadata():
26+
assert dist_version("hotdata-marimo") == hm.__version__
27+
28+
29+
@pytest.mark.parametrize("name", hm.__all__)
30+
def test_public_export_is_importable(name: str):
31+
assert hasattr(hm, name), f"missing export: {name}"
32+
assert getattr(hm, name) is not None
33+
34+
35+
def test_runtime_primitives_are_reexported():
36+
from hotdata_runtime import HotdataClient, QueryResult, from_env
37+
38+
assert hm.HotdataClient is HotdataClient
39+
assert hm.QueryResult is QueryResult
40+
assert hm.from_env is from_env
41+
42+
43+
@pytest.mark.parametrize(
44+
("alias", "target"),
45+
[
46+
("hotdata_sql_editor", "sql_editor"),
47+
("hotdata_table_browser", "table_browser"),
48+
("hotdata_query_result", "query_result"),
49+
("hotdata_connection_picker", "connection_picker"),
50+
("hotdata_workspace_selector", "workspace_selector_from_env"),
51+
("hotdata_recent_results", "recent_results"),
52+
],
53+
)
54+
def test_mo_ui_aliases_match_public_helpers(alias: str, target: str):
55+
assert getattr(hm, alias) is getattr(hm, target)
56+
57+
58+
def test_source_uses_hotdata_runtime_root_imports():
59+
violations: list[str] = []
60+
for path in SOURCE_ROOT.rglob("*.py"):
61+
if _RUNTIME_SUBMODULE.search(path.read_text(encoding="utf-8")):
62+
violations.append(str(path.relative_to(REPO_ROOT)))
63+
assert not violations, (
64+
"Use `from hotdata_runtime import ...` in package source; "
65+
f"found submodule imports in: {', '.join(violations)}"
66+
)
67+
68+
69+
def test_no_stale_submodule_surface():
70+
with pytest.raises(ModuleNotFoundError):
71+
importlib.import_module("hotdata_marimo.client")

tests/test_sql_engine_registry.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

tests/test_version.py

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)