Replaces the BigQuery wrap-view pattern with a discovery + scoped-fetch toolkit driven by the analyst's Claude session. Adds /api/v2/{catalog,schema,sample,scan,scan/estimate}, da catalog/schema/describe/fetch/snapshot/disk-info CLI commands, sqlglot-backed WHERE validator, process-local quota tracker, agent rails skill (cli/skills/agnes-data-querying.md). BREAKING: BQ wrap views off by default — set data_source.bigquery.legacy_wrap_views=true for one cycle. Backward-compat field_validator on primary_key. Catalog cache now matches documented 300s TTL with RBAC fresh per request. Cuts release v0.14.0.
94 lines
3.8 KiB
Python
94 lines
3.8 KiB
Python
"""Tests for /api/sync/manifest — query_mode and source_type per table.
|
|
|
|
These tests target the `_build_manifest_for_user` helper directly so they can
|
|
exercise the query_mode/source_type joining logic without going through the
|
|
HTTP layer. The CLI (Task 7) relies on these fields to skip remote-mode
|
|
tables during download.
|
|
"""
|
|
|
|
import importlib
|
|
|
|
|
|
def _reload_db_module(monkeypatch, tmp_path):
|
|
"""Point DATA_DIR at tmp_path and reload db module so paths take effect."""
|
|
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
|
(tmp_path / "state").mkdir(exist_ok=True)
|
|
import src.db as db_module
|
|
importlib.reload(db_module)
|
|
return db_module
|
|
|
|
|
|
def test_manifest_includes_query_mode_for_local_table(tmp_path, monkeypatch):
|
|
"""Local-mode table must surface query_mode='local' in manifest."""
|
|
db_module = _reload_db_module(monkeypatch, tmp_path)
|
|
|
|
from src.repositories.sync_state import SyncStateRepository
|
|
from src.repositories.table_registry import TableRegistryRepository
|
|
from app.api.sync import _build_manifest_for_user
|
|
|
|
conn = db_module.get_system_db()
|
|
try:
|
|
TableRegistryRepository(conn).register(
|
|
id="orders", name="orders", source_type="keboola",
|
|
bucket="sales", source_table="orders", query_mode="local",
|
|
)
|
|
SyncStateRepository(conn).update_sync(
|
|
table_id="orders", rows=10, file_size_bytes=1024, hash="abc",
|
|
)
|
|
admin = {"role": "admin", "email": "a@x.com"}
|
|
manifest = _build_manifest_for_user(conn, admin)
|
|
assert manifest["tables"]["orders"]["query_mode"] == "local"
|
|
assert manifest["tables"]["orders"]["source_type"] == "keboola"
|
|
assert manifest["tables"]["orders"]["hash"] == "abc"
|
|
assert manifest["tables"]["orders"]["rows"] == 10
|
|
assert manifest["tables"]["orders"]["size_bytes"] == 1024
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def test_manifest_includes_query_mode_for_remote_table(tmp_path, monkeypatch):
|
|
"""Remote-mode table (BQ) must surface query_mode='remote' in manifest."""
|
|
db_module = _reload_db_module(monkeypatch, tmp_path)
|
|
|
|
from src.repositories.sync_state import SyncStateRepository
|
|
from src.repositories.table_registry import TableRegistryRepository
|
|
from app.api.sync import _build_manifest_for_user
|
|
|
|
conn = db_module.get_system_db()
|
|
try:
|
|
TableRegistryRepository(conn).register(
|
|
id="bq_view", name="bq_view", source_type="bigquery",
|
|
bucket="ds", source_table="bq_view", query_mode="remote",
|
|
)
|
|
SyncStateRepository(conn).update_sync(
|
|
table_id="bq_view", rows=0, file_size_bytes=0, hash="",
|
|
)
|
|
admin = {"role": "admin", "email": "a@x.com"}
|
|
manifest = _build_manifest_for_user(conn, admin)
|
|
assert manifest["tables"]["bq_view"]["query_mode"] == "remote"
|
|
assert manifest["tables"]["bq_view"]["source_type"] == "bigquery"
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def test_manifest_defaults_query_mode_local_for_unregistered_state(tmp_path, monkeypatch):
|
|
"""Sync state without a corresponding registry row must default query_mode='local'.
|
|
|
|
Defensive: if registry lookup misses (deleted entry, race), don't break the manifest.
|
|
"""
|
|
db_module = _reload_db_module(monkeypatch, tmp_path)
|
|
|
|
from src.repositories.sync_state import SyncStateRepository
|
|
from app.api.sync import _build_manifest_for_user
|
|
|
|
conn = db_module.get_system_db()
|
|
try:
|
|
SyncStateRepository(conn).update_sync(
|
|
table_id="orphan", rows=0, file_size_bytes=0, hash="",
|
|
)
|
|
admin = {"role": "admin", "email": "a@x.com"}
|
|
manifest = _build_manifest_for_user(conn, admin)
|
|
assert manifest["tables"]["orphan"]["query_mode"] == "local"
|
|
assert manifest["tables"]["orphan"]["source_type"] == ""
|
|
finally:
|
|
conn.close()
|