Coverage for tests/query/test_insert_execution.py: 100%
65 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 21:13 +0300
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 21:13 +0300
1"""Insert query construction, compilation, and execution tests."""
3from __future__ import annotations
5from pathlib import Path
6from sqlite3 import connect
7from tempfile import TemporaryDirectory
9from snektest import assert_eq, assert_in, assert_is_none, assert_raises, test
11from snekql import (
12 MISSING,
13 Database,
14 ExecutionError,
15 Fetched,
16 Integer,
17 Model,
18 Pending,
19 Text,
20 insert,
21)
22from snekql.query import compile_write_sql
23from tests.helpers import NULL_LOGGER
26def _fetch_rows(database_path: Path, sql: str) -> list[tuple[object, ...]]:
27 connection = connect(database_path)
28 try:
29 cursor = connection.execute(sql)
30 return [tuple(row) for row in cursor.fetchall()]
31 finally:
32 connection.close()
35@test(mark="fast")
36def insert_compilation_omits_missing_and_quotes_identifiers() -> None:
37 """Compiled insert SQL targets the model table and omits MISSING fields."""
39 class Order[S = Pending](Model[S, "Order[Fetched]"]):
40 """Table model with identifiers that must be quoted."""
42 __tablename__ = "select"
43 id: Order.GenCol[int] = Integer(primary_key=True, default=MISSING)
44 where: Order.Col[str] = Text(nullable=False)
46 sql, params = compile_write_sql(insert(Order(where="x")))
48 assert_eq(sql, 'INSERT INTO "select" ("where") VALUES (?)')
49 assert_eq(params, ("x",))
52@test(mark="fast")
53def insert_compilation_uses_default_values_when_every_field_is_missing() -> None:
54 """An all-generated model compiles to SQLite DEFAULT VALUES."""
56 class AuditLog[S = Pending](Model[S, "AuditLog[Fetched]"]):
57 """Table model with no explicit insertable values."""
59 id: AuditLog.GenCol[int] = Integer(primary_key=True, default=MISSING)
61 sql, params = compile_write_sql(insert(AuditLog()))
63 assert_eq(sql, 'INSERT INTO "audit_log" DEFAULT VALUES')
64 assert_eq(params, ())
67@test(mark="medium")
68async def insert_execution_includes_defaults_and_returns_none() -> None:
69 """Executing an insert persists Python defaults and default-factory values."""
71 class User[S = Pending](Model[S, "User[Fetched]"]):
72 """Table model with explicit, default, and generated fields."""
74 id: User.GenCol[int] = Integer(primary_key=True, default=MISSING)
75 email: User.Col[str] = Text(nullable=False)
76 label: User.Col[str] = Text(nullable=False, default_factory=lambda: "fresh")
77 status: User.Col[str] = Text(nullable=False, default="active")
79 with TemporaryDirectory() as directory:
80 database_path = Path(directory) / "app.db"
81 database = await Database.initialize(
82 logger=NULL_LOGGER, database=database_path, models=[User]
83 )
84 try:
85 async with database.transaction() as tx:
86 result = await tx.execute(insert(User(email="a@example.com")))
87 finally:
88 await database.close()
90 rows = _fetch_rows(
91 database_path,
92 'SELECT "email", "label", "status" FROM "user"',
93 )
95 assert_is_none(result)
96 assert_eq(rows, [("a@example.com", "fresh", "active")])
99@test(mark="medium")
100async def execution_errors_preserve_insert_sql_and_params() -> None:
101 """SQLite insert failures are wrapped with SQL and parameter context."""
103 class User[S = Pending](Model[S, "User[Fetched]"]):
104 """Table model used to trigger a duplicate primary key failure."""
106 id: User.GenCol[int] = Integer(primary_key=True, default=MISSING)
107 email: User.Col[str] = Text(nullable=False)
109 with TemporaryDirectory() as directory:
110 database_path = Path(directory) / "app.db"
111 database = await Database.initialize(
112 logger=NULL_LOGGER, database=database_path, models=[User]
113 )
114 try:
115 async with database.transaction() as tx:
116 await tx.execute(insert(User(id=1, email="first@example.com")))
117 with assert_raises(ExecutionError) as caught_error:
118 await tx.execute(
119 insert(User(id=1, email="second@example.com")),
120 )
121 finally:
122 await database.close()
124 error = caught_error.exception
125 assert_in('INSERT INTO "user"', error.sql)
126 assert_in("?", error.sql)
127 assert_eq(error.params, (1, "second@example.com"))