"""Shared fixtures for bitable unit tests. Provides: - ``bitable_db``: initialized BitableDB (skips if PG unavailable) - ``bitable_service``: BitableService backed by bitable_db - Factory functions: make_table, make_field, make_record, make_formula_field """ from __future__ import annotations import os import pytest from agentkit.bitable.models import FieldOwner, FieldType def _pg_available() -> bool: """Check if PostgreSQL is reachable via DATABASE_URL env var.""" url = os.environ.get("DATABASE_URL") or os.environ.get("AGENTKIT_DATABASE_URL") return bool(url) @pytest.fixture async def bitable_db(): """Initialize a fresh bitable DB for each test (skips if PG unavailable).""" if not _pg_available(): pytest.skip("PostgreSQL not available (set DATABASE_URL)") from agentkit.bitable.db import BitableDB db = BitableDB() try: await db.init() # Clean slate: drop and recreate bitable schema from sqlalchemy import text async with db.engine.begin() as conn: await conn.execute(text("DROP SCHEMA IF EXISTS bitable CASCADE")) await db.init() # re-create fresh yield db finally: # Cleanup from sqlalchemy import text if db.engine is not None: async with db.engine.begin() as conn: await conn.execute(text("DROP SCHEMA IF EXISTS bitable CASCADE")) await db.close() @pytest.fixture async def bitable_service(bitable_db): """BitableService backed by the test bitable_db.""" from agentkit.bitable.service import BitableService yield BitableService(bitable_db) # ── Factory fixtures ─────────────────────────────────────── @pytest.fixture def make_table(bitable_service): """Factory: create a table and return it.""" counter = [0] async def _make( name: str | None = None, description: str = "", primary_key_field_id: str | None = None, ): counter[0] += 1 return await bitable_service.create_table( name=name or f"test_table_{counter[0]}", description=description, primary_key_field_id=primary_key_field_id, ) return _make @pytest.fixture def make_field(bitable_service): """Factory: create a field and return it.""" counter = [0] async def _make( table_id: str, name: str | None = None, field_type: FieldType = FieldType.text, config: dict | None = None, owner: FieldOwner = FieldOwner.agent, ): counter[0] += 1 return await bitable_service.create_field( table_id=table_id, name=name or f"field_{counter[0]}", field_type=field_type, config=config or {}, owner=owner, ) return _make @pytest.fixture def make_record(bitable_service): """Factory: create a record and return it.""" counter = [0] async def _make(table_id: str, values: dict | None = None): counter[0] += 1 return await bitable_service.create_record( table_id=table_id, values=values or {}, ) return _make @pytest.fixture def make_formula_field(bitable_service): """Factory: create a formula field and return it.""" counter = [0] async def _make( table_id: str, name: str | None = None, formula_expr: str = "=1+1", ): counter[0] += 1 return await bitable_service.create_field( table_id=table_id, name=name or f"calc_{counter[0]}", field_type=FieldType.formula, config={"formula_expr": formula_expr}, owner=FieldOwner.user, ) return _make