"""Phase 5.a integration tests — error message decoding + PEP 249 classification. Server SQ_ERR responses get decoded to: - the appropriate PEP 249 exception class (Programming/Integrity/...) - a human-readable message with sqlcode, near-token, ISAM code, offset - structured ``sqlcode``, ``isamcode``, ``offset``, ``near`` attributes The connection survives errors — subsequent queries work normally. """ from __future__ import annotations import pytest import informix_db from tests.conftest import ConnParams pytestmark = pytest.mark.integration def _connect(conn_params: ConnParams) -> informix_db.Connection: return informix_db.connect( host=conn_params.host, port=conn_params.port, user=conn_params.user, password=conn_params.password, database=conn_params.database, server=conn_params.server, connect_timeout=10.0, read_timeout=10.0, ) def test_syntax_error_is_programming_error(conn_params: ConnParams) -> None: """sqlcode -201 (syntax error) → ProgrammingError.""" with _connect(conn_params) as conn: cur = conn.cursor() with pytest.raises(informix_db.ProgrammingError) as excinfo: cur.execute("SELEKT * FROM systables") e = excinfo.value assert e.sqlcode == -201 assert "syntax error" in str(e).lower() def test_table_not_found_is_programming_error(conn_params: ConnParams) -> None: """sqlcode -206 (table not found) → ProgrammingError; near-token has table name.""" with _connect(conn_params) as conn: cur = conn.cursor() with pytest.raises(informix_db.ProgrammingError) as excinfo: cur.execute("SELECT * FROM no_such_table_xyz") e = excinfo.value assert e.sqlcode == -206 assert e.near == "no_such_table_xyz" assert "not in the database" in str(e) def test_column_not_found_is_programming_error(conn_params: ConnParams) -> None: """sqlcode -217 (column not found) → ProgrammingError; near-token has column name.""" with _connect(conn_params) as conn: cur = conn.cursor() with pytest.raises(informix_db.ProgrammingError) as excinfo: cur.execute("SELECT no_such_column FROM systables") e = excinfo.value assert e.sqlcode == -217 assert e.near == "no_such_column" def test_unique_violation_is_integrity_error(conn_params: ConnParams) -> None: """sqlcode -268 (UNIQUE violation) → IntegrityError.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute("CREATE TEMP TABLE t_uniq (id INTEGER PRIMARY KEY)") cur.execute("INSERT INTO t_uniq VALUES (?)", (1,)) with pytest.raises(informix_db.IntegrityError) as excinfo: cur.execute("INSERT INTO t_uniq VALUES (?)", (1,)) e = excinfo.value assert e.sqlcode == -268 assert "duplicate" in str(e).lower() def test_not_null_violation_is_integrity_error(conn_params: ConnParams) -> None: """sqlcode -391 (NOT NULL violation) → IntegrityError.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute("CREATE TEMP TABLE t_nn (id INTEGER NOT NULL)") with pytest.raises(informix_db.IntegrityError) as excinfo: cur.execute("INSERT INTO t_nn VALUES (?)", (None,)) assert excinfo.value.sqlcode == -391 def test_commit_in_unlogged_db_is_operational_error(conn_params: ConnParams) -> None: """sqlcode -255 (no transaction) → OperationalError.""" with _connect(conn_params) as conn: with pytest.raises(informix_db.OperationalError) as excinfo: conn.commit() assert excinfo.value.sqlcode == -255 def test_connection_survives_query_error(conn_params: ConnParams) -> None: """Critical: a failed query must not poison the connection — subsequent queries must work.""" with _connect(conn_params) as conn: cur = conn.cursor() # First query fails with pytest.raises(informix_db.ProgrammingError): cur.execute("SELECT * FROM no_such_table") # Second query works cur.execute("SELECT 1 FROM systables WHERE tabid = 1") assert cur.fetchone() == (1,) def test_error_has_structured_fields(conn_params: ConnParams) -> None: """``sqlcode``, ``isamcode``, ``offset``, ``near`` are attached for inspection.""" with _connect(conn_params) as conn: cur = conn.cursor() with pytest.raises(informix_db.Error) as excinfo: cur.execute("SELECT * FROM no_such_table") e = excinfo.value # All four structured attributes present assert hasattr(e, "sqlcode") assert hasattr(e, "isamcode") assert hasattr(e, "offset") assert hasattr(e, "near") assert e.sqlcode == -206 assert e.near == "no_such_table"