f9f77b3a18
- JOIN execution: INNER/LEFT/RIGHT/FULL/CROSS with column disambiguation - Deadlock detection: wait-for graph wired into TxnManager.write() - TLS/SSL: OpenSSL via std/net for TCP wire protocol, auto self-signed certs - Parameterized queries: ? placeholders with WireValue binding - Wire protocol: mkQueryParams message support - HTTP /query endpoint accepts JSON params array - Nim client: query(sql, params) overload - Tests: 262 passing (15 new) All PLAN.md production blockers resolved.
185 lines
5.7 KiB
Nim
185 lines
5.7 KiB
Nim
## BaraDB Nim Client — Tests
|
|
import std/unittest
|
|
import std/strutils
|
|
import baradb/client
|
|
|
|
suite "QueryBuilder":
|
|
test "Simple SELECT":
|
|
let client = newClient()
|
|
let qb = newQueryBuilder(client)
|
|
let sql = qb.select("name", "age").from("users").build()
|
|
check sql == "SELECT name, age FROM users"
|
|
|
|
test "SELECT with WHERE":
|
|
let client = newClient()
|
|
let qb = newQueryBuilder(client)
|
|
let sql = qb.select("name").from("users").where("age > 18").build()
|
|
check sql == "SELECT name FROM users WHERE age > 18"
|
|
|
|
test "SELECT with JOIN":
|
|
let client = newClient()
|
|
let qb = newQueryBuilder(client)
|
|
let sql = qb.select("u.name", "o.total").from("users u")
|
|
.join("orders o", "u.id = o.user_id").build()
|
|
check "JOIN" in sql
|
|
check "ON" in sql
|
|
|
|
test "SELECT with GROUP BY and HAVING":
|
|
let client = newClient()
|
|
let qb = newQueryBuilder(client)
|
|
let sql = qb.select("dept", "count(*)").from("employees")
|
|
.groupBy("dept").having("count(*) > 5").build()
|
|
check "GROUP BY" in sql
|
|
check "HAVING" in sql
|
|
|
|
test "SELECT with ORDER BY and LIMIT":
|
|
let client = newClient()
|
|
let qb = newQueryBuilder(client)
|
|
let sql = qb.select("name").from("users")
|
|
.orderBy("name", "DESC").limit(10).offset(5).build()
|
|
check "ORDER BY name DESC" in sql
|
|
check "LIMIT 10" in sql
|
|
check "OFFSET 5" in sql
|
|
|
|
test "Full complex query":
|
|
let client = newClient()
|
|
let qb = newQueryBuilder(client)
|
|
let sql = qb.select("u.name", "count(*) as cnt")
|
|
.from("users u")
|
|
.leftJoin("orders o", "u.id = o.user_id")
|
|
.where("u.age > 18")
|
|
.groupBy("u.name")
|
|
.having("cnt > 3")
|
|
.orderBy("cnt", "DESC")
|
|
.limit(50)
|
|
.build()
|
|
check sql.startsWith("SELECT")
|
|
check "LEFT JOIN" in sql
|
|
check "WHERE" in sql
|
|
check "GROUP BY" in sql
|
|
check "HAVING" in sql
|
|
check "ORDER BY" in sql
|
|
check "LIMIT 50" in sql
|
|
|
|
suite "Client Config":
|
|
test "Default config":
|
|
let config = defaultConfig()
|
|
check config.host == "127.0.0.1"
|
|
check config.port == 5432
|
|
check config.database == "default"
|
|
|
|
test "Custom config":
|
|
let config = ClientConfig(host: "db.example.com", port: 9999,
|
|
database: "production", username: "app")
|
|
check config.host == "db.example.com"
|
|
check config.port == 9999
|
|
|
|
test "Client creation":
|
|
let client = newClient()
|
|
check not client.isConnected
|
|
|
|
suite "Wire Protocol":
|
|
test "Build query message":
|
|
let msg = makeQueryMessage(1, "SELECT 1")
|
|
check msg.len > 0
|
|
# First 4 bytes: mkQuery (0x02) in big-endian uint32
|
|
check msg[3] == 2'u8
|
|
|
|
test "Wire value serialization":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkString, strVal: "hello")
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkString
|
|
check decoded.strVal == "hello"
|
|
|
|
test "Null value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkNull)
|
|
buf.serializeValue(val)
|
|
check buf.len == 1
|
|
check buf[0] == 0x00'u8
|
|
|
|
test "Int64 value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkInt64, int64Val: 123456789)
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkInt64
|
|
check decoded.int64Val == 123456789
|
|
|
|
test "Array value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkArray, arrayVal: @[
|
|
WireValue(kind: fkString, strVal: "a"),
|
|
WireValue(kind: fkString, strVal: "b"),
|
|
])
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkArray
|
|
check decoded.arrayVal.len == 2
|
|
check decoded.arrayVal[0].strVal == "a"
|
|
|
|
suite "Client async":
|
|
test "Client nextId":
|
|
let client = newClient()
|
|
check not client.isConnected
|
|
|
|
|
|
suite "Wire Protocol Extended":
|
|
test "Int8 value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkInt8, int8Val: -42)
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkInt8
|
|
check decoded.int8Val == -42
|
|
|
|
test "Int16 value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkInt16, int16Val: -1000)
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkInt16
|
|
check decoded.int16Val == -1000
|
|
|
|
test "Float32 value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkFloat32, float32Val: 3.14'f32)
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkFloat32
|
|
check decoded.float32Val == 3.14'f32
|
|
|
|
test "Bytes value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkBytes, bytesVal: @[1'u8, 2'u8, 3'u8])
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkBytes
|
|
check decoded.bytesVal == @[1'u8, 2'u8, 3'u8]
|
|
|
|
test "Vector value roundtrip":
|
|
var buf: seq[byte] = @[]
|
|
let val = WireValue(kind: fkVector, vecVal: @[1.0'f32, 2.0'f32, 3.0'f32])
|
|
buf.serializeValue(val)
|
|
var pos = 0
|
|
let decoded = buf.deserializeValue(pos)
|
|
check decoded.kind == fkVector
|
|
check decoded.vecVal.len == 3
|
|
check decoded.vecVal[0] == 1.0'f32
|
|
|
|
test "WireValue to string":
|
|
check wireValueToString(WireValue(kind: fkNull)) == ""
|
|
check wireValueToString(WireValue(kind: fkBool, boolVal: true)) == "true"
|
|
check wireValueToString(WireValue(kind: fkInt32, int32Val: 42)) == "42"
|
|
check wireValueToString(WireValue(kind: fkString, strVal: "hello")) == "hello"
|
|
check wireValueToString(WireValue(kind: fkVector, vecVal: @[1.0'f32])) == "<vector:1>"
|