import pytest
import tempfile
import shutil
from pathlib import Path
import sys
import os
import numpy as np
from datetime import datetime, date
from decimal import Decimal
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'apexbase', 'python'))
try:
from apexbase import ApexClient, ARROW_AVAILABLE, POLARS_AVAILABLE
except ImportError as e:
pytest.skip(f"ApexBase not available: {e}", allow_module_level=True)
try:
import pandas as pd
PANDAS_AVAILABLE = True
except ImportError:
PANDAS_AVAILABLE = False
try:
import polars as pl
POLARS_DF_AVAILABLE = True
except ImportError:
POLARS_DF_AVAILABLE = False
try:
import pyarrow as pa
PYARROW_AVAILABLE = True
except ImportError:
PYARROW_AVAILABLE = False
class TestSingleRecordStorage:
def test_store_single_dict_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {"name": "Alice", "age": 25, "city": "NYC"}
client.store(data)
count = client.count_rows()
assert count == 1
result = client.retrieve(0)
assert result["name"] == "Alice"
assert result["age"] == 25
assert result["city"] == "NYC"
client.close()
def test_store_single_dict_all_types(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"string_field": "test_string",
"int_field": 42,
"float_field": 3.14159,
"bool_field": True,
"none_field": None,
"bytes_field": b"binary_data",
"negative_int": -100,
"zero_float": 0.0,
"empty_string": "",
"false_bool": False,
}
client.store(data)
result = client.retrieve(0)
assert result["string_field"] == "test_string"
assert result["int_field"] == 42
assert result["float_field"] == 3.14159
assert result["bool_field"] is True
assert result["none_field"] is None
assert result["bytes_field"] == b"binary_data"
assert result["negative_int"] == -100
assert result["zero_float"] == 0.0
assert result["empty_string"] == ""
assert result["false_bool"] is False
client.close()
def test_store_single_dict_special_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"large_int": 2**63 - 1, "small_float": 1e-10,
"large_float": 1e10,
"infinity": float('inf'),
"neg_infinity": float('-inf'),
}
client.store(data)
result = client.retrieve(0)
assert result["large_int"] == 2**63 - 1
assert abs(result["small_float"] - 1e-10) < 1e-15
assert abs(result["large_float"] - 1e10) < 1e-5
client.close()
def test_store_empty_dict(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store({})
count = client.count_rows()
assert count == 1
result = client.retrieve(0)
assert result == {} or result == {"_id": 0}
client.close()
def test_store_dict_with_unicode(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"chinese": "你好世界",
"emoji": "🌍🚀",
"arabic": "مرحبا بالعالم",
"russian": "Привет мир",
"french": "Bonjour le monde",
}
client.store(data)
result = client.retrieve(0)
for key, value in data.items():
assert result[key] == value
client.close()
class TestBatchStorage:
def test_store_list_of_dicts_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
{"name": "Charlie", "age": 35},
]
client.store(data)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
names = [r["name"] for r in results]
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_store_empty_list(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.store([])
count = client.count_rows()
assert count == 0
client.close()
def test_store_list_with_various_types(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = [
{"type": "string", "value": "test"},
{"type": "int", "value": 42},
{"type": "float", "value": 3.14},
{"type": "bool", "value": True},
{"type": "none", "value": None},
{"type": "bytes", "value": b"binary"},
]
client.store(data)
count = client.count_rows()
assert count == 6
results = client.retrieve_all()
types = [r["type"] for r in results]
assert len(types) == 6
assert "string" in types
assert "int" in types
client.close()
def test_store_list_with_missing_fields(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = [
{"name": "Alice", "age": 25, "city": "NYC"},
{"name": "Bob", "age": 30}, {"name": "Charlie", "city": "LA"}, {"age": 40}, ]
client.store(data)
count = client.count_rows()
assert count == 4
results = client.retrieve_all()
assert len(results) == 4
client.close()
def test_store_large_list(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = [{"id": i, "value": f"record_{i}"} for i in range(1000)]
client.store(data)
count = client.count_rows()
assert count == 1000
result_0 = client.retrieve(0)
assert result_0["id"] == 0
assert result_0["value"] == "record_0"
result_999 = client.retrieve(999)
assert result_999["id"] == 999
assert result_999["value"] == "record_999"
client.close()
class TestColumnarStorage:
def test_store_columnar_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"names": ["Alice", "Bob", "Charlie"],
"ages": [25, 30, 35],
"cities": ["NYC", "LA", "Chicago"],
}
client.store(data)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
names = [r["names"] for r in results]
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_store_columnar_mixed_types(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"strings": ["a", "b", "c"],
"integers": [1, 2, 3],
"floats": [1.1, 2.2, 3.3],
"booleans": [True, False, True],
"bytes_data": [b"x", b"y", b"z"],
}
client.store(data)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
def test_store_columnar_empty_columns(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"empty_list": [],
"non_empty": [1, 2, 3],
}
with pytest.raises(ValueError, match="same length"):
client.store(data)
client.close()
def test_store_columnar_unequal_lengths(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"short": [1, 2],
"long": [1, 2, 3, 4, 5],
}
with pytest.raises(ValueError, match="same length"):
client.store(data)
client.close()
def test_store_columnar_single_value(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"single": ["only_value"],
}
client.store(data)
count = client.count_rows()
assert count == 1
result = client.retrieve(0)
assert result["single"] == "only_value"
client.close()
@pytest.mark.skipif(not hasattr(np, 'array'), reason="NumPy not available")
class TestNumPyStorage:
def test_store_numpy_numeric(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"int_array": np.array([1, 2, 3, 4, 5], dtype=np.int64),
"float_array": np.array([1.1, 2.2, 3.3, 4.4, 5.5], dtype=np.float64),
"bool_array": np.array([True, False, True, False, True], dtype=np.bool_),
}
client.store(data)
count = client.count_rows()
assert count == 5
results = client.retrieve_all()
assert len(results) == 5
client.close()
def test_store_numpy_mixed_dtypes(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"int32": np.array([1, 2, 3], dtype=np.int32),
"int64": np.array([100, 200, 300], dtype=np.int64),
"float32": np.array([1.1, 2.2, 3.3], dtype=np.float32),
"float64": np.array([10.1, 20.2, 30.3], dtype=np.float64),
}
client.store(data)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
def test_store_numpy_large_arrays(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
size = 10000
data = {
"large_int": np.arange(size, dtype=np.int64),
"large_float": np.random.random(size).astype(np.float64),
}
client.store(data)
count = client.count_rows()
assert count == size
result_0 = client.retrieve(0)
assert result_0["large_int"] == 0
result_last = client.retrieve(size - 1)
assert result_last["large_int"] == size - 1
client.close()
def test_store_numpy_string_arrays(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"string_array": np.array(["a", "b", "c", "d"], dtype=str),
"unicode_array": np.array(["测试", "🚀", "café", "日本語"], dtype=str),
}
client.store(data)
count = client.count_rows()
assert count == 4
results = client.retrieve_all()
assert len(results) == 4
client.close()
@pytest.mark.skipif(not PANDAS_AVAILABLE, reason="Pandas not available")
class TestPandasStorage:
def test_store_pandas_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"],
})
client.store(df)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
def test_store_pandas_mixed_dtypes(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"strings": ["a", "b", "c"],
"integers": pd.Series([1, 2, 3], dtype="int64"),
"floats": pd.Series([1.1, 2.2, 3.3], dtype="float64"),
"booleans": pd.Series([True, False, True], dtype="bool"),
"datetime": pd.date_range("2023-01-01", periods=3),
})
client.store(df)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
def test_store_pandas_empty(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame()
client.store(df)
count = client.count_rows()
assert count == 0
client.close()
def test_store_pandas_with_nan(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"col1": [1, 2, 3, 4],
"col2": ["a", "b", "c", "d"],
})
try:
client.store(df)
count = client.count_rows()
assert count == 4
except TypeError as e:
print(f"Pandas NaN: {e}")
client.close()
def test_from_pandas_method(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"product": ["A", "B", "C"],
"price": [10.99, 20.50, 30.75],
})
returned_client = client.from_pandas(df)
assert returned_client is client
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
@pytest.mark.skipif(not POLARS_DF_AVAILABLE, reason="Polars not available")
class TestPolarsStorage:
def test_store_polars_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pl.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"],
})
try:
client.store(df)
count = client.count_rows()
assert count == 3
except (AttributeError, TypeError) as e:
print(f"Polars storage issue: {e}")
client.close()
def test_store_polars_mixed_dtypes(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pl.DataFrame({
"strings": ["a", "b", "c"],
"integers": pl.Series("integers", [1, 2, 3], dtype=pl.Int64),
"floats": pl.Series("floats", [1.1, 2.2, 3.3], dtype=pl.Float64),
"booleans": pl.Series("booleans", [True, False, True], dtype=pl.Boolean),
})
try:
client.store(df)
count = client.count_rows()
assert count == 3
except (AttributeError, TypeError) as e:
print(f"Polars mixed dtypes issue: {e}")
client.close()
def test_from_polars_method(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pl.DataFrame({
"id": [1, 2, 3],
"value": ["x", "y", "z"],
})
returned_client = client.from_polars(df)
assert returned_client is client
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
@pytest.mark.skipif(not PYARROW_AVAILABLE, reason="PyArrow not available")
class TestPyArrowStorage:
def test_store_arrow_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
table = pa.Table.from_pydict({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"],
})
client.store(table)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
def test_store_arrow_mixed_dtypes(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
table = pa.Table.from_pydict({
"strings": ["a", "b", "c"],
"integers": pa.array([1, 2, 3], type=pa.int64()),
"floats": pa.array([1.1, 2.2, 3.3], type=pa.float64()),
"booleans": pa.array([True, False, True], type=pa.bool_()),
})
client.store(table)
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
def test_from_pyarrow_method(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
table = pa.Table.from_pydict({
"key": ["a", "b", "c"],
"value": [1, 2, 3],
})
returned_client = client.from_pyarrow(table)
assert returned_client is client
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
client.close()
class TestStorageEdgeCases:
def test_store_unsupported_format(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
with pytest.raises(ValueError, match="Data must be dict, list of dicts"):
client.store("unsupported_string")
with pytest.raises(ValueError, match="Data must be dict, list of dicts"):
client.store(123)
with pytest.raises(ValueError, match="Data must be dict, list of dicts"):
client.store(set([1, 2, 3]))
client.close()
def test_store_on_closed_client(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.close()
with pytest.raises(RuntimeError, match="connection has been closed"):
client.store({"test": "data"})
with pytest.raises(RuntimeError, match="connection has been closed"):
client.store([{"test": "data"}])
with pytest.raises(RuntimeError, match="connection has been closed"):
client.store({"col": [1, 2, 3]})
def test_store_very_large_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
long_string = "x" * 1000000
data = {
"long_string": long_string,
"normal": "test",
}
client.store(data)
result = client.retrieve(0)
assert len(result["long_string"]) == 1000000
assert result["normal"] == "test"
client.close()
def test_store_nested_structures(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"nested_dict": {"key": "value"},
"nested_list": [1, 2, 3],
"normal": "test",
}
try:
client.store(data)
result = client.retrieve(0)
assert "normal" in result
except Exception as e:
print(f"Nested structures handled as: {e}")
client.close()
def test_store_special_characters(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
data = {
"quotes": 'Single "double" quotes',
"newlines": "Line 1\nLine 2\rLine 3",
"tabs": "Tab\tseparated",
"backslashes": "Backslash\\test",
"unicode": "Unicode: ñáéíóú",
"emoji": "Emoji: 🎉🚀🌟",
"null_bytes": "Null\x00byte",
}
client.store(data)
result = client.retrieve(0)
for key, expected in data.items():
assert result[key] == expected
client.close()
def test_store_with_fts_enabled(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
client.init_fts(index_fields=["content", "title"])
data = {
"title": "Test Document",
"content": "This is searchable content",
"metadata": "not_indexed",
}
client.store(data)
results = client.search_text("searchable")
assert len(results) > 0
client.close()
def test_store_performance_considerations(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
base_data = [{"id": i, "value": f"item_{i}"} for i in range(100)]
client.store(base_data.copy())
list_count = client.count_rows()
client.create_table("columnar_test")
columnar_data = {
"id": list(range(100)),
"value": [f"item_{i}" for i in range(100)],
}
client.store(columnar_data)
columnar_count = client.count_rows()
assert list_count == 100
assert columnar_count == 100
client.close()
if __name__ == "__main__":
pytest.main([__file__, "-v"])