feat(clients): professional clients with tests, CI/CD, and examples
- Python: pyproject.toml, pytest suite (unit + integration), README, examples - JavaScript: package.json, TypeScript definitions, node:test suite, README, examples - Nim: integration tests, examples, README, improved nimble tasks - Rust: fixed nodelay/localhost IPv6 bug, integration tests, examples, README - Added GitHub Actions CI for all 4 clients against Docker server - Added docker-compose.test.yml for local integration testing - Added .gitignore for each client
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
.venv
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
@@ -0,0 +1,141 @@
|
||||
# BaraDB Python Client
|
||||
|
||||
Official Python client for **BaraDB** — a multimodal database engine written in Nim.
|
||||
|
||||
## Features
|
||||
|
||||
- **Binary wire protocol** — fast, compact TCP communication
|
||||
- **Sync & blocking API** — simple to use in scripts and apps
|
||||
- **Query builder** — fluent SQL construction
|
||||
- **Parameterized queries** — safe from SQL injection
|
||||
- **Vector & JSON support** — first-class multimodal types
|
||||
- **Context managers** — `with` statement support
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install baradb
|
||||
```
|
||||
|
||||
Or from source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/barabadb/baradadb.git
|
||||
cd clients/python
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
from baradb import Client
|
||||
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
|
||||
result = client.query("SELECT name, age FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"], row["age"])
|
||||
|
||||
client.close()
|
||||
```
|
||||
|
||||
### Context Manager
|
||||
|
||||
```python
|
||||
from baradb import Client
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query("SELECT 1")
|
||||
print(result.row_count)
|
||||
```
|
||||
|
||||
### Parameterized Queries
|
||||
|
||||
```python
|
||||
from baradb import Client, WireValue
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
"SELECT * FROM users WHERE age > $1 AND country = $2",
|
||||
[WireValue.int64(18), WireValue.string("BG")],
|
||||
)
|
||||
for row in result:
|
||||
print(row)
|
||||
```
|
||||
|
||||
### Query Builder
|
||||
|
||||
```python
|
||||
from baradb import Client, QueryBuilder
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
qb = (
|
||||
QueryBuilder(client)
|
||||
.select("name", "email")
|
||||
.from_("users")
|
||||
.where("active = true")
|
||||
.order_by("name")
|
||||
.limit(10)
|
||||
)
|
||||
result = qb.exec()
|
||||
for row in result:
|
||||
print(row)
|
||||
```
|
||||
|
||||
### Vector Search
|
||||
|
||||
```python
|
||||
from baradb import Client, WireValue
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
"SELECT id, name FROM products ORDER BY embedding <-> $1 LIMIT 5",
|
||||
[WireValue.vector([0.1, 0.2, 0.3])],
|
||||
)
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
Unit tests (no server):
|
||||
|
||||
```bash
|
||||
pytest tests/test_wire_protocol.py tests/test_query_builder.py
|
||||
```
|
||||
|
||||
Integration tests (requires server on `localhost:9472`):
|
||||
|
||||
```bash
|
||||
# Start server
|
||||
docker run -d -p 9472:9472 baradb:latest
|
||||
|
||||
# Run all tests
|
||||
pytest
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `Client(host, port, database, username, password, timeout)`
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|------------|-------------|----------------------------|
|
||||
| `host` | `localhost` | Server hostname |
|
||||
| `port` | `9472` | TCP wire protocol port |
|
||||
| `database` | `default` | Default database |
|
||||
| `username` | `admin` | Username |
|
||||
| `password` | `""` | Password |
|
||||
| `timeout` | `30` | Socket timeout in seconds |
|
||||
|
||||
### Methods
|
||||
|
||||
- `connect()` — open TCP connection
|
||||
- `close()` — close connection
|
||||
- `query(sql) -> QueryResult` — execute SELECT-like query
|
||||
- `query_params(sql, params) -> QueryResult` — parameterized query
|
||||
- `execute(sql) -> int` — execute DDL/DML, returns affected rows
|
||||
- `auth(token)` — JWT authentication
|
||||
- `ping() -> bool` — health check
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
BaraDB Python Client
|
||||
|
||||
Official Python client for BaraDB — Multimodal Database Engine.
|
||||
Communicates via the BaraDB Wire Protocol (binary, big-endian, TCP).
|
||||
|
||||
Install:
|
||||
pip install baradb
|
||||
|
||||
Quick Start:
|
||||
from baradb import Client
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
result = client.query("SELECT name FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"])
|
||||
client.close()
|
||||
"""
|
||||
|
||||
from .core import (
|
||||
Client,
|
||||
QueryBuilder,
|
||||
QueryResult,
|
||||
WireValue,
|
||||
MsgKind,
|
||||
FieldKind,
|
||||
ResultFormat,
|
||||
)
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__all__ = [
|
||||
"Client",
|
||||
"QueryBuilder",
|
||||
"QueryResult",
|
||||
"WireValue",
|
||||
"MsgKind",
|
||||
"FieldKind",
|
||||
"ResultFormat",
|
||||
]
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
BaraDB Python Client — Basic Examples
|
||||
|
||||
This script demonstrates common operations with the BaraDB Python client.
|
||||
Run it while a BaraDB server is listening on localhost:9472.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from baradb import Client, QueryBuilder, WireValue
|
||||
|
||||
|
||||
def example_connection():
|
||||
print("=== Connection ===")
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
print(f"Connected: {client.is_connected()}")
|
||||
print(f"Ping: {client.ping()}")
|
||||
client.close()
|
||||
print(f"Connected after close: {client.is_connected()}")
|
||||
print()
|
||||
|
||||
|
||||
def example_context_manager():
|
||||
print("=== Context Manager ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
print(f"Inside context: {client.is_connected()}")
|
||||
print(f"Ping: {client.ping()}")
|
||||
print("Outside context: closed automatically")
|
||||
print()
|
||||
|
||||
|
||||
def example_simple_query():
|
||||
print("=== Simple Query ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
result = 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}")
|
||||
for row in result:
|
||||
print(f" answer={row['answer']}, db={row['db']}")
|
||||
print()
|
||||
|
||||
|
||||
def example_parameterized_query():
|
||||
print("=== Parameterized Query ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
"SELECT $1 as num, $2 as txt, $3 as flag",
|
||||
[
|
||||
WireValue.int64(123),
|
||||
WireValue.string("hello world"),
|
||||
WireValue.bool_val(True),
|
||||
],
|
||||
)
|
||||
for row in result:
|
||||
print(f" num={row['num']}, txt={row['txt']}, flag={row['flag']}")
|
||||
print()
|
||||
|
||||
|
||||
def example_query_builder():
|
||||
print("=== Query Builder ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
sql = (
|
||||
QueryBuilder(client)
|
||||
.select("id", "name")
|
||||
.from_("users")
|
||||
.where("active = true")
|
||||
.order_by("name", "ASC")
|
||||
.limit(5)
|
||||
.build()
|
||||
)
|
||||
print(f"Generated SQL: {sql}")
|
||||
print()
|
||||
|
||||
|
||||
def example_vector():
|
||||
print("=== Vector Value ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
"SELECT $1 as embedding",
|
||||
[WireValue.vector([0.1, 0.2, 0.3, 0.4])],
|
||||
)
|
||||
for row in result:
|
||||
print(f" embedding type: {type(row['embedding'])}")
|
||||
print()
|
||||
|
||||
|
||||
def example_ddl_dml():
|
||||
print("=== DDL & DML ===")
|
||||
with Client("localhost", 9472) as client:
|
||||
# Clean up
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS demo_products")
|
||||
except Exception as exc:
|
||||
print(f"Cleanup warning: {exc}")
|
||||
|
||||
client.execute(
|
||||
"CREATE TABLE demo_products (id INT PRIMARY KEY, name STRING, price FLOAT)"
|
||||
)
|
||||
affected = 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")
|
||||
print(f"Select returned {result.row_count} row(s)")
|
||||
for row in result:
|
||||
print(f" {row}")
|
||||
|
||||
client.execute("DROP TABLE demo_products")
|
||||
print("Table dropped")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
print("BaraDB Python Client Examples")
|
||||
print("Make sure BaraDB is running on localhost:9472")
|
||||
print()
|
||||
|
||||
examples = [
|
||||
example_connection,
|
||||
example_context_manager,
|
||||
example_simple_query,
|
||||
example_parameterized_query,
|
||||
example_query_builder,
|
||||
example_vector,
|
||||
example_ddl_dml,
|
||||
]
|
||||
|
||||
for fn in examples:
|
||||
try:
|
||||
fn()
|
||||
except Exception as exc:
|
||||
print(f"ERROR in {fn.__name__}: {exc}", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,59 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "baradb"
|
||||
version = "1.0.0"
|
||||
description = "Official Python client for BaraDB — Multimodal Database Engine"
|
||||
readme = "README.md"
|
||||
license = { text = "Apache-2.0" }
|
||||
requires-python = ">=3.9"
|
||||
authors = [
|
||||
{ name = "BaraDB Team", email = "team@baradb.dev" },
|
||||
]
|
||||
keywords = ["database", "baradb", "multimodal", "client", "wire-protocol"]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Topic :: Database",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.0",
|
||||
"pytest-asyncio>=0.21.0",
|
||||
"mypy>=1.0",
|
||||
"ruff>=0.1.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/barabadb/baradadb"
|
||||
Documentation = "https://docs.baradb.dev"
|
||||
Repository = "https://github.com/barabadb/baradadb.git"
|
||||
Issues = "https://github.com/barabadb/baradadb/issues"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["baradb"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
pythonpath = ["."]
|
||||
|
||||
[tool.mypy]
|
||||
strict = true
|
||||
warn_return_any = true
|
||||
warn_unused_configs = true
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
select = ["E", "F", "I", "W"]
|
||||
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
Integration tests for BaraDB Python client.
|
||||
Requires a running BaraDB server on localhost:9472.
|
||||
|
||||
These tests are skipped automatically if the server is unreachable.
|
||||
"""
|
||||
|
||||
import socket
|
||||
import pytest
|
||||
from baradb import Client, WireValue, QueryBuilder
|
||||
|
||||
|
||||
BARADB_HOST = "localhost"
|
||||
BARADB_PORT = 9472
|
||||
|
||||
|
||||
def _server_available() -> bool:
|
||||
"""Check if BaraDB server is listening."""
|
||||
try:
|
||||
with socket.create_connection((BARADB_HOST, BARADB_PORT), timeout=2):
|
||||
return True
|
||||
except (socket.timeout, ConnectionRefusedError, OSError):
|
||||
return False
|
||||
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not _server_available(),
|
||||
reason=f"BaraDB server not available at {BARADB_HOST}:{BARADB_PORT}",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
c = Client(BARADB_HOST, BARADB_PORT)
|
||||
c.connect()
|
||||
yield c
|
||||
c.close()
|
||||
|
||||
|
||||
class TestConnection:
|
||||
def test_connect_and_close(self):
|
||||
c = Client(BARADB_HOST, BARADB_PORT)
|
||||
assert not c.is_connected()
|
||||
c.connect()
|
||||
assert c.is_connected()
|
||||
c.close()
|
||||
assert not c.is_connected()
|
||||
|
||||
def test_context_manager(self):
|
||||
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
|
||||
|
||||
|
||||
class TestQuery:
|
||||
def test_simple_select(self, client):
|
||||
result = 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(
|
||||
"SELECT $1 as num, $2 as txt",
|
||||
[WireValue.int64(42), WireValue.string("hello")],
|
||||
)
|
||||
assert result is not None
|
||||
|
||||
|
||||
class TestExecute:
|
||||
def test_create_table_and_insert(self, client):
|
||||
# Clean up first (best-effort)
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS test_users")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
client.execute(
|
||||
"CREATE TABLE test_users (id INT PRIMARY KEY, name STRING, age INT)"
|
||||
)
|
||||
affected = 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")
|
||||
assert result.row_count == 1
|
||||
row = result.rows[0]
|
||||
# Server returns all columns; map by name via dict iteration
|
||||
row_dict = dict(zip(result.columns, row))
|
||||
assert row_dict["name"] == "Alice"
|
||||
assert row_dict["age"] == 30
|
||||
|
||||
client.execute("DROP TABLE test_users")
|
||||
|
||||
|
||||
class TestQueryBuilder:
|
||||
def test_builder_exec(self, client):
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS test_products")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
client.execute(
|
||||
"CREATE TABLE test_products (id INT PRIMARY KEY, name STRING, price FLOAT)"
|
||||
)
|
||||
client.execute(
|
||||
"INSERT INTO test_products (id, name, price) VALUES (1, 'Widget', 9.99)"
|
||||
)
|
||||
|
||||
qb = (
|
||||
QueryBuilder(client)
|
||||
.select("name", "price")
|
||||
.from_("test_products")
|
||||
.where("id = 1")
|
||||
)
|
||||
result = qb.exec()
|
||||
assert result.row_count == 1
|
||||
|
||||
client.execute("DROP TABLE test_products")
|
||||
|
||||
|
||||
class TestAuth:
|
||||
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")
|
||||
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):
|
||||
try:
|
||||
client.execute("DROP TABLE IF EXISTS test_txn")
|
||||
except Exception:
|
||||
pass
|
||||
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")
|
||||
|
||||
result = client.query("SELECT COUNT(*) FROM test_txn")
|
||||
assert result.row_count >= 0
|
||||
|
||||
client.execute("DROP TABLE test_txn")
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Unit tests for QueryBuilder — no running server required.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from baradb import Client, QueryBuilder
|
||||
|
||||
|
||||
class TestQueryBuilder:
|
||||
def test_simple_select(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = qb.select("name", "age").from_("users").build()
|
||||
assert sql == "SELECT name, age FROM users"
|
||||
|
||||
def test_select_all(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = qb.from_("users").build()
|
||||
assert sql == "SELECT * FROM users"
|
||||
|
||||
def test_where_single(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = qb.select("name").from_("users").where("age > 18").build()
|
||||
assert sql == "SELECT name FROM users WHERE age > 18"
|
||||
|
||||
def test_where_multiple(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = (
|
||||
qb.select("name")
|
||||
.from_("users")
|
||||
.where("age > 18")
|
||||
.where("active = true")
|
||||
.build()
|
||||
)
|
||||
assert sql == "SELECT name FROM users WHERE age > 18 AND active = true"
|
||||
|
||||
def test_join(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = (
|
||||
qb.select("u.name", "o.total")
|
||||
.from_("users u")
|
||||
.join("orders o", "u.id = o.user_id")
|
||||
.build()
|
||||
)
|
||||
assert "JOIN orders o ON u.id = o.user_id" in sql
|
||||
|
||||
def test_left_join(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = (
|
||||
qb.select("u.name", "o.total")
|
||||
.from_("users u")
|
||||
.left_join("orders o", "u.id = o.user_id")
|
||||
.build()
|
||||
)
|
||||
assert "LEFT JOIN orders o ON u.id = o.user_id" in sql
|
||||
|
||||
def test_group_by_having(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = (
|
||||
qb.select("dept", "count(*)")
|
||||
.from_("employees")
|
||||
.group_by("dept")
|
||||
.having("count(*) > 5")
|
||||
.build()
|
||||
)
|
||||
assert "GROUP BY dept" in sql
|
||||
assert "HAVING count(*) > 5" in sql
|
||||
|
||||
def test_order_by(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = qb.select("name").from_("users").order_by("name", "DESC").build()
|
||||
assert "ORDER BY name DESC" in sql
|
||||
|
||||
def test_limit_offset(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = qb.select("name").from_("users").limit(10).offset(5).build()
|
||||
assert "LIMIT 10" in sql
|
||||
assert "OFFSET 5" in sql
|
||||
|
||||
def test_full_complex_query(self):
|
||||
client = Client()
|
||||
qb = QueryBuilder(client)
|
||||
sql = (
|
||||
qb.select("u.name", "count(*) as cnt")
|
||||
.from_("users u")
|
||||
.left_join("orders o", "u.id = o.user_id")
|
||||
.where("u.age > 18")
|
||||
.group_by("u.name")
|
||||
.having("cnt > 3")
|
||||
.order_by("cnt", "DESC")
|
||||
.limit(50)
|
||||
.build()
|
||||
)
|
||||
assert sql.startswith("SELECT")
|
||||
assert "FROM users u" in sql
|
||||
assert "LEFT JOIN orders o ON u.id = o.user_id" in sql
|
||||
assert "WHERE u.age > 18" in sql
|
||||
assert "GROUP BY u.name" in sql
|
||||
assert "HAVING cnt > 3" in sql
|
||||
assert "ORDER BY cnt DESC" in sql
|
||||
assert "LIMIT 50" in sql
|
||||
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
Unit tests for BaraDB Python client wire protocol.
|
||||
No running server required.
|
||||
"""
|
||||
|
||||
import struct
|
||||
import pytest
|
||||
from baradb import WireValue, FieldKind, MsgKind, ResultFormat
|
||||
|
||||
|
||||
class TestWireValue:
|
||||
"""Tests for WireValue serialization / deserialization."""
|
||||
|
||||
def test_null(self):
|
||||
wv = WireValue.null()
|
||||
assert wv.kind == FieldKind.NULL
|
||||
data = wv.serialize()
|
||||
assert data == b"\x00"
|
||||
|
||||
def test_bool_true(self):
|
||||
wv = WireValue.bool_val(True)
|
||||
assert wv.serialize() == b"\x01\x01"
|
||||
|
||||
def test_bool_false(self):
|
||||
wv = WireValue.bool_val(False)
|
||||
assert wv.serialize() == b"\x01\x00"
|
||||
|
||||
def test_int8(self):
|
||||
wv = WireValue.int8(-42)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.INT8
|
||||
assert struct.unpack(">b", data[1:])[0] == -42
|
||||
|
||||
def test_int16(self):
|
||||
wv = WireValue.int16(-1000)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.INT16
|
||||
assert struct.unpack(">h", data[1:])[0] == -1000
|
||||
|
||||
def test_int32(self):
|
||||
wv = WireValue.int32(123456)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.INT32
|
||||
assert struct.unpack(">i", data[1:])[0] == 123456
|
||||
|
||||
def test_int64(self):
|
||||
wv = WireValue.int64(9999999999)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.INT64
|
||||
assert struct.unpack(">q", data[1:])[0] == 9999999999
|
||||
|
||||
def test_float32(self):
|
||||
wv = WireValue.float32(3.14)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.FLOAT32
|
||||
assert abs(struct.unpack(">f", data[1:])[0] - 3.14) < 0.01
|
||||
|
||||
def test_float64(self):
|
||||
wv = WireValue.float64(2.718281828)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.FLOAT64
|
||||
assert abs(struct.unpack(">d", data[1:])[0] - 2.718281828) < 1e-9
|
||||
|
||||
def test_string(self):
|
||||
wv = WireValue.string("hello")
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.STRING
|
||||
length = struct.unpack(">I", data[1:5])[0]
|
||||
assert length == 5
|
||||
assert data[5:] == b"hello"
|
||||
|
||||
def test_bytes(self):
|
||||
wv = WireValue.bytes_val(b"\xde\xad\xbe\xef")
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.BYTES
|
||||
length = struct.unpack(">I", data[1:5])[0]
|
||||
assert length == 4
|
||||
assert data[5:] == b"\xde\xad\xbe\xef"
|
||||
|
||||
def test_vector(self):
|
||||
wv = WireValue.vector([1.0, 2.0, 3.0])
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.VECTOR
|
||||
dim = struct.unpack(">I", data[1:5])[0]
|
||||
assert dim == 3
|
||||
floats = [struct.unpack(">f", data[5 + i * 4 : 9 + i * 4])[0] for i in range(3)]
|
||||
assert floats == [1.0, 2.0, 3.0]
|
||||
|
||||
def test_json(self):
|
||||
wv = WireValue.json_val('{"key": "value"}')
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.JSON
|
||||
length = struct.unpack(">I", data[1:5])[0]
|
||||
assert length == 16
|
||||
|
||||
def test_array(self):
|
||||
inner = [WireValue.string("a"), WireValue.string("b")]
|
||||
wv = WireValue.array_val(inner)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.ARRAY
|
||||
count = struct.unpack(">I", data[1:5])[0]
|
||||
assert count == 2
|
||||
|
||||
def test_object(self):
|
||||
inner = {"name": WireValue.string("Bara"), "age": WireValue.int32(42)}
|
||||
wv = WireValue.object_val(inner)
|
||||
data = wv.serialize()
|
||||
assert data[0] == FieldKind.OBJECT
|
||||
count = struct.unpack(">I", data[1:5])[0]
|
||||
assert count == 2
|
||||
|
||||
|
||||
class TestMessageKinds:
|
||||
"""Sanity checks for protocol constants."""
|
||||
|
||||
def test_client_kinds(self):
|
||||
assert MsgKind.CLIENT_HANDSHAKE == 0x01
|
||||
assert MsgKind.QUERY == 0x02
|
||||
assert MsgKind.QUERY_PARAMS == 0x03
|
||||
assert MsgKind.EXECUTE == 0x04
|
||||
assert MsgKind.BATCH == 0x05
|
||||
assert MsgKind.TRANSACTION == 0x06
|
||||
assert MsgKind.CLOSE == 0x07
|
||||
assert MsgKind.PING == 0x08
|
||||
assert MsgKind.AUTH == 0x09
|
||||
|
||||
def test_server_kinds(self):
|
||||
assert MsgKind.SERVER_HANDSHAKE == 0x80
|
||||
assert MsgKind.READY == 0x81
|
||||
assert MsgKind.DATA == 0x82
|
||||
assert MsgKind.COMPLETE == 0x83
|
||||
assert MsgKind.ERROR == 0x84
|
||||
assert MsgKind.AUTH_CHALLENGE == 0x85
|
||||
assert MsgKind.AUTH_OK == 0x86
|
||||
assert MsgKind.SCHEMA_CHANGE == 0x87
|
||||
assert MsgKind.PONG == 0x88
|
||||
assert MsgKind.TRANSACTION_STATE == 0x89
|
||||
|
||||
def test_result_formats(self):
|
||||
assert ResultFormat.BINARY == 0x00
|
||||
assert ResultFormat.JSON == 0x01
|
||||
assert ResultFormat.TEXT == 0x02
|
||||
Reference in New Issue
Block a user