Skip to content

Commit 5b18aa9

Browse files
committed
Increasing code coverage
1 parent 1d5981b commit 5b18aa9

File tree

1 file changed

+175
-39
lines changed

1 file changed

+175
-39
lines changed

tests/test_013_encoding_decoding.py

Lines changed: 175 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Comprehensive Encoding/Decoding Test Suite
33
44
This consolidated module provides complete testing for encoding/decoding functionality
5-
in mssql-python, ensuring pyodbc compatibility, thread safety, and connection pooling support.
5+
in mssql-python, thread safety, and connection pooling support.
66
77
Total Tests: 131
88
@@ -43,12 +43,6 @@
4343
- European: Latin-1, CP1252, ISO-8859 family
4444
- UTF-8 and UTF-16 variants
4545
46-
6. PYODBC COMPATIBILITY (12 tests)
47-
- No automatic fallback behavior
48-
- UTF-16 BOM rejection for SQL_WCHAR
49-
- SQL_WMETADATA flexibility
50-
- API compatibility and behavior matching
51-
5246
7. THREAD SAFETY (8 tests)
5347
- Race condition prevention in setencoding/setdecoding
5448
- Thread-safe reads with getencoding/getdecoding
@@ -3144,13 +3138,8 @@ def test_encoding_length_limit_security(db_connection):
31443138
db_connection.setencoding(encoding=enc_name, ctype=SQL_CHAR)
31453139

31463140

3147-
# ====================================================================================
3148-
# UTF-8 ENCODING TESTS (pyodbc Compatibility)
3149-
# ====================================================================================
3150-
3151-
31523141
def test_utf8_encoding_strict_no_fallback(db_connection):
3153-
"""Test that UTF-8 encoding does NOT fallback to latin-1 (pyodbc compatibility)."""
3142+
"""Test that UTF-8 encoding does NOT fallback to latin-1"""
31543143
db_connection.setencoding(encoding="utf-8", ctype=SQL_CHAR)
31553144

31563145
cursor = db_connection.cursor()
@@ -3180,7 +3169,7 @@ def test_utf8_encoding_strict_no_fallback(db_connection):
31803169

31813170

31823171
def test_utf8_decoding_strict_no_fallback(db_connection):
3183-
"""Test that UTF-8 decoding does NOT fallback to latin-1 (pyodbc compatibility)."""
3172+
"""Test that UTF-8 decoding does NOT fallback to latin-1"""
31843173
db_connection.setdecoding(SQL_CHAR, encoding="utf-8", ctype=SQL_CHAR)
31853174

31863175
cursor = db_connection.cursor()
@@ -3506,11 +3495,6 @@ def test_utf16_unicode_preservation(db_connection):
35063495
cursor.close()
35073496

35083497

3509-
# ====================================================================================
3510-
# ERROR HANDLING TESTS (Strict Mode, pyodbc Compatibility)
3511-
# ====================================================================================
3512-
3513-
35143498
def test_encoding_error_strict_mode(db_connection):
35153499
"""Test that encoding errors are raised or data is mangled in strict mode (no fallback)."""
35163500
db_connection.setencoding(encoding="ascii", ctype=SQL_CHAR)
@@ -6058,19 +6042,6 @@ def test_encoding_error_propagation_in_bind_parameters(db_connection):
60586042
cursor.close()
60596043

60606044

6061-
# ============================================================================
6062-
# ADDITIONAL COVERAGE TESTS FOR MISSING LINES
6063-
# ============================================================================
6064-
6065-
6066-
# Note: Tests for cursor._get_encoding_settings() and cursor._get_decoding_settings()
6067-
# fallback paths (lines 318, 327, 357) are not easily testable because:
6068-
# 1. The connection property is read-only and cannot be mocked
6069-
# 2. These are defensive code paths for unusual error conditions
6070-
# 3. The default fallback behavior (line 327) is tested implicitly by all other tests
6071-
# Coverage for these lines may require integration tests with actual connection failures
6072-
6073-
60746045
def test_sql_c_char_encoding_with_bytes_and_bytearray(db_connection):
60756046
"""Test SQL_C_CHAR encoding with bytes and bytearray parameters (lines 327-358 in ddbc_bindings.cpp)."""
60766047
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
@@ -6418,13 +6389,178 @@ def test_binary_lob_fetching(db_connection):
64186389
cursor.close()
64196390

64206391

6421-
# Note: Removed test_comprehensive_encoding_decoding_coverage
6422-
# The individual test functions already provide comprehensive coverage of:
6423-
# - SQL_C_CHAR encoding paths (test_sql_c_char_encoding_with_bytes_and_bytearray)
6424-
# - DAE paths (test_dae_sql_c_char_with_various_data_types)
6425-
# - Executemany paths (test_executemany_sql_c_char_encoding_paths)
6426-
# - LOB decoding (test_lob_decoding_with_fallback, test_binary_lob_fetching)
6427-
# - Character decoding (test_char_column_decoding_with_fallback)
6392+
def test_cpp_bind_params_str_encoding(db_connection):
6393+
"""str encoding with SQL_C_CHAR."""
6394+
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
6395+
cursor = db_connection.cursor()
6396+
try:
6397+
cursor.execute("CREATE TABLE #test_cpp_str (data VARCHAR(50))")
6398+
# This hits: py::isinstance<py::str>(param) == true
6399+
# and: param.attr("encode")(charEncoding, "strict")
6400+
# Note: VARCHAR stores in DB collation (Latin1), so we use ASCII-compatible chars
6401+
cursor.execute("INSERT INTO #test_cpp_str VALUES (?)", "Hello UTF-8 Test")
6402+
cursor.execute("SELECT data FROM #test_cpp_str")
6403+
assert cursor.fetchone()[0] == "Hello UTF-8 Test"
6404+
finally:
6405+
cursor.close()
6406+
6407+
6408+
def test_cpp_bind_params_bytes_encoding(db_connection):
6409+
"""bytes handling with SQL_C_CHAR."""
6410+
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
6411+
cursor = db_connection.cursor()
6412+
try:
6413+
cursor.execute("CREATE TABLE #test_cpp_bytes (data VARCHAR(50))")
6414+
# This hits: py::isinstance<py::bytes>(param) == true
6415+
cursor.execute("INSERT INTO #test_cpp_bytes VALUES (?)", b"Bytes data")
6416+
cursor.execute("SELECT data FROM #test_cpp_bytes")
6417+
assert cursor.fetchone()[0] == "Bytes data"
6418+
finally:
6419+
cursor.close()
6420+
6421+
6422+
def test_cpp_bind_params_bytearray_encoding(db_connection):
6423+
"""bytearray handling with SQL_C_CHAR."""
6424+
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
6425+
cursor = db_connection.cursor()
6426+
try:
6427+
cursor.execute("CREATE TABLE #test_cpp_bytearray (data VARCHAR(50))")
6428+
# This hits: bytearray branch - PyByteArray_AsString/Size
6429+
cursor.execute("INSERT INTO #test_cpp_bytearray VALUES (?)", bytearray(b"Bytearray data"))
6430+
cursor.execute("SELECT data FROM #test_cpp_bytearray")
6431+
assert cursor.fetchone()[0] == "Bytearray data"
6432+
finally:
6433+
cursor.close()
6434+
6435+
6436+
def test_cpp_bind_params_encoding_error(db_connection):
6437+
"""encoding error handling."""
6438+
db_connection.setencoding(encoding="ascii", ctype=mssql_python.SQL_CHAR)
6439+
cursor = db_connection.cursor()
6440+
try:
6441+
cursor.execute("CREATE TABLE #test_cpp_encode_err (data VARCHAR(50))")
6442+
# This should trigger the catch block (lines 337-345)
6443+
try:
6444+
cursor.execute("INSERT INTO #test_cpp_encode_err VALUES (?)", "Non-ASCII: 你好")
6445+
# If no error, that's OK - some drivers might handle it
6446+
except Exception as e:
6447+
# Expected: encoding error caught by C++ layer
6448+
assert "encode" in str(e).lower() or "ascii" in str(e).lower()
6449+
finally:
6450+
cursor.close()
6451+
6452+
6453+
def test_cpp_dae_str_encoding(db_connection):
6454+
"""str encoding in Data-At-Execution."""
6455+
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
6456+
cursor = db_connection.cursor()
6457+
try:
6458+
cursor.execute("CREATE TABLE #test_cpp_dae_str (data VARCHAR(MAX))")
6459+
# Large string triggers DAE
6460+
# This hits: py::isinstance<py::str>(pyObj) == true in DAE path
6461+
# Note: VARCHAR stores in DB collation, so we use ASCII-compatible chars
6462+
large_str = "A" * 10000 + " END_MARKER"
6463+
cursor.execute("INSERT INTO #test_cpp_dae_str VALUES (?)", large_str)
6464+
cursor.execute("SELECT data FROM #test_cpp_dae_str")
6465+
result = cursor.fetchone()[0]
6466+
assert len(result) > 10000
6467+
assert "END_MARKER" in result
6468+
finally:
6469+
cursor.close()
6470+
6471+
6472+
def test_cpp_dae_bytes_encoding(db_connection):
6473+
"""bytes encoding in Data-At-Execution."""
6474+
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
6475+
cursor = db_connection.cursor()
6476+
try:
6477+
cursor.execute("CREATE TABLE #test_cpp_dae_bytes (data VARCHAR(MAX))")
6478+
# Large bytes triggers DAE with bytes branch
6479+
# This hits: else branch (line 1751) - encodedStr = pyObj.cast<std::string>()
6480+
large_bytes = b"B" * 10000
6481+
cursor.execute("INSERT INTO #test_cpp_dae_bytes VALUES (?)", large_bytes)
6482+
cursor.execute("SELECT LEN(data) FROM #test_cpp_dae_bytes")
6483+
assert cursor.fetchone()[0] == 10000
6484+
finally:
6485+
cursor.close()
6486+
6487+
6488+
def test_cpp_dae_encoding_error(db_connection):
6489+
"""encoding error in Data-At-Execution."""
6490+
db_connection.setencoding(encoding="ascii", ctype=mssql_python.SQL_CHAR)
6491+
cursor = db_connection.cursor()
6492+
try:
6493+
cursor.execute("CREATE TABLE #test_cpp_dae_err (data VARCHAR(MAX))")
6494+
# Large non-ASCII string to trigger DAE + encoding error
6495+
large_unicode = "你好世界 " * 3000
6496+
try:
6497+
cursor.execute("INSERT INTO #test_cpp_dae_err VALUES (?)", large_unicode)
6498+
# No error is OK - some implementations may handle it
6499+
except Exception as e:
6500+
# Expected: catch block lines 1753-1756
6501+
error_msg = str(e).lower()
6502+
assert "encode" in error_msg or "ascii" in error_msg
6503+
finally:
6504+
cursor.close()
6505+
6506+
6507+
def test_cpp_executemany_str_encoding(db_connection):
6508+
"""str encoding in executemany."""
6509+
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
6510+
cursor = db_connection.cursor()
6511+
try:
6512+
cursor.execute("CREATE TABLE #test_cpp_many_str (id INT, data VARCHAR(50))")
6513+
# This hits: columnValues[i].attr("encode")(charEncoding, "strict") for each row
6514+
params = [
6515+
(1, "Row 1 UTF-8 ✓"),
6516+
(2, "Row 2 UTF-8 ✓"),
6517+
(3, "Row 3 UTF-8 ✓"),
6518+
]
6519+
cursor.executemany("INSERT INTO #test_cpp_many_str VALUES (?, ?)", params)
6520+
cursor.execute("SELECT COUNT(*) FROM #test_cpp_many_str")
6521+
assert cursor.fetchone()[0] == 3
6522+
finally:
6523+
cursor.close()
6524+
6525+
6526+
def test_cpp_executemany_bytes_encoding(db_connection):
6527+
"""bytes/bytearray in executemany."""
6528+
db_connection.setencoding(encoding="utf-8", ctype=mssql_python.SQL_CHAR)
6529+
cursor = db_connection.cursor()
6530+
try:
6531+
cursor.execute("CREATE TABLE #test_cpp_many_bytes (id INT, data VARCHAR(50))")
6532+
# This hits: else branch (line 2065) - bytes/bytearray handling
6533+
params = [
6534+
(1, b"Bytes 1"),
6535+
(2, b"Bytes 2"),
6536+
]
6537+
cursor.executemany("INSERT INTO #test_cpp_many_bytes VALUES (?, ?)", params)
6538+
cursor.execute("SELECT COUNT(*) FROM #test_cpp_many_bytes")
6539+
assert cursor.fetchone()[0] == 2
6540+
finally:
6541+
cursor.close()
6542+
6543+
6544+
def test_cpp_executemany_encoding_error(db_connection):
6545+
"""encoding error in executemany."""
6546+
db_connection.setencoding(encoding="ascii", ctype=mssql_python.SQL_CHAR)
6547+
cursor = db_connection.cursor()
6548+
try:
6549+
cursor.execute("CREATE TABLE #test_cpp_many_err (id INT, data VARCHAR(50))")
6550+
# This should trigger catch block lines 2055-2063
6551+
params = [
6552+
(1, "OK ASCII"),
6553+
(2, "Non-ASCII 中文"), # Should trigger error
6554+
]
6555+
try:
6556+
cursor.executemany("INSERT INTO #test_cpp_many_err VALUES (?, ?)", params)
6557+
# No error is OK
6558+
except Exception as e:
6559+
# Expected: catch block with error message
6560+
error_msg = str(e).lower()
6561+
assert "encode" in error_msg or "ascii" in error_msg or "parameter" in error_msg
6562+
finally:
6563+
cursor.close()
64286564

64296565

64306566
if __name__ == "__main__":

0 commit comments

Comments
 (0)