Kansho/backend/tests/test_sql_compat.py
Lars 36b9a2e424
Some checks failed
Deploy Development / deploy (push) Successful in 55s
Test Suite / backend-postgres (push) Failing after 7s
Test Suite / frontend-build (push) Successful in 16s
Run the backend suite on Dev Postgres instead of isolated SQLite.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 14:28:17 +02:00

83 lines
3.6 KiB
Python

"""SQL dialect adapter for the SQLite/Postgres dual backend."""
from __future__ import annotations
import os
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from sql_compat import adapt_sql, split_sql, sqlite_schema_to_postgres, use_postgres
def expect(ok: bool, message: str) -> None:
if not ok:
raise SystemExit(f"FAIL: {message}")
print(f"OK {message}")
def main() -> None:
os.environ.pop("KANSHO_DB_BACKEND", None)
os.environ.pop("KANSHO_DB_PATH", None)
os.environ.pop("DB_HOST", None)
os.environ.pop("DATABASE_URL", None)
expect(use_postgres() is False, "default engine is sqlite")
os.environ["KANSHO_DB_PATH"] = "/tmp/isolated.sqlite"
os.environ["DB_HOST"] = "postgres"
expect(use_postgres() is False, "KANSHO_DB_PATH without backend keeps Windows local on sqlite")
os.environ["KANSHO_DB_BACKEND"] = "postgres"
expect(use_postgres() is True, "explicit postgres backend wins")
os.environ["KANSHO_DB_BACKEND"] = "sqlite"
expect(use_postgres() is False, "explicit sqlite backend wins")
os.environ.pop("KANSHO_DB_BACKEND", None)
os.environ.pop("KANSHO_DB_PATH", None)
expect(use_postgres() is True, "DB_HOST alone selects postgres")
os.environ.pop("DB_HOST", None)
translated = sqlite_schema_to_postgres("created TEXT NOT NULL DEFAULT (datetime('now'))")
expect("CURRENT_TIMESTAMP::text" in translated, "schema datetime default")
expect("datetime('now')" not in translated, "no sqlite datetime left in schema")
ignore = adapt_sql("INSERT OR IGNORE INTO tiers (id) VALUES (?)")
expect("INSERT INTO tiers" in ignore, "insert or ignore becomes insert")
expect("ON CONFLICT DO NOTHING" in ignore, "conflict do nothing appended")
expect("%s" in ignore and "?" not in ignore, "placeholders converted")
upsert = adapt_sql(
"INSERT INTO app_settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
)
expect("ON CONFLICT (key)" in upsert, "on conflict parenthesis spacing")
expect(upsert.count("ON CONFLICT") == 1, "existing on conflict not duplicated")
expect("%s" in upsert, "upsert placeholders")
quoted = adapt_sql("SELECT '?' AS q, name FROM profiles WHERE id = ?")
expect("'?'" in quoted, "question mark inside quotes kept")
expect(quoted.endswith("%s") or quoted.rstrip().endswith("%s"), "trailing placeholder converted")
case_sql = adapt_sql("UPDATE t SET x = CASE WHEN ? = 1 THEN ? ELSE x END")
expect("CASE WHEN %s = 1 THEN %s ELSE x END" in case_sql, "boolean-safe case placeholder")
rowid_sql = adapt_sql("SELECT e.* FROM journal_entries e ORDER BY e.created, e.rowid")
expect("e.rowid" not in rowid_sql, "sqlite rowid removed for postgres")
expect("ORDER BY e.created, e.id" in rowid_sql, "rowid becomes primary id")
parts = split_sql("CREATE TABLE a (id TEXT); CREATE TABLE b (id TEXT);")
expect(parts == ["CREATE TABLE a (id TEXT)", "CREATE TABLE b (id TEXT)"], "split two statements")
schema = (ROOT / "schema.sql").read_text(encoding="utf-8")
pg_schema = sqlite_schema_to_postgres(schema)
expect("datetime('now')" not in pg_schema, "translated schema has no sqlite datetime")
expect("CURRENT_TIMESTAMP::text" in pg_schema, "translated schema uses timestamp text default")
expect(len(split_sql(pg_schema)) > 40, "schema splits into many statements")
from sqlite_to_postgres import TABLES
tables = re.findall(r"CREATE TABLE IF NOT EXISTS (\w+)", schema)
expect(tables == list(TABLES), "import table order matches schema.sql")
print("sql compat: OK")
if __name__ == "__main__":
main()