Skip to content

Commit b85445d

Browse files
committed
Adding more test cases
1 parent c2ea17e commit b85445d

File tree

1 file changed

+299
-0
lines changed

1 file changed

+299
-0
lines changed

tests/test_003_connection.py

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4144,3 +4144,302 @@ def test_getinfo_comprehensive_edge_case_coverage(db_connection):
41444144
assert not isinstance(
41454145
e, (SystemError, MemoryError)
41464146
), f"Info type {info_type} caused critical error: {e}"
4147+
4148+
4149+
def test_timeout_long_running_query_with_small_timeout(conn_str):
4150+
"""Test that a long-running query with small timeout (1-2 seconds) raises timeout error.
4151+
4152+
This test replicates exactly what test_timeout_bug.py does to ensure consistency.
4153+
"""
4154+
import time
4155+
import mssql_python
4156+
4157+
print(f"DEBUG: Connection string: {conn_str}")
4158+
4159+
# Test 1: Create connection with timeout parameter (like test_timeout_bug.py)
4160+
print("DEBUG: [Test 1] Creating connection with timeout=2 seconds")
4161+
connection = mssql_python.connect(conn_str, timeout=2)
4162+
print(f"DEBUG: Connection created, timeout property: {connection.timeout}")
4163+
4164+
try:
4165+
cursor = connection.cursor()
4166+
start_time = time.perf_counter()
4167+
print("DEBUG: Executing WAITFOR DELAY '00:00:05' (5 seconds)")
4168+
4169+
try:
4170+
cursor.execute("WAITFOR DELAY '00:00:05'")
4171+
elapsed = time.perf_counter() - start_time
4172+
print(f"DEBUG: BUG CONFIRMED: Query completed without timeout after {elapsed:.2f}s")
4173+
pytest.skip(
4174+
f"Timeout not enforced - query completed in {elapsed:.2f}s (expected ~2s timeout)"
4175+
)
4176+
except mssql_python.OperationalError as e:
4177+
elapsed = time.perf_counter() - start_time
4178+
print(f"DEBUG: ✓ Query timed out after {elapsed:.2f}s: {e}")
4179+
assert elapsed < 4.0, f"Timeout took too long: {elapsed:.2f}s"
4180+
assert "timeout" in str(e).lower(), f"Not a timeout error: {e}"
4181+
except Exception as e:
4182+
elapsed = time.perf_counter() - start_time
4183+
print(f"DEBUG: ✓ Query raised exception after {elapsed:.2f}s: {type(e).__name__}: {e}")
4184+
assert elapsed < 4.0, f"Exception took too long: {elapsed:.2f}s"
4185+
# Accept any exception that happens quickly as it might be timeout-related
4186+
finally:
4187+
cursor.close()
4188+
connection.close()
4189+
4190+
except Exception as e:
4191+
print(f"DEBUG: Unexpected error in test: {e}")
4192+
if connection:
4193+
connection.close()
4194+
raise
4195+
4196+
# Test 2: Set timeout dynamically (like test_timeout_bug.py)
4197+
print("DEBUG: [Test 2] Setting timeout dynamically via property")
4198+
connection = mssql_python.connect(conn_str)
4199+
print(f"DEBUG: Initial timeout: {connection.timeout}")
4200+
connection.timeout = 2
4201+
print(f"DEBUG: After setting: {connection.timeout}")
4202+
4203+
try:
4204+
cursor = connection.cursor()
4205+
start_time = time.perf_counter()
4206+
4207+
try:
4208+
cursor.execute("WAITFOR DELAY '00:00:05'")
4209+
elapsed = time.perf_counter() - start_time
4210+
print(f"DEBUG: BUG CONFIRMED: Query completed without timeout after {elapsed:.2f}s")
4211+
# This is the main test - if we get here, timeout is not working
4212+
assert (
4213+
False
4214+
), f"Timeout should have occurred after ~2s, but query completed in {elapsed:.2f}s"
4215+
except mssql_python.OperationalError as e:
4216+
elapsed = time.perf_counter() - start_time
4217+
print(f"DEBUG: ✓ Query timed out after {elapsed:.2f}s: {e}")
4218+
assert elapsed < 4.0, f"Timeout took too long: {elapsed:.2f}s"
4219+
assert "timeout" in str(e).lower(), f"Not a timeout error: {e}"
4220+
except Exception as e:
4221+
elapsed = time.perf_counter() - start_time
4222+
print(f"DEBUG: ✓ Query raised exception after {elapsed:.2f}s: {type(e).__name__}: {e}")
4223+
assert elapsed < 4.0, f"Exception took too long: {elapsed:.2f}s"
4224+
finally:
4225+
cursor.close()
4226+
connection.close()
4227+
4228+
except Exception as e:
4229+
print(f"DEBUG: Unexpected error in dynamic timeout test: {e}")
4230+
if connection:
4231+
connection.close()
4232+
raise
4233+
4234+
4235+
def test_cursor_timeout_single_execute(db_connection):
4236+
"""Test that creating a cursor with timeout set and calling execute once behaves correctly."""
4237+
cursor = db_connection.cursor()
4238+
4239+
# Set timeout on connection which should affect cursor
4240+
original_timeout = db_connection.timeout
4241+
db_connection.timeout = 30 # 30 seconds - reasonable timeout
4242+
4243+
try:
4244+
# Test single execution with timeout set
4245+
cursor.execute("SELECT 1 AS test_value")
4246+
result = cursor.fetchone()
4247+
assert result is not None, "Query should execute successfully with timeout set"
4248+
assert result[0] == 1, "Query should return expected result"
4249+
4250+
# Test that cursor can be used for another query
4251+
cursor.execute("SELECT 2 AS test_value")
4252+
result = cursor.fetchone()
4253+
assert result is not None, "Second query should also work"
4254+
assert result[0] == 2, "Second query should return expected result"
4255+
4256+
finally:
4257+
cursor.close()
4258+
db_connection.timeout = original_timeout
4259+
4260+
4261+
def test_cursor_timeout_multiple_executions_consistency(db_connection):
4262+
"""Test executing multiple times with same cursor and verify timeout applies consistently."""
4263+
cursor = db_connection.cursor()
4264+
4265+
# Set a reasonable timeout
4266+
original_timeout = db_connection.timeout
4267+
db_connection.timeout = 15 # 15 seconds
4268+
4269+
try:
4270+
# Execute multiple queries in sequence to verify timeout consistency
4271+
queries = [
4272+
"SELECT 1 AS query_num",
4273+
"SELECT 2 AS query_num",
4274+
"SELECT 3 AS query_num",
4275+
"SELECT GETDATE() AS current_datetime",
4276+
"SELECT @@VERSION AS version_info",
4277+
]
4278+
4279+
for i, query in enumerate(queries):
4280+
start_time = time.perf_counter()
4281+
cursor.execute(query)
4282+
result = cursor.fetchone()
4283+
elapsed_time = time.perf_counter() - start_time
4284+
4285+
assert result is not None, f"Query {i+1} should return a result"
4286+
# All queries should complete well within the timeout
4287+
assert elapsed_time < 10, f"Query {i+1} took too long: {elapsed_time:.2f}s"
4288+
4289+
# For simple queries, verify expected results
4290+
if i < 3: # First three queries return sequential numbers
4291+
assert result[0] == i + 1, f"Query {i+1} returned incorrect result"
4292+
4293+
print(
4294+
f"Successfully executed {len(queries)} queries consistently with timeout={db_connection.timeout}s"
4295+
)
4296+
4297+
finally:
4298+
cursor.close()
4299+
db_connection.timeout = original_timeout
4300+
4301+
4302+
def test_cursor_reset_timeout_behavior(db_connection):
4303+
"""Test that _reset_cursor handles timeout correctly and _set_timeout is called as intended."""
4304+
# Create initial cursor
4305+
cursor1 = db_connection.cursor()
4306+
4307+
original_timeout = db_connection.timeout
4308+
db_connection.timeout = 20 # Set reasonable timeout
4309+
4310+
try:
4311+
# Execute a query to establish cursor state
4312+
cursor1.execute("SELECT 'initial_query' AS status")
4313+
result1 = cursor1.fetchone()
4314+
assert result1[0] == "initial_query", "Initial query should work"
4315+
cursor1.close() # Close to release connection resources
4316+
4317+
# Create another cursor to test that timeout is properly set on new cursors
4318+
cursor2 = db_connection.cursor()
4319+
cursor2.execute("SELECT 'second_cursor' AS status")
4320+
result2 = cursor2.fetchone()
4321+
assert result2[0] == "second_cursor", "Second cursor should work with timeout"
4322+
cursor2.close() # Close to release connection resources
4323+
4324+
# Create another cursor to test reuse (simulating _reset_cursor scenario)
4325+
cursor3 = db_connection.cursor()
4326+
cursor3.execute("SELECT 'reuse_test' AS status")
4327+
result3 = cursor3.fetchone()
4328+
assert result3[0] == "reuse_test", "Cursor should work with timeout"
4329+
4330+
# Change timeout and verify cursor still works with new timeout
4331+
db_connection.timeout = 25
4332+
cursor3.execute("SELECT 'updated_timeout_test' AS status")
4333+
result4 = cursor3.fetchone()
4334+
assert result4[0] == "updated_timeout_test", "Cursor should work with updated timeout"
4335+
4336+
# Test that multiple operations work consistently
4337+
for i in range(3):
4338+
cursor3.execute(f"SELECT 'iteration_{i}' AS status")
4339+
result = cursor3.fetchone()
4340+
assert result[0] == f"iteration_{i}", f"Iteration {i} should work with timeout"
4341+
4342+
print(f"Successfully tested cursor reset behavior with timeout settings")
4343+
4344+
finally:
4345+
# Clean up cursor
4346+
try:
4347+
if "cursor3" in locals() and not cursor3.closed:
4348+
cursor3.close()
4349+
except:
4350+
pass
4351+
db_connection.timeout = original_timeout
4352+
4353+
4354+
def test_timeout_compatibility_with_previous_versions(db_connection):
4355+
"""Test that timeout behavior is compatible and doesn't break existing functionality."""
4356+
cursor = db_connection.cursor()
4357+
4358+
original_timeout = db_connection.timeout
4359+
4360+
try:
4361+
# Test with default timeout (0 = no timeout)
4362+
assert db_connection.timeout == 0, "Default timeout should be 0"
4363+
4364+
cursor.execute("SELECT 'default_timeout' AS test")
4365+
result = cursor.fetchone()
4366+
assert result[0] == "default_timeout", "Should work with default timeout"
4367+
4368+
# Test setting various timeout values
4369+
timeout_values = [5, 10, 30, 60, 0] # Including 0 to reset
4370+
4371+
for timeout_val in timeout_values:
4372+
db_connection.timeout = timeout_val
4373+
assert db_connection.timeout == timeout_val, f"Timeout should be set to {timeout_val}"
4374+
4375+
# Execute a quick query to verify functionality
4376+
cursor.execute(f"SELECT {timeout_val} AS timeout_value")
4377+
result = cursor.fetchone()
4378+
assert result[0] == timeout_val, f"Should work with timeout={timeout_val}"
4379+
4380+
# Test that timeout doesn't affect normal operations
4381+
test_operations = [
4382+
("SELECT COUNT(*) FROM sys.objects", "count query"),
4383+
("SELECT DB_NAME()", "database name"),
4384+
("SELECT GETDATE()", "current date"),
4385+
("SELECT 1 WHERE 1=1", "conditional query"),
4386+
("SELECT 'test' + 'string'", "string concatenation"),
4387+
]
4388+
4389+
db_connection.timeout = 10 # Set reasonable timeout
4390+
4391+
for query, description in test_operations:
4392+
cursor.execute(query)
4393+
result = cursor.fetchone()
4394+
assert result is not None, f"Operation '{description}' should work with timeout"
4395+
4396+
print("Successfully verified timeout compatibility with existing functionality")
4397+
4398+
finally:
4399+
cursor.close()
4400+
db_connection.timeout = original_timeout
4401+
4402+
4403+
def test_timeout_edge_cases_and_boundaries(db_connection):
4404+
"""Test timeout behavior with edge cases and boundary conditions."""
4405+
cursor = db_connection.cursor()
4406+
original_timeout = db_connection.timeout
4407+
4408+
try:
4409+
# Test boundary timeout values
4410+
boundary_values = [0, 1, 2, 5, 10, 30, 60, 120, 300] # 0 to 5 minutes
4411+
4412+
for timeout_val in boundary_values:
4413+
db_connection.timeout = timeout_val
4414+
assert (
4415+
db_connection.timeout == timeout_val
4416+
), f"Should accept timeout value {timeout_val}"
4417+
4418+
# Execute a very quick query to ensure no issues with boundary values
4419+
cursor.execute("SELECT 1 AS boundary_test")
4420+
result = cursor.fetchone()
4421+
assert result[0] == 1, f"Should work with boundary timeout {timeout_val}"
4422+
4423+
# Test with zero timeout (no timeout)
4424+
db_connection.timeout = 0
4425+
cursor.execute("SELECT 'no_timeout_test' AS test")
4426+
result = cursor.fetchone()
4427+
assert result[0] == "no_timeout_test", "Should work with zero timeout"
4428+
4429+
# Test invalid timeout values (should raise ValueError)
4430+
invalid_values = [-1, -5, -100]
4431+
for invalid_val in invalid_values:
4432+
with pytest.raises(ValueError, match="Timeout cannot be negative"):
4433+
db_connection.timeout = invalid_val
4434+
4435+
# Test non-integer timeout values (should raise TypeError)
4436+
invalid_types = ["10", 10.5, None, [], {}]
4437+
for invalid_type in invalid_types:
4438+
with pytest.raises(TypeError):
4439+
db_connection.timeout = invalid_type
4440+
4441+
print("Successfully tested timeout edge cases and boundaries")
4442+
4443+
finally:
4444+
cursor.close()
4445+
db_connection.timeout = original_timeout

0 commit comments

Comments
 (0)