"""Phase 6.e integration tests — INTERVAL parameter encoding round-trip. The encoder produces a 2-byte-length-prefixed BCD payload identical in shape to DATETIME/DECIMAL (per the Phase 6.c discovery). These tests verify the round-trip: Python value → SQ_BIND wire bytes → Informix storage → SELECT → Python value. If the server silently drops a parametrized INSERT with no error, the encoder envelope is wrong (same diagnostic pattern as DECIMAL/DATETIME). """ from __future__ import annotations import datetime import pytest import informix_db from informix_db import IntervalYM 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_timedelta_param_round_trip(conn_params: ConnParams) -> None: """``datetime.timedelta`` as a bind parameter round-trips.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute( "CREATE TEMP TABLE t_iv_e " "(id INTEGER, span INTERVAL DAY(9) TO FRACTION(5))" ) cur.executemany( "INSERT INTO t_iv_e VALUES (?, ?)", [ (1, datetime.timedelta(days=3, hours=4, minutes=5, seconds=6)), (2, datetime.timedelta(days=0, hours=12)), (3, datetime.timedelta(seconds=45)), (4, datetime.timedelta(days=999_999_999)), ], ) cur.execute("SELECT id, span FROM t_iv_e ORDER BY id") rows = cur.fetchall() assert rows == [ (1, datetime.timedelta(days=3, hours=4, minutes=5, seconds=6)), (2, datetime.timedelta(days=0, hours=12)), (3, datetime.timedelta(seconds=45)), (4, datetime.timedelta(days=999_999_999)), ] def test_negative_timedelta_round_trip(conn_params: ConnParams) -> None: """Negative ``timedelta`` exercises the asymmetric base-100 complement encode.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute( "CREATE TEMP TABLE t_iv_e2 " "(id INTEGER, span INTERVAL DAY(9) TO FRACTION(5))" ) cur.executemany( "INSERT INTO t_iv_e2 VALUES (?, ?)", [ (1, datetime.timedelta(days=-3, hours=-4, minutes=-5, seconds=-6)), (2, datetime.timedelta(seconds=-45)), ], ) cur.execute("SELECT id, span FROM t_iv_e2 ORDER BY id") rows = cur.fetchall() assert rows == [ (1, datetime.timedelta(days=-3, hours=-4, minutes=-5, seconds=-6)), (2, datetime.timedelta(seconds=-45)), ] def test_timedelta_with_microseconds(conn_params: ConnParams) -> None: """timedelta down to 10us precision — Informix FRACTION(5) limit.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute( "CREATE TEMP TABLE t_iv_e3 " "(id INTEGER, span INTERVAL DAY(9) TO FRACTION(5))" ) cur.execute( "INSERT INTO t_iv_e3 VALUES (?, ?)", (1, datetime.timedelta(seconds=10, microseconds=500_000)), ) cur.execute("SELECT span FROM t_iv_e3") (val,) = cur.fetchone() assert val == datetime.timedelta(seconds=10, microseconds=500_000) def test_intervalym_param_round_trip(conn_params: ConnParams) -> None: """``IntervalYM`` as a bind parameter round-trips.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute( "CREATE TEMP TABLE t_iv_ym " "(id INTEGER, age INTERVAL YEAR(9) TO MONTH)" ) cur.executemany( "INSERT INTO t_iv_ym VALUES (?, ?)", [ (1, IntervalYM(months=5 * 12 + 3)), (2, IntervalYM(months=0)), (3, IntervalYM(months=12)), (4, IntervalYM(months=2026 * 12 + 7)), ], ) cur.execute("SELECT id, age FROM t_iv_ym ORDER BY id") rows = cur.fetchall() assert rows == [ (1, IntervalYM(months=5 * 12 + 3)), (2, IntervalYM(months=0)), (3, IntervalYM(months=12)), (4, IntervalYM(months=2026 * 12 + 7)), ] def test_intervalym_negative_round_trip(conn_params: ConnParams) -> None: """Negative ``IntervalYM`` round-trips.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute( "CREATE TEMP TABLE t_iv_ym2 " "(id INTEGER, age INTERVAL YEAR(9) TO MONTH)" ) cur.executemany( "INSERT INTO t_iv_ym2 VALUES (?, ?)", [ (1, IntervalYM(months=-(5 * 12 + 3))), (2, IntervalYM(months=-1)), ], ) cur.execute("SELECT id, age FROM t_iv_ym2 ORDER BY id") rows = cur.fetchall() assert rows == [ (1, IntervalYM(months=-(5 * 12 + 3))), (2, IntervalYM(months=-1)), ] def test_interval_in_where_clause(conn_params: ConnParams) -> None: """timedelta as a parameter in a WHERE clause.""" with _connect(conn_params) as conn: cur = conn.cursor() cur.execute( "CREATE TEMP TABLE t_iv_w " "(id INTEGER, span INTERVAL DAY(9) TO SECOND)" ) cur.executemany( "INSERT INTO t_iv_w VALUES (?, ?)", [ (1, datetime.timedelta(days=1)), (2, datetime.timedelta(days=10)), (3, datetime.timedelta(days=100)), ], ) cur.execute( "SELECT id FROM t_iv_w WHERE span > ? ORDER BY id", (datetime.timedelta(days=5),), ) assert cur.fetchall() == [(2,), (3,)]