import pytest
import tempfile
import shutil
from pathlib import Path
import sys
import os
import numpy as np
import threading
import time
from concurrent.futures import ThreadPoolExecutor
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'apexbase', 'python'))
try:
from apexbase import ApexClient, FTS_AVAILABLE, ARROW_AVAILABLE, POLARS_AVAILABLE
except ImportError as e:
pytest.skip(f"ApexBase not available: {e}", allow_module_level=True)
class TestInvalidParameters:
def test_invalid_durability_parameter(self):
with tempfile.TemporaryDirectory() as temp_dir:
with pytest.raises(ValueError, match="durability must be"):
ApexClient(dirpath=temp_dir, durability="invalid")
def test_negative_batch_size(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir, batch_size=-1)
client.create_table("default")
assert client._batch_size == -1
client.close()
def test_negative_cache_size(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir, cache_size=-1)
client.create_table("default")
assert client._cache_size == -1
client.close()
def test_invalid_limit_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"name": "Alice", "age": 25})
try:
results = client.query(limit=-1)
except Exception as e:
pass
try:
results = client.query(limit=0)
except Exception as e:
pass
client.close()
def test_invalid_id_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"name": "Alice", "age": 25})
try:
result = client.retrieve(-1)
except (TypeError, ValueError, OverflowError):
pass
try:
client.delete(None)
except (TypeError, ValueError):
pass
client.close()
def test_invalid_table_names(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
try:
client.create_table("")
except Exception as e:
assert isinstance(e, (ValueError, RuntimeError))
long_name = "a" * 10000
try:
client.create_table(long_name)
except Exception as e:
assert isinstance(e, (ValueError, RuntimeError))
client.close()
class TestResourceExhaustion:
def test_memory_exhaustion_large_data(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
try:
large_data = {
"large_field": "x" * 100_000_000, "normal_field": "test"
}
client.store(large_data)
result = client.retrieve(1)
assert len(result["large_field"]) == 100_000_000
except MemoryError:
pass
except Exception as e:
print(f"Large data handling: {e}")
client.close()
def test_disk_space_exhaustion_simulation(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
try:
for i in range(100):
large_file = Path(temp_dir) / f"filler_{i}.dat"
try:
with open(large_file, 'wb') as f:
f.write(b'x' * 1_000_000) except OSError:
break
client.store({"test": "data when disk is full"})
except Exception as e:
print(f"Disk space exhaustion handling: {e}")
client.close()
def test_too_many_open_files(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
try:
clients = []
for i in range(1000):
try:
new_client = ApexClient(dirpath=temp_dir)
new_client.create_table("default")
clients.append(new_client)
new_client.store({"id": i, "data": f"test_{i}"})
except OSError:
break
for c in clients:
c.close()
except Exception as e:
print(f"File handle exhaustion handling: {e}")
client.close()
def test_concurrent_resource_usage(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
def worker(worker_id):
try:
for i in range(10):
data = {"worker_id": worker_id, "iteration": i, "data": f"test_{worker_id}_{i}"}
client.store(data)
return True
except Exception as e:
print(f"Worker {worker_id} error: {e}")
return False
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(worker, i) for i in range(10)]
results = [f.result() for f in futures]
success_count = sum(results)
assert success_count >= 5
all_results = client.retrieve_all()
assert len(all_results) >= success_count * 5
client.close()
class TestConcurrentAccess:
def test_concurrent_writes(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
def writer(writer_id):
try:
for i in range(50):
data = {"writer_id": writer_id, "iteration": i, "timestamp": time.time()}
client.store(data)
return True
except Exception as e:
print(f"Writer {writer_id} error: {e}")
return False
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(writer, i) for i in range(5)]
results = [f.result() for f in futures]
success_count = sum(results)
assert success_count >= 4
all_results = client.retrieve_all()
assert len(all_results) >= 200
client.close()
def test_concurrent_reads_writes(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
for i in range(100):
client.store({"id": i, "value": f"initial_{i}"})
def writer():
try:
for i in range(50):
client.store({"id": i + 1000, "value": f"concurrent_{i}"})
return True
except Exception as e:
print(f"Concurrent writer error: {e}")
return False
def reader():
try:
for i in range(50):
results = client.query("id >= 0")
assert len(results) >= 100 return True
except Exception as e:
print(f"Concurrent reader error: {e}")
return False
with ThreadPoolExecutor(max_workers=6) as executor:
futures = [executor.submit(writer)] + [executor.submit(reader) for _ in range(5)]
results = [f.result() for f in futures]
success_count = sum(results)
assert success_count >= 5
client.close()
def test_concurrent_table_operations(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
def table_worker(worker_id):
try:
table_name = f"table_{worker_id}"
client.create_table(table_name)
for i in range(10):
client.use_table(table_name)
client.store({"worker_id": worker_id, "iteration": i})
return True
except Exception as e:
print(f"Table worker {worker_id} error: {e}")
return False
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(table_worker, i) for i in range(5)]
results = [f.result() for f in futures]
success_count = sum(results)
assert success_count >= 4
client.close()
class TestDataCorruptionScenarios:
def test_partial_data_write_interruption(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
large_data = {
"field1": "x" * 1_000_000,
"field2": "y" * 1_000_000,
"field3": "z" * 1_000_000,
}
try:
client.store(large_data)
result = client.retrieve(1)
assert len(result["field1"]) == 1_000_000
assert len(result["field2"]) == 1_000_000
assert len(result["field3"]) == 1_000_000
except Exception as e:
print(f"Large write handling: {e}")
client.close()
def test_mixed_data_types_corruption(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
problematic_data = [
{"field": None},
{"field": ""},
{"field": 0},
{"field": False},
{"field": []}, {"field": {}}, {"field": float('inf')}, {"field": float('-inf')}, ]
try:
for data in problematic_data:
client.store(data)
for i in range(len(problematic_data)):
result = client.retrieve(i + 1)
assert result is not None
except Exception as e:
print(f"Mixed types handling: {e}")
client.close()
def test_unicode_corruption(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
unicode_data = [
{"text": "Normal unicode: ñáéíóú"},
{"text": "Emoji: 🎉🚀🌟"},
{"text": "Mixed: Hello 🌍 世界"},
{"text": "Zero-width: test\u200btest"}, {"text": "Control chars: test\u0001test"}, {"text": "High unicode: \U0001F600"}, ]
try:
for data in unicode_data:
client.store(data)
for i, original in enumerate(unicode_data):
result = client.retrieve(i + 1)
assert result["text"] == original["text"]
except Exception as e:
print(f"Unicode corruption handling: {e}")
client.close()
class TestNetworkAndIOErrors:
def test_file_permission_errors(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"test": "data"})
client.flush()
client.close()
db_file = Path(temp_dir) / "apexbase.apex"
if not db_file.exists():
return
try:
db_file.chmod(0o444)
try:
client2 = ApexClient(dirpath=temp_dir)
client2.create_table("default")
client2.store({"new": "data"})
client2.close()
except (PermissionError, RuntimeError, OSError):
pass except Exception as e:
print(f"Permission error handling: {e}")
finally:
try:
db_file.chmod(0o644)
except:
pass
def test_directory_permission_errors(self):
with tempfile.TemporaryDirectory() as temp_dir:
try:
os.chmod(temp_dir, 0o555)
try:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.close()
except PermissionError:
pass except Exception as e:
print(f"Directory permission error handling: {e}")
finally:
try:
os.chmod(temp_dir, 0o755)
except:
pass
def test_missing_database_file(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"test": "data"})
client.close()
db_file = Path(temp_dir) / "default.apex"
db_file.unlink()
try:
client2 = ApexClient(dirpath=temp_dir)
client2.create_table("default")
client2.store({"new": "data"})
client2.close()
except Exception as e:
print(f"Missing file handling: {e}")
class TestMemoryPressure:
def test_large_result_set_memory(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
large_data = [
{"id": i, "data": "x" * 1000} for i in range(10000) ]
client.store(large_data)
try:
results = client.retrieve_all()
assert len(results) == 10000
if ARROW_AVAILABLE:
try:
df = results.to_pandas()
assert len(df) == 10000
except MemoryError:
pass
except MemoryError:
pass except Exception as e:
print(f"Large result set handling: {e}")
client.close()
def test_memory_leak_simulation(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
try:
for i in range(1000):
client.store({"iteration": i, "data": f"test_{i}"})
results = client.query(f"iteration = {i}")
assert len(results) == 1
result = client.retrieve(i + 1)
assert result is not None
del results, result
if i % 100 == 0:
import gc
gc.collect()
except MemoryError:
pass except Exception as e:
print(f"Memory leak simulation: {e}")
client.close()
class TestInvalidStateTransitions:
def test_operations_after_close(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"test": "data"})
client.close()
with pytest.raises(RuntimeError, match="connection has been closed"):
client.store({"new": "data"})
with pytest.raises(RuntimeError, match="connection has been closed"):
client.query()
with pytest.raises(RuntimeError, match="connection has been closed"):
client.retrieve(1)
with pytest.raises(RuntimeError, match="connection has been closed"):
client.delete(1)
with pytest.raises(RuntimeError, match="connection has been closed"):
client.create_table("test")
with pytest.raises(RuntimeError, match="connection has been closed"):
client.list_tables()
def test_multiple_close_calls(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"test": "data"})
client.close()
client.close() client.close()
def test_fts_operations_without_init(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"content": "test"})
with pytest.raises(ValueError, match="Full-text search is not enabled"):
client.search_text("test")
with pytest.raises(ValueError, match="Full-text search is not enabled"):
client.fuzzy_search_text("test")
with pytest.raises(ValueError, match="Full-text search is not enabled"):
client.search_and_retrieve("test")
client.close()
class TestBoundaryConditions:
def test_empty_string_operations(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({"empty": "", "normal": "test"})
results = client.query("empty = ''")
assert len(results) == 1
client.init_fts(index_fields=['content'])
client.store({"content": ""})
search_results = client.search_text("")
assert isinstance(search_results, np.ndarray)
client.close()
def test_zero_and_negative_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"value": 0},
{"value": -1},
{"value": -1000000},
{"value": 0.0},
{"value": -0.001},
]
client.store(test_data)
results = client.query("value = 0")
assert len(results) >= 1
results = client.query("value < 0")
assert len(results) >= 2
client.close()
def test_maximum_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"value": 2**63 - 1}, {"value": -2**63}, {"value": float('inf')}, {"value": float('-inf')}, ]
try:
client.store(test_data)
results = client.query(f"value = {2**63 - 1}")
assert len(results) == 1
except Exception as e:
print(f"Maximum values handling: {e}")
client.close()
def test_very_long_identifiers(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
long_field_name = "field_" + "x" * 1000
try:
client.store({long_field_name: "test"})
results = client.query(f"{long_field_name} = 'test'")
assert len(results) == 1
except Exception as e:
print(f"Long identifier handling: {e}")
client.close()
class TestExceptionPropagation:
def test_descriptive_error_messages(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.close()
with pytest.raises(RuntimeError) as exc_info:
client.store({"test": "data"})
error_msg = str(exc_info.value)
assert "connection has been closed" in error_msg.lower()
assert len(error_msg) > 10
def test_error_recovery(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
try:
client.delete(999999) except Exception:
pass
client.store({"recovery": "test"})
result = client.retrieve(1)
assert result["recovery"] == "test"
client.close()
if __name__ == "__main__":
pytest.main([__file__, "-v"])