|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
| 5 | +import sys |
| 6 | +from pathlib import Path |
| 7 | + |
| 8 | +import pytest |
| 9 | + |
5 | 10 | from .test_database_base import BaseDatabaseTestsWithLimit, DatabaseTestConfig |
6 | 11 |
|
7 | 12 |
|
@@ -128,3 +133,86 @@ def test_query_duckdb_invalid_query(self, duckdb_connection, cli_runner): |
128 | 133 | ) |
129 | 134 | # Should fail gracefully |
130 | 135 | assert result.returncode != 0 or "error" in result.stdout.lower() or "error" in result.stderr.lower() |
| 136 | + |
| 137 | + |
| 138 | +@pytest.mark.parametrize("use_worker", [True, False]) |
| 139 | +def test_duckdb_process_worker_no_lock(duckdb_db, use_worker): |
| 140 | + """Ensure DuckDB queries work with or without the process worker.""" |
| 141 | + if sys.platform.startswith("win"): |
| 142 | + pytest.skip("DuckDB file-lock behavior differs on Windows") |
| 143 | + |
| 144 | + from sqlit.domains.connections.app.session import ConnectionSession |
| 145 | + from sqlit.domains.connections.domain.config import ConnectionConfig, FileEndpoint |
| 146 | + from sqlit.domains.process_worker.app.process_worker_client import ProcessWorkerClient |
| 147 | + from sqlit.domains.process_worker.app.support import supports_process_worker |
| 148 | + from sqlit.domains.query.app.transaction import TransactionExecutor |
| 149 | + |
| 150 | + config = ConnectionConfig( |
| 151 | + name="duckdb_lock_test", |
| 152 | + db_type="duckdb", |
| 153 | + endpoint=FileEndpoint(path=str(duckdb_db)), |
| 154 | + ) |
| 155 | + |
| 156 | + session = ConnectionSession.create(config) |
| 157 | + try: |
| 158 | + outcome = None |
| 159 | + use_worker_effective = bool(use_worker and supports_process_worker(session.provider)) |
| 160 | + if use_worker_effective: |
| 161 | + client = ProcessWorkerClient() |
| 162 | + try: |
| 163 | + outcome = client.execute("SELECT 1", config, max_rows=1) |
| 164 | + finally: |
| 165 | + client.close() |
| 166 | + else: |
| 167 | + executor = TransactionExecutor(config=config, provider=session.provider) |
| 168 | + try: |
| 169 | + executor.execute("SELECT 1", max_rows=1) |
| 170 | + finally: |
| 171 | + executor.close() |
| 172 | + finally: |
| 173 | + session.close() |
| 174 | + |
| 175 | + if use_worker_effective: |
| 176 | + assert outcome is not None |
| 177 | + assert outcome.error is None, f"Unexpected error: {outcome.error}" |
| 178 | + |
| 179 | + |
| 180 | +def test_duckdb_schema_service_repo_file(tmp_path): |
| 181 | + """Validate explorer schema service against the repo DuckDB fixture.""" |
| 182 | + if sys.platform.startswith("win"): |
| 183 | + pytest.skip("DuckDB file-lock behavior differs on Windows") |
| 184 | + |
| 185 | + from sqlit.domains.connections.app.session import ConnectionSession |
| 186 | + from sqlit.domains.connections.domain.config import ConnectionConfig, FileEndpoint |
| 187 | + from sqlit.domains.explorer.app.schema_service import ExplorerSchemaService |
| 188 | + try: |
| 189 | + import duckdb # type: ignore |
| 190 | + except Exception: |
| 191 | + pytest.skip("duckdb is not installed") |
| 192 | + |
| 193 | + db_path = Path(tmp_path) / "cats.duckdb" |
| 194 | + conn = duckdb.connect(str(db_path)) |
| 195 | + conn.execute("CREATE TABLE cats (id INTEGER, name VARCHAR, motto VARCHAR)") |
| 196 | + conn.execute("INSERT INTO cats VALUES (1, 'Mochi', 'Nap hard, snack harder')") |
| 197 | + conn.execute("INSERT INTO cats VALUES (2, 'Nimbus', 'Gravity is optional')") |
| 198 | + conn.close() |
| 199 | + |
| 200 | + config = ConnectionConfig( |
| 201 | + name="duckdb_repo_fixture", |
| 202 | + db_type="duckdb", |
| 203 | + endpoint=FileEndpoint(path=str(db_path)), |
| 204 | + ) |
| 205 | + |
| 206 | + session = ConnectionSession.create(config) |
| 207 | + try: |
| 208 | + service = ExplorerSchemaService(session=session, object_cache={}) |
| 209 | + tables = service.list_folder_items("tables", None) |
| 210 | + table_names = {name for kind, _, name in tables if kind == "table"} |
| 211 | + assert "cats" in table_names |
| 212 | + |
| 213 | + schema = session.provider.capabilities.default_schema or "main" |
| 214 | + columns = service.list_columns(None, schema, "cats") |
| 215 | + column_names = {col.name for col in columns} |
| 216 | + assert {"id", "name", "motto"}.issubset(column_names) |
| 217 | + finally: |
| 218 | + session.close() |
0 commit comments