agnes-the-ai-analyst/tests/test_v2_schema_materialized_local.py
ZdenekSrotyr 12db59127b
release: 0.53.0 — close Tier B trackers (#259-#261) + admin UI fix (#265) (#267)
* release: 0.53.0 — Tier B trackers + admin UI bugfix

Closes #259 (init resume sentinel), #260 (startup parquet-lock sweep),
#261 (materialized schema uses local parquet, not BQ), #265 (admin
tables apostrophe → HTML-entity escape).

Tracker notes: #262 closed as obsolete (pre-empted by 0.51.0 changes),
#266 left open pending UX clarification.

* fix(init): move resume sentinel from .agnes/ to .claude/

The clean-install integration test (test_clean_install_integration.py)
forbids creating .agnes/ in the workspace root via its
forbidden_unconditional list — that path is reserved for ~/.agnes/ in
the user's HOME (marketplace clone, CA bundle).

.claude/ is already created by agnes init for settings.json + hooks,
so dropping init-complete next to those keeps the resume sentinel
consistent with the rest of Claude Code's workspace surface and lets
the clean-install assertions pass.

Issue #259.

* docs(changelog): point #259 entry at new .claude/init-complete path

Follows the sentinel move from .agnes/ → .claude/ to keep the changelog
in sync with what 0.53.0 actually ships.
2026-05-12 16:28:41 +02:00

64 lines
2.4 KiB
Python

"""Materialized BQ tables read schema from the local parquet, not from
BigQuery INFORMATION_SCHEMA. Issue #261 fix verification."""
from pathlib import Path
from unittest.mock import patch
def test_materialized_bq_schema_does_not_call_bq(tmp_path: Path, monkeypatch):
"""When `query_mode='materialized'`, `build_schema_uncached` must
bypass `_fetch_bq_schema` and read from the local parquet."""
monkeypatch.setenv("DATA_DIR", str(tmp_path))
# Create a tiny parquet with two columns so the parquet reader has
# something to DESCRIBE.
import duckdb
parquet_dir = tmp_path / "extracts" / "bigquery" / "data"
parquet_dir.mkdir(parents=True)
parquet_path = parquet_dir / "orders.parquet"
conn = duckdb.connect(":memory:")
conn.execute(
f"COPY (SELECT 1 AS event_id, 'USD' AS currency) TO '{parquet_path}' (FORMAT PARQUET)"
)
conn.close()
from app.api.v2_schema import build_schema_uncached
fake_row = {
"id": "orders",
"source_type": "bigquery",
"query_mode": "materialized",
"bucket": "dwh",
"source_table": "orders",
}
fake_bq = object() # never used — BQ path must be skipped
with patch("app.api.v2_schema._fetch_bq_schema") as mock_bq_schema, \
patch("app.api.v2_schema._fetch_bq_table_options") as mock_bq_opts:
result = build_schema_uncached(
conn=None, table_id="orders", bq=fake_bq, row=fake_row,
)
mock_bq_schema.assert_not_called()
mock_bq_opts.assert_not_called()
cols = {c["name"] for c in result["columns"]}
assert cols == {"event_id", "currency"}
assert result["sql_flavor"] == "duckdb"
def test_remote_bq_schema_still_calls_bq(tmp_path: Path, monkeypatch):
"""Sanity: remote BQ tables (not materialized) still go through BQ."""
monkeypatch.setenv("DATA_DIR", str(tmp_path))
from app.api.v2_schema import build_schema_uncached
fake_row = {
"id": "ue",
"source_type": "bigquery",
"query_mode": "remote",
"bucket": "finance",
"source_table": "ue",
}
fake_bq = object()
with patch(
"app.api.v2_schema._fetch_bq_schema", return_value=[],
) as mock_bq_schema, patch(
"app.api.v2_schema._fetch_bq_table_options", return_value={},
):
build_schema_uncached(conn=None, table_id="ue", bq=fake_bq, row=fake_row)
mock_bq_schema.assert_called_once()