"""Tests for admin metadata API — column metadata CRUD and push.""" import pytest def _auth(token): return {"Authorization": f"Bearer {token}"} def _register_table(c, token, table_name="test_table"): """Helper to register a table in the registry.""" resp = c.post( "/api/admin/register-table", json={"name": table_name, "source_type": "keboola", "bucket": "in.c-test", "source_table": table_name, "query_mode": "local"}, headers=_auth(token), ) return resp.json().get("id", table_name.lower()) class TestGetMetadata: def test_get_metadata_empty(self, seeded_app): c = seeded_app["client"] token = seeded_app["admin_token"] resp = c.get("/api/admin/metadata/some_table", headers=_auth(token)) assert resp.status_code == 200 data = resp.json() assert data["table_id"] == "some_table" assert "columns" in data assert isinstance(data["columns"], list) def test_get_metadata_requires_auth(self, seeded_app): c = seeded_app["client"] resp = c.get("/api/admin/metadata/some_table") assert resp.status_code == 401 def test_get_metadata_analyst_allowed(self, seeded_app): """GET metadata is allowed for authenticated users (not admin-only).""" c = seeded_app["client"] token = seeded_app["analyst_token"] resp = c.get("/api/admin/metadata/some_table", headers=_auth(token)) assert resp.status_code == 200 class TestSaveMetadata: def test_save_column_metadata_as_admin(self, seeded_app): c = seeded_app["client"] token = seeded_app["admin_token"] table_id = _register_table(c, token, "orders_meta") resp = c.post( f"/api/admin/metadata/{table_id}", json={ "columns": [ {"column_name": "id", "basetype": "INTEGER", "description": "Primary key", "confidence": "manual"}, {"column_name": "name", "basetype": "VARCHAR", "description": "Customer name"}, ] }, headers=_auth(token), ) assert resp.status_code == 200 data = resp.json() assert data["status"] == "ok" assert data["count"] == 2 def test_save_metadata_analyst_gets_403(self, seeded_app): c = seeded_app["client"] token = seeded_app["analyst_token"] resp = c.post( "/api/admin/metadata/some_table", json={"columns": [{"column_name": "id", "basetype": "INTEGER"}]}, headers=_auth(token), ) assert resp.status_code == 403 def test_save_metadata_requires_auth(self, seeded_app): c = seeded_app["client"] resp = c.post( "/api/admin/metadata/some_table", json={"columns": [{"column_name": "id"}]}, ) assert resp.status_code == 401 def test_save_metadata_missing_columns_returns_422(self, seeded_app): c = seeded_app["client"] token = seeded_app["admin_token"] resp = c.post( "/api/admin/metadata/some_table", json={}, # missing 'columns' headers=_auth(token), ) assert resp.status_code == 422 def test_save_then_get_metadata(self, seeded_app): """Save metadata then verify it can be retrieved.""" c = seeded_app["client"] token = seeded_app["admin_token"] table_id = _register_table(c, token, "round_trip_table") c.post( f"/api/admin/metadata/{table_id}", json={ "columns": [ {"column_name": "amount", "basetype": "DECIMAL", "description": "Order amount"}, ] }, headers=_auth(token), ) resp = c.get(f"/api/admin/metadata/{table_id}", headers=_auth(token)) assert resp.status_code == 200 columns = resp.json()["columns"] assert len(columns) >= 1 col_names = [c_["column_name"] for c_ in columns] assert "amount" in col_names class TestPushMetadata: def test_push_metadata_table_not_found_returns_404(self, seeded_app): c = seeded_app["client"] token = seeded_app["admin_token"] resp = c.post("/api/admin/metadata/nonexistent_table/push", headers=_auth(token)) assert resp.status_code == 404 def test_push_metadata_non_keboola_returns_400(self, seeded_app, monkeypatch): """Push is only supported for keboola tables. Registering a BQ row through the API now requires a configured BQ project + dataset + source_table (issue #108 M1 added strict validation), so we seed the row directly via the repository to keep this test focused on the push-path behavior under test. """ from src.db import get_system_db from src.repositories.table_registry import TableRegistryRepository c = seeded_app["client"] token = seeded_app["admin_token"] conn = get_system_db() try: TableRegistryRepository(conn).register( id="bq_table", name="bq_table", source_type="bigquery", bucket="analytics", source_table="bq_table", query_mode="remote", profile_after_sync=False, ) finally: conn.close() resp = c.post("/api/admin/metadata/bq_table/push", headers=_auth(token)) assert resp.status_code == 400 assert "keboola" in resp.json()["detail"].lower() def test_push_metadata_analyst_gets_403(self, seeded_app): c = seeded_app["client"] token = seeded_app["analyst_token"] resp = c.post("/api/admin/metadata/some_table/push", headers=_auth(token)) assert resp.status_code == 403 def test_push_metadata_requires_auth(self, seeded_app): c = seeded_app["client"] resp = c.post("/api/admin/metadata/some_table/push") assert resp.status_code == 401 def test_push_keboola_table_without_env_vars_returns_500(self, seeded_app): """Keboola table without env vars configured should return 500.""" c = seeded_app["client"] token = seeded_app["admin_token"] table_id = _register_table(c, token, "kbc_push_table") # Should fail with 500 because KBC_STACK_URL and KBC_STORAGE_TOKEN are not set resp = c.post(f"/api/admin/metadata/{table_id}/push", headers=_auth(token)) assert resp.status_code == 500