wayydb-api / tests /python /test_basic.py
rcgalbo's picture
Initial commit: WayyDB columnar time-series database
be7c937
"""Basic functionality tests for WayyDB Python bindings."""
import pytest
import numpy as np
import wayy_db as wdb
class TestTable:
"""Tests for Table class."""
def test_create_empty_table(self):
table = wdb.Table("test")
assert table.name == "test"
assert table.num_rows == 0
assert table.num_columns == 0
assert len(table) == 0
def test_from_dict(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades", sorted_by="timestamp")
assert table.name == "trades"
assert table.num_rows == 5
assert table.num_columns == 4
assert table.sorted_by == "timestamp"
def test_column_access(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
assert table.has_column("price")
assert not table.has_column("nonexistent")
price_col = table["price"]
assert price_col.name == "price"
assert price_col.dtype == wdb.DType.Float64
assert len(price_col) == 5
def test_to_numpy_zero_copy(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
prices = table["price"].to_numpy()
assert isinstance(prices, np.ndarray)
assert prices.dtype == np.float64
assert len(prices) == 5
np.testing.assert_array_equal(prices, sample_trades["price"])
def test_to_dict(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
result = table.to_dict()
assert set(result.keys()) == {"timestamp", "symbol", "price", "size"}
np.testing.assert_array_equal(result["price"], sample_trades["price"])
def test_column_names(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
names = table.column_names()
assert set(names) == {"timestamp", "symbol", "price", "size"}
class TestDatabase:
"""Tests for Database class."""
def test_in_memory_database(self):
db = wdb.Database()
assert not db.is_persistent
assert db.tables() == []
def test_create_table(self):
db = wdb.Database()
table = db.create_table("trades")
assert db.has_table("trades")
assert "trades" in db.tables()
def test_persistent_database(self, temp_dir, sample_trades):
# Create and populate
db = wdb.Database(temp_dir)
table = db.create_table("trades")
for name, data in sample_trades.items():
dtype = {
np.dtype("int64"): wdb.DType.Int64,
np.dtype("float64"): wdb.DType.Float64,
np.dtype("uint32"): wdb.DType.Symbol,
}[data.dtype]
table.add_column_from_numpy(name, data, dtype)
table.set_sorted_by("timestamp")
db.save()
# Reload and verify
db2 = wdb.Database(temp_dir)
assert db2.has_table("trades")
loaded = db2["trades"]
assert loaded.num_rows == 5
assert loaded.sorted_by == "timestamp"
class TestOperations:
"""Tests for operations module."""
def test_aggregations(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
price_col = table["price"]
assert wdb.ops.sum(price_col) == pytest.approx(1214.0)
assert wdb.ops.avg(price_col) == pytest.approx(242.8)
assert wdb.ops.min(price_col) == pytest.approx(150.0)
assert wdb.ops.max(price_col) == pytest.approx(381.0)
def test_window_functions(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
price_col = table["price"]
mavg = wdb.ops.mavg(price_col, 2)
assert len(mavg) == 5
assert mavg[1] == pytest.approx((150.0 + 380.0) / 2)
msum = wdb.ops.msum(price_col, 2)
assert len(msum) == 5
def test_ema(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
price_col = table["price"]
ema = wdb.ops.ema(price_col, 0.5)
assert len(ema) == 5
assert ema[0] == pytest.approx(150.0) # First value unchanged
def test_diff(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
price_col = table["price"]
diff = wdb.ops.diff(price_col, 1)
assert len(diff) == 5
assert diff[1] == pytest.approx(380.0 - 150.0)
class TestAsOfJoin:
"""Tests for as-of join operation."""
def test_aj_basic(self, sample_trades, sample_quotes):
trades = wdb.from_dict(sample_trades, name="trades", sorted_by="timestamp")
quotes = wdb.from_dict(sample_quotes, name="quotes", sorted_by="timestamp")
result = wdb.ops.aj(trades, quotes, on=["symbol"], as_of="timestamp")
assert result.num_rows == 5
assert result.has_column("bid")
assert result.has_column("ask")
assert result.has_column("price")
def test_aj_requires_sorted(self, sample_trades, sample_quotes):
trades = wdb.from_dict(sample_trades, name="trades") # Not sorted
quotes = wdb.from_dict(sample_quotes, name="quotes", sorted_by="timestamp")
with pytest.raises(wdb.InvalidOperation):
wdb.ops.aj(trades, quotes, on=["symbol"], as_of="timestamp")
class TestExceptions:
"""Tests for exception handling."""
def test_column_not_found(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
with pytest.raises(wdb.ColumnNotFound):
_ = table["nonexistent"]
def test_invalid_operation(self, sample_trades):
table = wdb.from_dict(sample_trades, name="trades")
with pytest.raises(wdb.ColumnNotFound):
table.set_sorted_by("nonexistent")