diff --git a/.gitignore b/.gitignore index 520411d..3205d55 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,5 @@ clients/rust/target/ # Nim compiled binaries clients/nim/src/baradb/client src/barabadb/storage/compaction -docker-compose.test.yml src/barabadb/query/executor tests/join_tests diff --git a/clients/javascript/tests/integration.test.js b/clients/javascript/tests/integration.test.js index e8ef14c..de29d72 100644 --- a/clients/javascript/tests/integration.test.js +++ b/clients/javascript/tests/integration.test.js @@ -8,8 +8,8 @@ const assert = require('node:assert'); const net = require('net'); const { Client, WireValue, QueryBuilder } = require('../baradb'); -const HOST = 'localhost'; -const PORT = 9472; +const HOST = process.env.BARADB_HOST || 'localhost'; +const PORT = parseInt(process.env.BARADB_PORT || '9472', 10); function serverAvailable() { return new Promise((resolve) => { diff --git a/clients/nim/tests/test_integration.nim b/clients/nim/tests/test_integration.nim index 63d00f4..53075b1 100644 --- a/clients/nim/tests/test_integration.nim +++ b/clients/nim/tests/test_integration.nim @@ -1,15 +1,17 @@ ## BaraDB Nim Client — Integration Tests -## Requires a running BaraDB server on localhost:9472. +## Requires a running BaraDB server. +## Set BARADB_HOST / BARADB_PORT env vars to override defaults. import std/unittest import std/asyncdispatch import std/asyncnet import std/strutils +import std/os import baradb/client const - TestHost = "127.0.0.1" - TestPort = 9472 + TestHost = getEnv("BARADB_HOST", "127.0.0.1") + TestPort = parseInt(getEnv("BARADB_PORT", "9472")) proc serverAvailable(): bool = try: diff --git a/clients/python/README.md b/clients/python/README.md index 5c21582..3c24218 100644 --- a/clients/python/README.md +++ b/clients/python/README.md @@ -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]" ``` diff --git a/clients/python/examples/basic.py b/clients/python/examples/basic.py index d72987c..1159b5a 100644 --- a/clients/python/examples/basic.py +++ b/clients/python/examples/basic.py @@ -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()) diff --git a/clients/python/pyproject.toml b/clients/python/pyproject.toml index 9c84c3a..86bbacd 100644 --- a/clients/python/pyproject.toml +++ b/clients/python/pyproject.toml @@ -48,6 +48,7 @@ packages = ["baradb"] [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["."] +asyncio_mode = "auto" [tool.mypy] strict = true diff --git a/clients/python/tests/test_integration.py b/clients/python/tests/test_integration.py index aa7b84b..a9daaf8 100644 --- a/clients/python/tests/test_integration.py +++ b/clients/python/tests/test_integration.py @@ -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") diff --git a/clients/python/tests/test_wire_protocol.py b/clients/python/tests/test_wire_protocol.py index da36ba8..d4c2a54 100644 --- a/clients/python/tests/test_wire_protocol.py +++ b/clients/python/tests/test_wire_protocol.py @@ -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] diff --git a/clients/rust/Cargo.toml b/clients/rust/Cargo.toml index 3d4611c..5855c04 100644 --- a/clients/rust/Cargo.toml +++ b/clients/rust/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" authors = ["BaraDB Team "] description = "Official async Rust client for BaraDB — binary protocol client" license = "Apache-2.0" -repository = "https://github.com/katehonz/barabaDB" +repository = "https://github.com/barabadb/baradadb.git" documentation = "https://docs.baradb.dev" readme = "README.md" keywords = ["database", "baradb", "multimodal", "client", "wire-protocol"] @@ -16,6 +16,7 @@ rust-version = "1.70" tokio = { version = "1.35", features = ["full"] } [dev-dependencies] +tokio = { version = "1.35", features = ["full"] } [[example]] name = "basic" diff --git a/clients/rust/README.md b/clients/rust/README.md index d8f310f..eafb830 100644 --- a/clients/rust/README.md +++ b/clients/rust/README.md @@ -23,7 +23,7 @@ tokio = { version = "1.35", features = ["full"] } Or from source: ```bash -git clone https://github.com/katehonz/barabaDB.git +git clone https://github.com/barabadb/baradadb.git cd clients/rust cargo build ``` diff --git a/clients/rust/examples/ping_test.rs b/clients/rust/examples/ping_test.rs index e88456f..f223936 100644 --- a/clients/rust/examples/ping_test.rs +++ b/clients/rust/examples/ping_test.rs @@ -1,11 +1,12 @@ use baradb::Client; -fn main() { - let mut client = Client::connect("localhost", 9472).unwrap(); +#[tokio::main] +async fn main() { + let mut client = Client::connect("localhost", 9472).await.unwrap(); println!("Connected: {}", client.is_connected()); - match client.ping() { + match client.ping().await { Ok(v) => println!("Ping: {}", v), Err(e) => println!("Ping error: {}", e), } - client.close(); + client.close().await; } diff --git a/clients/rust/tests/integration_tests.rs b/clients/rust/tests/integration_tests.rs index 8df8e66..7f0c07f 100644 --- a/clients/rust/tests/integration_tests.rs +++ b/clients/rust/tests/integration_tests.rs @@ -1,104 +1,118 @@ // BaraDB Rust Client — Integration Tests -// Requires a running BaraDB server on localhost:9472. +// Requires a running BaraDB server. +// Set BARADB_HOST / BARADB_PORT env vars to override defaults. use std::net::TcpStream; -use std::time::Duration; use baradb::{Client, QueryBuilder, WireValue}; -const HOST: &str = "127.0.0.1"; -const PORT: u16 = 9472; - -fn server_available() -> bool { - TcpStream::connect((HOST, PORT)).is_ok() +fn host() -> String { + std::env::var("BARADB_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()) } -#[test] -fn test_connect_and_close() { +fn port() -> u16 { + std::env::var("BARADB_PORT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(9472) +} + +fn server_available() -> bool { + TcpStream::connect((host().as_str(), port())).is_ok() +} + +#[tokio::test] +async fn test_connect_and_close() { if !server_available() { return; } - let mut client = Client::connect(HOST, PORT).unwrap(); + let mut client = Client::connect(&host(), port()).await.unwrap(); assert!(client.is_connected()); - client.close(); + client.close().await; assert!(!client.is_connected()); } -#[test] -fn test_ping() { +#[tokio::test] +async fn test_ping() { if !server_available() { return; } - let mut client = Client::connect(HOST, PORT).unwrap(); - assert!(client.ping().unwrap()); - client.close(); + let mut client = Client::connect(&host(), port()).await.unwrap(); + assert!(client.ping().await.unwrap()); + client.close().await; } -#[test] -fn test_simple_select() { +#[tokio::test] +async fn test_simple_select() { if !server_available() { return; } - let mut client = Client::connect(HOST, PORT).unwrap(); - let result = client.query("SELECT 1 as one").unwrap(); + let mut client = Client::connect(&host(), port()).await.unwrap(); + let result = client.query("SELECT 1 as one").await.unwrap(); assert!(result.row_count() >= 0); - client.close(); + client.close().await; } -#[test] -fn test_parameterized_query() { +#[tokio::test] +async fn test_parameterized_query() { if !server_available() { return; } - let mut client = Client::connect(HOST, PORT).unwrap(); + let mut client = Client::connect(&host(), port()).await.unwrap(); let result = client .query_params( "SELECT $1 as num, $2 as txt", &[WireValue::Int64(42), WireValue::String("hello".to_string())], ) + .await .unwrap(); assert!(result.row_count() >= 0); - client.close(); + client.close().await; } -#[test] -fn test_create_table_insert_select_drop() { +#[tokio::test] +async fn test_create_table_insert_select_drop() { if !server_available() { return; } - let mut client = Client::connect(HOST, PORT).unwrap(); + let mut client = Client::connect(&host(), port()).await.unwrap(); - let _ = client.execute("DROP TABLE IF EXISTS rust_test_users"); + let _ = client.execute("DROP TABLE IF EXISTS rust_test_users").await; client .execute("CREATE TABLE rust_test_users (id INT PRIMARY KEY, name STRING, age INT)") + .await .unwrap(); let affected = client .execute("INSERT INTO rust_test_users (id, name, age) VALUES (1, 'Alice', 30)") + .await .unwrap(); assert!(affected >= 0); let result = client .query("SELECT name, age FROM rust_test_users WHERE id = 1") + .await .unwrap(); assert_eq!(result.row_count(), 1); - client.execute("DROP TABLE rust_test_users").unwrap(); - client.close(); + client.execute("DROP TABLE rust_test_users").await.unwrap(); + client.close().await; } -#[test] -fn test_query_builder_exec() { +#[tokio::test] +async fn test_query_builder_exec() { if !server_available() { return; } - let mut client = Client::connect(HOST, PORT).unwrap(); + let mut client = Client::connect(&host(), port()).await.unwrap(); - let _ = client.execute("DROP TABLE IF EXISTS rust_test_products"); + let _ = client.execute("DROP TABLE IF EXISTS rust_test_products").await; client .execute("CREATE TABLE rust_test_products (id INT PRIMARY KEY, name STRING, price FLOAT)") + .await .unwrap(); client .execute("INSERT INTO rust_test_products (id, name, price) VALUES (1, 'Widget', 9.99)") + .await .unwrap(); let result = QueryBuilder::new(&mut client) @@ -106,21 +120,22 @@ fn test_query_builder_exec() { .from("rust_test_products") .where_clause("id = 1") .exec() + .await .unwrap(); assert_eq!(result.row_count(), 1); - client.execute("DROP TABLE rust_test_products").unwrap(); - client.close(); + client.execute("DROP TABLE rust_test_products").await.unwrap(); + client.close().await; } -#[test] -fn test_auth_dummy_token() { +#[tokio::test] +async fn test_auth_dummy_token() { if !server_available() { return; } - let mut client = Client::connect(HOST, PORT).unwrap(); - let res = client.auth("dummy-token-for-testing"); + let mut client = Client::connect(&host(), port()).await.unwrap(); + let res = client.auth("dummy-token-for-testing").await; // Dev server may accept or reject — both are fine match res { Ok(()) => {} @@ -129,5 +144,5 @@ fn test_auth_dummy_token() { assert!(msg.contains("Auth") || msg.to_lowercase().contains("error")); } } - client.close(); + client.close().await; } diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..6ce16a6 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,101 @@ +# BaraDB — Client Integration Test Stack +# +# This compose file starts BaraDB server and runs all client test suites. +# Because test containers run in parallel, do NOT use --abort-on-container-exit +# (the first finished container would kill the others). +# +# Recommended usage: +# ./scripts/test-clients.sh +# +# Or manually run each suite: +# docker compose -f docker-compose.test.yml up -d baradb +# docker compose -f docker-compose.test.yml run --rm test-python +# docker compose -f docker-compose.test.yml run --rm test-javascript +# docker compose -f docker-compose.test.yml run --rm test-nim +# docker compose -f docker-compose.test.yml run --rm test-rust +# docker compose -f docker-compose.test.yml down + +services: + baradb: + build: + context: . + dockerfile: Dockerfile + image: baradb:latest + ports: + - "9472:9472" + healthcheck: + test: ["CMD", "sh", "-c", "wget -qO- http://localhost:9912/health >/dev/null 2>&1"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + + test-python: + image: python:3.12-slim + depends_on: + baradb: + condition: service_healthy + environment: + - BARADB_HOST=baradb + - BARADB_PORT=9472 + volumes: + - ./clients/python:/workspace + working_dir: /workspace + command: > + sh -c " + pip install pytest pytest-asyncio -q && + pip install -e ".[dev]" -q && + pytest tests/test_wire_protocol.py tests/test_query_builder.py -v && + pytest tests/test_integration.py -v + " + + test-javascript: + image: node:20-slim + depends_on: + baradb: + condition: service_healthy + environment: + - BARADB_HOST=baradb + - BARADB_PORT=9472 + volumes: + - ./clients/javascript:/workspace + working_dir: /workspace + command: > + sh -c " + node --test tests/unit.test.js && + node --test tests/integration.test.js + " + + test-nim: + image: nimlang/nim:2.2.10 + depends_on: + baradb: + condition: service_healthy + environment: + - BARADB_HOST=baradb + - BARADB_PORT=9472 + volumes: + - ./clients/nim:/workspace + - ./clients/nim/src:/workspace/src + working_dir: /workspace + command: > + sh -c " + nim c --path:src -r tests/test_client.nim && + nim c --path:src -r tests/test_integration.nim + " + + test-rust: + image: rust:1.78 + depends_on: + baradb: + condition: service_healthy + environment: + - BARADB_HOST=baradb + - BARADB_PORT=9472 + volumes: + - ./clients/rust:/workspace + working_dir: /workspace + command: > + sh -c " + cargo test -- --test-threads=1 + " diff --git a/scripts/test-clients.sh b/scripts/test-clients.sh new file mode 100755 index 0000000..9bcbbae --- /dev/null +++ b/scripts/test-clients.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# BaraDB — Client Integration Test Runner +# Spins up the BaraDB server in Docker and runs all client test suites sequentially. +# +# Usage: +# ./scripts/test-clients.sh + +set -euo pipefail + +COMPOSE="docker compose -f docker-compose.test.yml" + +echo "=== Building BaraDB server image ===" +$COMPOSE build baradb + +echo "=== Starting BaraDB server ===" +$COMPOSE up -d baradb + +# Wait for server healthcheck +echo "=== Waiting for server to be healthy ===" +$COMPOSE ps baradb --format json 2>/dev/null | grep -q '"Health":"healthy"' || sleep 10 + +# Give a little extra time for the wire protocol port to be ready +sleep 3 + +run_test() { + local service=$1 + echo "" + echo "========================================" + echo "=== Running $service tests ===" + echo "========================================" + if $COMPOSE run --rm "$service"; then + echo "✅ $service tests PASSED" + else + echo "❌ $service tests FAILED" + EXIT_CODE=1 + fi +} + +EXIT_CODE=0 + +run_test test-python +run_test test-javascript +run_test test-nim +run_test test-rust + +echo "" +echo "========================================" +if [ "$EXIT_CODE" -eq 0 ]; then + echo "🎉 All client tests passed!" +else + echo "⚠️ Some client tests failed." +fi +echo "========================================" + +echo "=== Stopping BaraDB server ===" +$COMPOSE down -v + +exit $EXIT_CODE