fix(clients): repair Python & Rust tests, add container test orchestration
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
- Python: fix wire protocol test method names (bool_val -> bool, etc.) - Python: make integration tests and example fully async with pytest-asyncio - Rust: add tokio dev-dependency and convert integration tests to async/await - Rust: update ping_test example to async - Nim: remove committed ELF build artifact - Docker: add BARADB_HOST/BARADB_PORT env vars to test containers - Docker: fix docker-compose.test.yml usage (remove --abort-on-container-exit) - Add scripts/test-clients.sh for sequential client test runs - Remove docker-compose.test.yml from .gitignore so it is tracked - Fix repository URLs in Python and Rust READMEs
This commit is contained in:
@@ -21,7 +21,7 @@ pip install baradb
|
||||
Or from source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/katehonz/barabaDB.git
|
||||
git clone https://github.com/barabadb/baradadb.git
|
||||
cd clients/python
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
@@ -6,35 +6,36 @@ This script demonstrates common operations with the BaraDB Python client.
|
||||
Run it while a BaraDB server is listening on localhost:9472.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from baradb import Client, QueryBuilder, WireValue
|
||||
|
||||
|
||||
def example_connection():
|
||||
async def example_connection():
|
||||
print("=== Connection ===")
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
await client.connect()
|
||||
print(f"Connected: {client.is_connected()}")
|
||||
print(f"Ping: {client.ping()}")
|
||||
client.close()
|
||||
print(f"Ping: {await client.ping()}")
|
||||
await client.close()
|
||||
print(f"Connected after close: {client.is_connected()}")
|
||||
print()
|
||||
|
||||
|
||||
def example_context_manager():
|
||||
async def example_context_manager():
|
||||
print("=== Context Manager ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
async with Client("localhost", 9472) as client:
|
||||
print(f"Inside context: {client.is_connected()}")
|
||||
print(f"Ping: {client.ping()}")
|
||||
print(f"Ping: {await client.ping()}")
|
||||
print("Outside context: closed automatically")
|
||||
print()
|
||||
|
||||
|
||||
def example_simple_query():
|
||||
async def example_simple_query():
|
||||
print("=== Simple Query ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query("SELECT 42 as answer, 'BaraDB' as db")
|
||||
async with Client("localhost", 9472) as client:
|
||||
result = await client.query("SELECT 42 as answer, 'BaraDB' as db")
|
||||
print(f"Columns: {result.columns}")
|
||||
print(f"Rows: {result.rows}")
|
||||
print(f"Row count: {result.row_count}")
|
||||
@@ -43,15 +44,15 @@ def example_simple_query():
|
||||
print()
|
||||
|
||||
|
||||
def example_parameterized_query():
|
||||
async def example_parameterized_query():
|
||||
print("=== Parameterized Query ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
async with Client("localhost", 9472) as client:
|
||||
result = await client.query_params(
|
||||
"SELECT $1 as num, $2 as txt, $3 as flag",
|
||||
[
|
||||
WireValue.int64(123),
|
||||
WireValue.string("hello world"),
|
||||
WireValue.bool_val(True),
|
||||
WireValue.bool(True),
|
||||
],
|
||||
)
|
||||
for row in result:
|
||||
@@ -59,9 +60,9 @@ def example_parameterized_query():
|
||||
print()
|
||||
|
||||
|
||||
def example_query_builder():
|
||||
async def example_query_builder():
|
||||
print("=== Query Builder ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
async with Client("localhost", 9472) as client:
|
||||
sql = (
|
||||
QueryBuilder(client)
|
||||
.select("id", "name")
|
||||
@@ -75,10 +76,10 @@ def example_query_builder():
|
||||
print()
|
||||
|
||||
|
||||
def example_vector():
|
||||
async def example_vector():
|
||||
print("=== Vector Value ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
async with Client("localhost", 9472) as client:
|
||||
result = await client.query_params(
|
||||
"SELECT $1 as embedding",
|
||||
[WireValue.vector([0.1, 0.2, 0.3, 0.4])],
|
||||
)
|
||||
@@ -87,34 +88,34 @@ def example_vector():
|
||||
print()
|
||||
|
||||
|
||||
def example_ddl_dml():
|
||||
async def example_ddl_dml():
|
||||
print("=== DDL & DML ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
async with Client("localhost", 9472) as client:
|
||||
# Clean up
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS demo_products")
|
||||
await client.execute("DROP TABLE IF EXISTS demo_products")
|
||||
except Exception as exc:
|
||||
print(f"Cleanup warning: {exc}")
|
||||
|
||||
client.execute(
|
||||
await client.execute(
|
||||
"CREATE TABLE demo_products (id INT PRIMARY KEY, name STRING, price FLOAT)"
|
||||
)
|
||||
affected = client.execute(
|
||||
affected = await client.execute(
|
||||
"INSERT INTO demo_products (id, name, price) VALUES (1, 'Widget', 9.99)"
|
||||
)
|
||||
print(f"Insert affected rows: {affected}")
|
||||
|
||||
result = client.query("SELECT * FROM demo_products")
|
||||
result = await client.query("SELECT * FROM demo_products")
|
||||
print(f"Select returned {result.row_count} row(s)")
|
||||
for row in result:
|
||||
print(f" {row}")
|
||||
|
||||
client.execute("DROP TABLE demo_products")
|
||||
await client.execute("DROP TABLE demo_products")
|
||||
print("Table dropped")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
async def main():
|
||||
print("BaraDB Python Client Examples")
|
||||
print("Make sure BaraDB is running on localhost:9472")
|
||||
print()
|
||||
@@ -131,10 +132,10 @@ def main():
|
||||
|
||||
for fn in examples:
|
||||
try:
|
||||
fn()
|
||||
await fn()
|
||||
except Exception as exc:
|
||||
print(f"ERROR in {fn.__name__}: {exc}", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -48,6 +48,7 @@ packages = ["baradb"]
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
pythonpath = ["."]
|
||||
asyncio_mode = "auto"
|
||||
|
||||
[tool.mypy]
|
||||
strict = true
|
||||
|
||||
@@ -5,13 +5,16 @@ Requires a running BaraDB server on localhost:9472.
|
||||
These tests are skipped automatically if the server is unreachable.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import socket
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from baradb import Client, WireValue, QueryBuilder
|
||||
|
||||
|
||||
BARADB_HOST = "localhost"
|
||||
BARADB_PORT = 9472
|
||||
BARADB_HOST = os.environ.get("BARADB_HOST", "localhost")
|
||||
BARADB_PORT = int(os.environ.get("BARADB_PORT", "9472"))
|
||||
|
||||
|
||||
def _server_available() -> bool:
|
||||
@@ -29,41 +32,41 @@ pytestmark = pytest.mark.skipif(
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
@pytest_asyncio.fixture
|
||||
async def client():
|
||||
c = Client(BARADB_HOST, BARADB_PORT)
|
||||
c.connect()
|
||||
await c.connect()
|
||||
yield c
|
||||
c.close()
|
||||
await c.close()
|
||||
|
||||
|
||||
class TestConnection:
|
||||
def test_connect_and_close(self):
|
||||
async def test_connect_and_close(self):
|
||||
c = Client(BARADB_HOST, BARADB_PORT)
|
||||
assert not c.is_connected()
|
||||
c.connect()
|
||||
await c.connect()
|
||||
assert c.is_connected()
|
||||
c.close()
|
||||
await c.close()
|
||||
assert not c.is_connected()
|
||||
|
||||
def test_context_manager(self):
|
||||
with Client(BARADB_HOST, BARADB_PORT) as c:
|
||||
async def test_context_manager(self):
|
||||
async with Client(BARADB_HOST, BARADB_PORT) as c:
|
||||
assert c.is_connected()
|
||||
assert not c.is_connected()
|
||||
|
||||
|
||||
class TestPing:
|
||||
def test_ping(self, client):
|
||||
assert client.ping() is True
|
||||
async def test_ping(self, client):
|
||||
assert await client.ping() is True
|
||||
|
||||
|
||||
class TestQuery:
|
||||
def test_simple_select(self, client):
|
||||
result = client.query("SELECT 1 as one")
|
||||
async def test_simple_select(self, client):
|
||||
result = await client.query("SELECT 1 as one")
|
||||
assert result.row_count >= 0 # server may return rows or empty
|
||||
|
||||
def test_query_with_params(self, client):
|
||||
result = client.query_params(
|
||||
async def test_query_with_params(self, client):
|
||||
result = await client.query_params(
|
||||
"SELECT $1 as num, $2 as txt",
|
||||
[WireValue.int64(42), WireValue.string("hello")],
|
||||
)
|
||||
@@ -71,22 +74,22 @@ class TestQuery:
|
||||
|
||||
|
||||
class TestExecute:
|
||||
def test_create_table_and_insert(self, client):
|
||||
async def test_create_table_and_insert(self, client):
|
||||
# Clean up first (best-effort)
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS test_users")
|
||||
await client.execute("DROP TABLE IF EXISTS test_users")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
client.execute(
|
||||
await client.execute(
|
||||
"CREATE TABLE test_users (id INT PRIMARY KEY, name STRING, age INT)"
|
||||
)
|
||||
affected = client.execute(
|
||||
affected = await client.execute(
|
||||
"INSERT INTO test_users (id, name, age) VALUES (1, 'Alice', 30)"
|
||||
)
|
||||
assert affected >= 0
|
||||
|
||||
result = client.query("SELECT name, age FROM test_users WHERE id = 1")
|
||||
result = await client.query("SELECT name, age FROM test_users WHERE id = 1")
|
||||
assert result.row_count == 1
|
||||
row = result.rows[0]
|
||||
# Server returns all columns; map by name via dict iteration
|
||||
@@ -94,20 +97,20 @@ class TestExecute:
|
||||
assert row_dict["name"] == "Alice"
|
||||
assert row_dict["age"] == 30
|
||||
|
||||
client.execute("DROP TABLE test_users")
|
||||
await client.execute("DROP TABLE test_users")
|
||||
|
||||
|
||||
class TestQueryBuilder:
|
||||
def test_builder_exec(self, client):
|
||||
async def test_builder_exec(self, client):
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS test_products")
|
||||
await client.execute("DROP TABLE IF EXISTS test_products")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
client.execute(
|
||||
await client.execute(
|
||||
"CREATE TABLE test_products (id INT PRIMARY KEY, name STRING, price FLOAT)"
|
||||
)
|
||||
client.execute(
|
||||
await client.execute(
|
||||
"INSERT INTO test_products (id, name, price) VALUES (1, 'Widget', 9.99)"
|
||||
)
|
||||
|
||||
@@ -117,36 +120,36 @@ class TestQueryBuilder:
|
||||
.from_("test_products")
|
||||
.where("id = 1")
|
||||
)
|
||||
result = qb.exec()
|
||||
result = await qb.exec()
|
||||
assert result.row_count == 1
|
||||
|
||||
client.execute("DROP TABLE test_products")
|
||||
await client.execute("DROP TABLE test_products")
|
||||
|
||||
|
||||
class TestAuth:
|
||||
def test_auth_with_dummy_token(self, client):
|
||||
async def test_auth_with_dummy_token(self, client):
|
||||
# Server uses default JWT secret in dev mode, any token format is accepted
|
||||
# depending on server config. We just verify the method does not crash.
|
||||
try:
|
||||
client.auth("dummy-token-for-testing")
|
||||
await client.auth("dummy-token-for-testing")
|
||||
except Exception as exc:
|
||||
# Auth may fail with invalid token — that's acceptable for this test
|
||||
assert "Auth" in str(exc) or "error" in str(exc).lower()
|
||||
|
||||
|
||||
class TestTransactions:
|
||||
def test_transaction_begin_commit(self, client):
|
||||
async def test_transaction_begin_commit(self, client):
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS test_txn")
|
||||
await client.execute("DROP TABLE IF EXISTS test_txn")
|
||||
except Exception:
|
||||
pass
|
||||
client.execute("CREATE TABLE test_txn (id INT PRIMARY KEY)")
|
||||
await client.execute("CREATE TABLE test_txn (id INT PRIMARY KEY)")
|
||||
|
||||
client.execute("BEGIN")
|
||||
client.execute("INSERT INTO test_txn (id) VALUES (1)")
|
||||
client.execute("COMMIT")
|
||||
await client.execute("BEGIN")
|
||||
await client.execute("INSERT INTO test_txn (id) VALUES (1)")
|
||||
await client.execute("COMMIT")
|
||||
|
||||
result = client.query("SELECT COUNT(*) FROM test_txn")
|
||||
result = await client.query("SELECT COUNT(*) FROM test_txn")
|
||||
assert result.row_count >= 0
|
||||
|
||||
client.execute("DROP TABLE test_txn")
|
||||
await client.execute("DROP TABLE test_txn")
|
||||
|
||||
@@ -18,11 +18,11 @@ class TestWireValue:
|
||||
assert data == b"\x00"
|
||||
|
||||
def test_bool_true(self):
|
||||
wv = WireValue.bool_val(True)
|
||||
wv = WireValue.bool(True)
|
||||
assert wv.serialize() == b"\x01\x01"
|
||||
|
||||
def test_bool_false(self):
|
||||
wv = WireValue.bool_val(False)
|
||||
wv = WireValue.bool(False)
|
||||
assert wv.serialize() == b"\x01\x00"
|
||||
|
||||
def test_int8(self):
|
||||
@@ -70,7 +70,7 @@ class TestWireValue:
|
||||
assert data[5:] == b"hello"
|
||||
|
||||
def test_bytes(self):
|
||||
wv = WireValue.bytes_val(b"\xde\xad\xbe\xef")
|
||||
wv = WireValue.bytes(b"\xde\xad\xbe\xef")
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.BYTES
|
||||
length = struct.unpack(">I", data[1:5])[0]
|
||||
@@ -87,7 +87,7 @@ class TestWireValue:
|
||||
assert floats == [1.0, 2.0, 3.0]
|
||||
|
||||
def test_json(self):
|
||||
wv = WireValue.json_val('{"key": "value"}')
|
||||
wv = WireValue.json('{"key": "value"}')
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.JSON
|
||||
length = struct.unpack(">I", data[1:5])[0]
|
||||
@@ -95,7 +95,7 @@ class TestWireValue:
|
||||
|
||||
def test_array(self):
|
||||
inner = [WireValue.string("a"), WireValue.string("b")]
|
||||
wv = WireValue.array_val(inner)
|
||||
wv = WireValue.array(inner)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.ARRAY
|
||||
count = struct.unpack(">I", data[1:5])[0]
|
||||
@@ -103,7 +103,7 @@ class TestWireValue:
|
||||
|
||||
def test_object(self):
|
||||
inner = {"name": WireValue.string("Bara"), "age": WireValue.int32(42)}
|
||||
wv = WireValue.object_val(inner)
|
||||
wv = WireValue.object(inner)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.OBJECT
|
||||
count = struct.unpack(">I", data[1:5])[0]
|
||||
|
||||
Reference in New Issue
Block a user