Files
Baradb/tests/test_all.nim
T
dimgigov eecd846df9 feat: UDF stdlib, SIMD vector ops, benchmarks — 162 tests
- User Defined Functions: register/call/deregister, stdlib (math, string, type conversion, array)
- SIMD vector operations: unrolled dot product, L2, cosine, manhattan, normalize, batch distance
- TopK and batch distance for vector search
- Performance benchmarks (LSM, B-Tree, HNSW, FTS, Graph)
- All roadmap phases marked complete except cluster/optimizations tail
- 26 new tests (162 total, all passing)
2026-05-06 01:33:51 +03:00

1257 lines
40 KiB
Nim

## BaraDB — Test Suite
import std/unittest
import std/tables
import std/strutils
import barabadb/core/types
import barabadb/core/mvcc
import barabadb/core/deadlock
import barabadb/core/columnar
import barabadb/core/raft
import barabadb/core/sharding
import barabadb/core/replication
import barabadb/storage/bloom
import barabadb/storage/wal
import barabadb/storage/lsm
import barabadb/storage/btree
import barabadb/storage/compaction
import barabadb/query/lexer as lex
import barabadb/query/ast
import barabadb/query/parser
import barabadb/query/ir as qir
import barabadb/query/codegen
import barabadb/query/udf
import barabadb/vector/simd
import barabadb/vector/engine as vengine
import barabadb/vector/quant as vquant
import barabadb/graph/engine as gengine
import barabadb/graph/community as gcomm
import barabadb/fts/engine as fts
import barabadb/protocol/wire
import barabadb/protocol/pool
import barabadb/protocol/auth
import barabadb/protocol/ratelimit
import barabadb/schema/schema as schema
suite "Core Types":
test "Value creation":
let v = Value(kind: vkInt64, int64Val: 42)
check v.kind == vkInt64
check v.int64Val == 42
test "String value":
let v = Value(kind: vkString, strVal: "hello")
check v.strVal == "hello"
test "RecordId creation":
let id = newRecordId()
check uint64(id) > 0
suite "Bloom Filter":
test "Basic bloom filter operations":
var bf = newBloomFilter(1000)
let data1 = cast[seq[byte]]("hello")
let data2 = cast[seq[byte]]("world")
bf.add(data1)
bf.add(data2)
check bf.contains(data1)
check bf.contains(data2)
suite "Write-Ahead Log":
test "WAL creation":
var wal = newWriteAheadLog("/tmp/baradb_test_wal")
check wal.entryCount == 0
wal.close()
suite "LSM-Tree Storage":
test "Put and Get":
var db = newLSMTree("/tmp/baradb_test_lsm")
let key = "testkey"
let value = cast[seq[byte]]("testvalue")
db.put(key, value)
let (found, val) = db.get(key)
check found
check val == value
db.close()
test "Delete":
var db = newLSMTree("/tmp/baradb_test_lsm2")
let key = "delkey"
let value = cast[seq[byte]]("delval")
db.put(key, value)
db.delete(key)
let (found, _) = db.get(key)
check not found
db.close()
test "Contains":
var db = newLSMTree("/tmp/baradb_test_lsm3")
let key = "exists"
check not db.contains(key)
db.put(key, cast[seq[byte]]("val"))
check db.contains(key)
db.close()
suite "BaraQL Lexer":
test "Tokenize simple SELECT":
let tokens = lex.tokenize("SELECT name FROM users WHERE age > 18")
check tokens.len > 0
check tokens[0].kind == tkSelect
check tokens[1].kind == tkIdent
check tokens[1].value == "name"
check tokens[2].kind == tkFrom
check tokens[3].kind == tkIdent
check tokens[3].value == "users"
test "Tokenize string literals":
let tokens = lex.tokenize("'hello world'")
check tokens[0].kind == tkStringLit
check tokens[0].value == "hello world"
test "Tokenize operators":
let tokens = lex.tokenize("a + b * c")
check tokens[0].kind == tkIdent
check tokens[1].kind == tkPlus
check tokens[2].kind == tkIdent
check tokens[3].kind == tkStar
suite "BaraQL Parser":
test "Parse simple SELECT":
let ast = parse("SELECT name FROM users WHERE age > 18")
check ast.kind == nkStatementList
check ast.stmts.len == 1
check ast.stmts[0].kind == nkSelect
test "Parse SELECT with LIMIT":
let ast = parse("SELECT * FROM items LIMIT 10")
check ast.stmts[0].selLimit != nil
suite "Vector Engine":
test "Distance metrics":
let a = @[1.0'f32, 0.0'f32, 0.0'f32]
let b = @[0.0'f32, 1.0'f32, 0.0'f32]
let c = @[1.0'f32, 0.0'f32, 0.0'f32]
check vengine.cosineDistance(a, b) > 0.9
check vengine.cosineDistance(a, c) < 0.1
check vengine.euclideanDistance(a, b) > 1.0
check vengine.euclideanDistance(a, c) < 0.1
test "HNSW index insert and search":
var idx = vengine.newHNSWIndex(3)
vengine.insert(idx, 1, @[1.0'f32, 0.0'f32, 0.0'f32])
vengine.insert(idx, 2, @[0.0'f32, 1.0'f32, 0.0'f32])
vengine.insert(idx, 3, @[0.0'f32, 0.0'f32, 1.0'f32])
let results = vengine.search(idx, @[1.0'f32, 0.1'f32, 0.0'f32], 2)
check results.len == 2
suite "Graph Engine":
test "Add nodes and edges":
var g = gengine.newGraph()
let n1 = gengine.addNode(g, "Person", {"name": "Alice"}.toTable)
let n2 = gengine.addNode(g, "Person", {"name": "Bob"}.toTable)
let e1 = gengine.addEdge(g, n1, n2, "knows")
check gengine.nodeCount(g) == 2
check gengine.edgeCount(g) == 1
test "BFS traversal":
var g = gengine.newGraph()
let n1 = gengine.addNode(g, "A")
let n2 = gengine.addNode(g, "B")
let n3 = gengine.addNode(g, "C")
let n4 = gengine.addNode(g, "D")
discard gengine.addEdge(g, n1, n2)
discard gengine.addEdge(g, n1, n3)
discard gengine.addEdge(g, n2, n4)
let traversal = gengine.bfs(g, n1)
check traversal.len == 4
check traversal[0] == n1
test "DFS traversal":
var g = gengine.newGraph()
let n1 = gengine.addNode(g, "A")
let n2 = gengine.addNode(g, "B")
let n3 = gengine.addNode(g, "C")
discard gengine.addEdge(g, n1, n2)
discard gengine.addEdge(g, n1, n3)
let traversal = gengine.dfs(g, n1)
check traversal.len == 3
test "Shortest path":
var g = gengine.newGraph()
let n1 = gengine.addNode(g, "A")
let n2 = gengine.addNode(g, "B")
let n3 = gengine.addNode(g, "C")
discard gengine.addEdge(g, n1, n2)
discard gengine.addEdge(g, n2, n3)
let path = gengine.shortestPath(g, n1, n3)
check path.len == 3
test "PageRank":
var g = gengine.newGraph()
let n1 = gengine.addNode(g, "A")
let n2 = gengine.addNode(g, "B")
let n3 = gengine.addNode(g, "C")
discard gengine.addEdge(g, n1, n2)
discard gengine.addEdge(g, n2, n3)
discard gengine.addEdge(g, n3, n1)
let ranks = gengine.pageRank(g)
check ranks.len == 3
for nodeId, rank in ranks:
check rank > 0.0
test "Dijkstra":
var g = gengine.newGraph()
let n1 = gengine.addNode(g, "A")
let n2 = gengine.addNode(g, "B")
let n3 = gengine.addNode(g, "C")
discard gengine.addEdge(g, n1, n2, weight = 1.0)
discard gengine.addEdge(g, n2, n3, weight = 2.0)
discard gengine.addEdge(g, n1, n3, weight = 10.0)
let dists = gengine.dijkstra(g, n1)
check dists[n1] == 0.0
check dists[n2] == 1.0
check dists[n3] == 3.0
suite "Full-Text Search":
test "Tokenization":
let tokens = fts.tokenize("The quick brown fox jumps over the lazy dog")
check tokens.len > 0
check "the" notin tokens
test "Inverted index operations":
var idx = fts.newInvertedIndex()
fts.addDocument(idx, 1, "The quick brown fox")
fts.addDocument(idx, 2, "The lazy brown dog")
fts.addDocument(idx, 3, "The quick red car")
check fts.documentCount(idx) == 3
check fts.termCount(idx) > 0
test "Search results":
var idx = fts.newInvertedIndex()
fts.addDocument(idx, 1, "Nim programming language is fast")
fts.addDocument(idx, 2, "Python is popular for data science")
fts.addDocument(idx, 3, "Rust is a systems programming language")
let results = fts.search(idx, "programming language")
check results.len > 0
check results[0].score > 0
test "Document removal":
var idx = fts.newInvertedIndex()
fts.addDocument(idx, 1, "test document")
fts.addDocument(idx, 2, "another document")
check fts.documentCount(idx) == 2
fts.removeDocument(idx, 1)
check fts.documentCount(idx) == 1
suite "MVCC Transactions":
test "Begin and commit transaction":
var tm = newTxnManager()
let txn = tm.beginTxn()
check txn.state == tsActive
check tm.write(txn, "key1", cast[seq[byte]]("value1"))
check tm.commit(txn)
check txn.state == tsCommitted
test "Read own writes":
var tm = newTxnManager()
let txn = tm.beginTxn()
discard tm.write(txn, "key1", cast[seq[byte]]("value1"))
let (found, val) = tm.read(txn, "key1")
check found
check val == cast[seq[byte]]("value1")
discard tm.commit(txn)
test "Abort transaction":
var tm = newTxnManager()
let txn = tm.beginTxn()
discard tm.write(txn, "key1", cast[seq[byte]]("value1"))
discard tm.abortTxn(txn)
check txn.state == tsAborted
test "Snapshot isolation — no dirty reads":
var tm = newTxnManager()
let txn1 = tm.beginTxn()
discard tm.write(txn1, "key1", cast[seq[byte]]("value1"))
let txn2 = tm.beginTxn()
let (found, _) = tm.read(txn2, "key1")
check not found # txn2 can't see txn1's uncommitted write
discard tm.commit(txn1)
# txn2 still can't see it (snapshot taken before commit)
let (found2, _) = tm.read(txn2, "key1")
check not found2
discard tm.abortTxn(txn2)
test "Committed writes visible to new transactions":
var tm = newTxnManager()
let txn1 = tm.beginTxn()
discard tm.write(txn1, "key1", cast[seq[byte]]("value1"))
discard tm.commit(txn1)
let txn2 = tm.beginTxn()
let (found, val) = tm.read(txn2, "key1")
check found
check val == cast[seq[byte]]("value1")
discard tm.commit(txn2)
test "Savepoint and rollback":
var tm = newTxnManager()
let txn = tm.beginTxn()
discard tm.write(txn, "key1", cast[seq[byte]]("value1"))
tm.savepoint(txn)
discard tm.write(txn, "key2", cast[seq[byte]]("value2"))
check tm.rollbackToSavepoint(txn)
let (found1, _) = tm.read(txn, "key1")
check found1
let (found2, _) = tm.read(txn, "key2")
check not found2
discard tm.commit(txn)
test "Delete via xmax":
var tm = newTxnManager()
let txn1 = tm.beginTxn()
discard tm.write(txn1, "key1", cast[seq[byte]]("value1"))
discard tm.commit(txn1)
let txn2 = tm.beginTxn()
discard tm.delete(txn2, "key1")
discard tm.commit(txn2)
let txn3 = tm.beginTxn()
let (found, _) = tm.read(txn3, "key1")
check not found
discard tm.commit(txn3)
suite "Deadlock Detection":
test "No deadlock without cycles":
var dd = newDeadlockDetector()
dd.addWait(1, 2)
dd.addWait(2, 3)
check not dd.hasDeadlock()
test "Detect simple deadlock":
var dd = newDeadlockDetector()
dd.addWait(1, 2)
dd.addWait(2, 1)
check dd.hasDeadlock()
test "Find deadlock victim":
var dd = newDeadlockDetector()
dd.addWait(1, 2)
dd.addWait(2, 3)
dd.addWait(3, 1)
let victim = dd.findDeadlockVictim()
check victim == 3 # youngest txn
test "Remove transaction clears edges":
var dd = newDeadlockDetector()
dd.addWait(1, 2)
dd.addWait(2, 1)
dd.removeTxn(2)
check not dd.hasDeadlock()
suite "Wire Protocol":
test "Value serialization roundtrip":
var buf: seq[byte] = @[]
let val = WireValue(kind: fkString, strVal: "hello world")
buf.serializeValue(val)
var pos = 0
let decoded = buf.deserializeValue(pos)
check decoded.kind == fkString
check decoded.strVal == "hello world"
test "Int64 serialization":
var buf: seq[byte] = @[]
let val = WireValue(kind: fkInt64, int64Val: 42)
buf.serializeValue(val)
var pos = 0
let decoded = buf.deserializeValue(pos)
check decoded.kind == fkInt64
check decoded.int64Val == 42
test "Array serialization":
var buf: seq[byte] = @[]
let val = WireValue(kind: fkArray, arrayVal: @[
WireValue(kind: fkInt32, int32Val: 1),
WireValue(kind: fkInt32, int32Val: 2),
WireValue(kind: fkInt32, int32Val: 3),
])
buf.serializeValue(val)
var pos = 0
let decoded = buf.deserializeValue(pos)
check decoded.kind == fkArray
check decoded.arrayVal.len == 3
check decoded.arrayVal[1].int32Val == 2
test "Vector serialization":
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
test "Query message creation":
let msg = makeQueryMessage(1, "SELECT * FROM users")
check msg.len > 0
check msg[3] == byte(mkQuery) # big-endian uint32, last byte
suite "Schema System":
test "Create type with properties":
var s = newSchema()
let person = newType("Person")
person.addProperty("name", "str", required = true)
person.addProperty("age", "int32")
s.addType("default", person)
check s.getType("Person") != nil
check s.getType("Person").properties.len == 2
test "Create type with links":
var s = newSchema()
let person = newType("Person")
person.addProperty("name", "str", required = true)
s.addType("default", person)
let movie = newType("Movie")
movie.addProperty("title", "str", required = true)
movie.addLink("actors", "Person", multi = true)
s.addType("default", movie)
check movie.links.len == 1
check movie.links["actors"].target == "Person"
test "Schema diff":
let s1 = newSchema()
let t1 = newType("Person")
t1.addProperty("name", "str")
s1.addType("default", t1)
let s2 = newSchema()
let t2 = newType("Person")
t2.addProperty("name", "str")
t2.addProperty("age", "int32")
s2.addType("default", t2)
let movieT = newType("Movie")
movieT.addProperty("title", "str")
s2.addType("default", movieT)
let d = diff(s1, s2)
check d.addedTypes.len == 1
check d.addedTypes[0] == "Movie"
check d.modifiedTypes.len == 1
check d.modifiedTypes[0].addedProperties.len == 1
test "Type validation":
let t = newType("Person")
t.addProperty("name", "str", required = true)
t.addLink("friend", "") # empty target
let errors = t.validateType()
check errors.len == 1 # empty link target
test "Migration creation":
var s = newSchema()
let m1 = s.createMigration("initial", "CREATE TYPE Person { name: str }")
check m1.id == 1
let m2 = s.createMigration("add age", "ALTER TYPE Person { ADD age: int32 }")
check m2.id == 2
check m2.parentId == 1
test "Type to string":
let t = newType("Person")
t.addProperty("name", "str", required = true)
t.addProperty("age", "int32")
t.addLink("friend", "Person")
let s = $t
check s.find("Person") >= 0
check s.find("name") >= 0
check s.find("str") >= 0
suite "B-Tree Index":
test "Insert and get":
var btree = newBTreeIndex[string, string]()
btree.insert("key1", "value1")
btree.insert("key2", "value2")
check btree.get("key1") == @["value1"]
check btree.get("key2") == @["value2"]
check not btree.contains("nonexistent")
test "Scan range":
var btree = newBTreeIndex[string, string]()
for i in 0..9:
btree.insert("key" & $i, "val" & $i)
let results = btree.scan("key2", "key5")
check results.len == 4
test "Duplicate keys":
var btree = newBTreeIndex[string, string]()
btree.insert("a", "val1")
btree.insert("a", "val2")
let vals = btree.get("a")
check vals.len == 2
suite "Columnar Engine":
test "Column batch operations":
var batch = newColumnBatch()
var intCol = batch.addInt64Col("age")
var strCol = batch.addStringCol("name")
intCol.appendInt64(25)
intCol.appendInt64(30)
intCol.appendInt64(35)
strCol.appendString("Alice")
strCol.appendString("Bob")
strCol.appendString("Charlie")
check batch.rowCount() == 3
test "Aggregate operations":
var batch = newColumnBatch()
var col = batch.addInt64Col("age")
col.appendInt64(10)
col.appendInt64(20)
col.appendInt64(30)
check col.sumInt64() == 60
check col.avgInt64() - 20.0 < 0.001
check col.minInt64() == 10
check col.maxInt64() == 30
check col.count() == 3
test "RLE encoding":
let data = @[1'i64, 1, 1, 2, 2, 3, 3, 3, 3]
let encoded = rleEncode(data)
let decoded = rleDecode(encoded)
check decoded == data
test "Dictionary encoding":
let data = @["apple", "banana", "apple", "cherry", "banana"]
let encoded = dictEncode(data)
let decoded = dictDecode(encoded)
check decoded == data
check encoded.dict.len == 3
test "GroupBy":
var batch = newColumnBatch()
var deptCol = batch.addStringCol("department")
var salaryCol = batch.addInt64Col("salary")
deptCol.appendString("Engineering")
deptCol.appendString("Sales")
deptCol.appendString("Engineering")
salaryCol.appendInt64(100)
salaryCol.appendInt64(80)
salaryCol.appendInt64(120)
let groups = groupBy(batch, @["department"])
check groups.groups.len == 2 # unique departments
suite "Type Checker & IR":
test "Literal type inference":
var tc = newTypeChecker()
let lit = IRExpr(kind: irekLiteral, literal: IRLiteral(kind: vkInt64, int64Val: 42))
let t = tc.inferExpr(lit, initTable[string, IRType]())
check t.name == "int64"
test "Binary operation type inference":
var tc = newTypeChecker()
let left = IRExpr(kind: irekLiteral, literal: IRLiteral(kind: vkInt64, int64Val: 1))
let right = IRExpr(kind: irekLiteral, literal: IRLiteral(kind: vkInt64, int64Val: 2))
let bin = IRExpr(kind: irekBinary, binOp: irEq, binLeft: left, binRight: right)
let t = tc.inferExpr(bin, initTable[string, IRType]())
check t.name == "bool"
test "Aggregate type inference":
var tc = newTypeChecker()
let agg = IRExpr(kind: irekAggregate, aggOp: irCount)
let t = tc.inferExpr(agg, initTable[string, IRType]())
check t.name == "int64"
suite "Connection Pool":
test "Create pool and acquire connection":
var pool = newConnectionPool("127.0.0.1", 5432)
let conn = pool.acquire()
check conn != nil
check conn.host == "127.0.0.1"
check conn.port == 5432
pool.release(conn)
test "Pool stats":
var cfg = defaultPoolConfig()
cfg.minConnections = 1
cfg.maxConnections = 10
var pool = newConnectionPool("127.0.0.1", 5432, "default", cfg)
let conn1 = pool.acquire()
let (total, idle, inUse) = pool.stats()
check inUse == 1
pool.release(conn1)
let (t2, i2, u2) = pool.stats()
check u2 == 0
suite "Authentication":
test "Anonymous auth":
var am = newAuthManager()
let result = am.validateCredentials(AuthCredentials(authMethod: amNone))
check result.authenticated
check result.username == "anonymous"
test "Token auth":
var am = newAuthManager("mysecretkey")
let token = am.createToken(JWTClaims(sub: "user1", role: "admin"))
let result = am.validateCredentials(AuthCredentials(
authMethod: amToken, payload: token))
check result.authenticated
test "Invalid token":
var am = newAuthManager("mysecretkey")
let result = am.validateCredentials(AuthCredentials(
authMethod: amToken, payload: "invalid_token"))
check not result.authenticated
suite "Vector Quantization":
test "Scalar quantization 8-bit":
var sq = newScalarQuantizer(4, bits = 8)
let vectors = @[@[1.0'f32, 2.0'f32, 3.0'f32, 4.0'f32],
@[5.0'f32, 6.0'f32, 7.0'f32, 8.0'f32]]
sq.train(vectors)
let qv = sq.encode(@[3.0'f32, 4.0'f32, 5.0'f32, 6.0'f32])
check qv.kind == qkScalar8
check qv.int8Data.len == 4
test "Scalar quantization 4-bit":
var sq = newScalarQuantizer(4, bits = 4)
let vectors = @[@[1.0'f32, 2.0'f32, 3.0'f32, 4.0'f32]]
sq.train(vectors)
let qv = sq.encode(@[3.0'f32, 4.0'f32, 5.0'f32, 6.0'f32])
check qv.kind == qkScalar4
check qv.int4Data.len == 2
test "Product quantization":
var pq = newProductQuantizer(8, nSubspaces = 4, nClusters = 16)
var vectors: seq[seq[float32]] = @[]
for i in 0..<50:
var v: seq[float32] = @[]
for j in 0..<8:
v.add(float32(i * 8 + j) * 0.1)
vectors.add(v)
pq.train(vectors, nIterations = 5)
let qv = pq.encode(vectors[0])
check qv.kind == qkProduct
check qv.pqCodes.len == 4
test "Binary quantization":
let v = @[1.0'f32, -1.0'f32, 0.5'f32, -0.5'f32]
let qv = binaryQuantize(v)
check qv.kind == qkBinary
check qv.binData.len == 1
suite "Louvain Community Detection":
test "Detect communities in simple graph":
var g = gengine.newGraph()
# Create two communities
let n1 = gengine.addNode(g, "A")
let n2 = gengine.addNode(g, "B")
let n3 = gengine.addNode(g, "C")
let n4 = gengine.addNode(g, "D")
# Community 1: fully connected
discard gengine.addEdge(g, n1, n2)
discard gengine.addEdge(g, n2, n3)
discard gengine.addEdge(g, n1, n3)
# Community 2
discard gengine.addEdge(g, n3, n4) # single connection
let result = louvain(g)
check result.communities.len > 0
check result.numCommunities >= 1
test "Pattern matching":
var g = gengine.newGraph()
let a = gengine.addNode(g, "Person", {"name": "Alice"}.toTable)
let b = gengine.addNode(g, "Person", {"name": "Bob"}.toTable)
let c = gengine.addNode(g, "Person", {"name": "Charlie"}.toTable)
discard gengine.addEdge(g, a, b, "knows")
discard gengine.addEdge(g, b, c, "knows")
discard gengine.addEdge(g, a, c, "knows")
var pattern = newGraphPattern()
pattern.addNode(0, "Person", {"name": "Alice"}.toTable)
pattern.addNode(1, "Person")
pattern.addEdge(0, 1, "knows")
let matches = matchPattern(g, pattern)
check matches.len >= 1
suite "SSTable Compaction":
test "Create compaction strategy":
var cs = newCompactionStrategy("/tmp/baradb_test_compaction")
check cs.levelCount == 0
check cs.tableCount == 0
test "Add table and check compaction need":
var cs = newCompactionStrategy("/tmp/baradb_test_compaction2")
cs.addTable(SSTableMeta(path: "test.sst", level: 0, minKey: "a", maxKey: "z",
entryCount: 100, sizeBytes: 1024, createdAt: 1))
check cs.tableCount == 1
suite "Page Cache":
test "Cache hit and miss":
var cache = newPageCache(10)
cache.put("key1", cast[seq[byte]]("data1"))
let (found, data) = cache.get("key1")
check found
check cache.hits == 1
let (found2, _) = cache.get("missing")
check not found2
check cache.misses == 1
test "LRU eviction":
var cache = newPageCache(2)
cache.put("a", cast[seq[byte]]("1"))
cache.put("b", cast[seq[byte]]("2"))
cache.put("c", cast[seq[byte]]("3")) # evicts "a"
check cache.len == 2
let (found, _) = cache.get("a")
check not found # evicted
test "Hit rate":
var cache = newPageCache(10)
cache.put("k", cast[seq[byte]]("v"))
discard cache.get("k")
discard cache.get("k")
discard cache.get("miss")
check cache.hitRate - 0.666 < 0.01
suite "Rate Limiter":
test "Token bucket allows requests":
var rl = newRateLimiter(rlaTokenBucket, 1000, 100)
check rl.allowRequest("client1")
check rl.allowRequest("client1")
test "Sliding window rate limiting":
var rl = newRateLimiter(rlaSlidingWindow, 1000, 3)
check rl.allowRequest("client1")
check rl.allowRequest("client1")
check rl.allowRequest("client1")
check not rl.allowRequest("client1") # over limit
test "Remaining quota":
var rl = newRateLimiter(rlaTokenBucket, 1000, 10)
discard rl.allowRequest("c1")
let remaining = rl.remainingQuota("c1")
check remaining >= 0
suite "FTS Fuzzy Search":
test "Levenshtein distance":
check levenshtein("kitten", "sitting") == 3
check levenshtein("", "abc") == 3
check levenshtein("same", "same") == 0
test "Fuzzy search":
var idx = newInvertedIndex()
idx.addDocument(1, "Nim programming language")
idx.addDocument(2, "Python is popular")
let results = idx.fuzzySearch("programing", maxDistance = 2) # typo
check results.len >= 0 # may or may not match
test "Regex search with wildcard":
var idx = newInvertedIndex()
idx.addDocument(1, "fast database engine")
idx.addDocument(2, "slow query optimizer")
let results = idx.regexSearch("fast*")
check results.len >= 0
suite "Vector Metadata Filtering":
test "Search with metadata filter":
var idx = vengine.newHNSWIndex(3)
vengine.insert(idx, 1, @[1.0'f32, 0.0'f32, 0.0'f32],
{"category": "A", "region": "US"}.toTable)
vengine.insert(idx, 2, @[0.9'f32, 0.1'f32, 0.0'f32],
{"category": "B", "region": "EU"}.toTable)
vengine.insert(idx, 3, @[1.0'f32, 0.0'f32, 0.0'f32],
{"category": "A", "region": "EU"}.toTable)
# Filter: only category A
proc filterA(metadata: Table[string, string]): bool =
return metadata.getOrDefault("category", "") == "A"
let results = vengine.searchWithFilter(idx, @[1.0'f32, 0.0'f32, 0.0'f32], 10,
filter = filterA)
check results.len == 2 # only category A entries
suite "BaraQL Parser — Extended":
test "Parse GROUP BY":
let ast = parse("SELECT dept, count(*) FROM employees GROUP BY dept")
check ast.stmts.len == 1
check ast.stmts[0].selGroupBy.len == 1
test "Parse GROUP BY with HAVING":
let ast = parse("SELECT dept, count(*) FROM employees GROUP BY dept HAVING count(*) > 5")
check ast.stmts[0].selHaving != nil
test "Parse ORDER BY with direction":
let ast = parse("SELECT name FROM users ORDER BY age DESC")
check ast.stmts[0].selOrderBy.len == 1
check ast.stmts[0].selOrderBy[0].orderByDir == sdDesc
test "Parse ORDER BY multiple columns":
let ast = parse("SELECT * FROM t ORDER BY a ASC, b DESC")
check ast.stmts[0].selOrderBy.len == 2
test "Parse INNER JOIN":
let ast = parse("SELECT u.name, o.total FROM users u INNER JOIN orders o ON u.id = o.user_id")
check ast.stmts[0].selJoins.len == 1
check ast.stmts[0].selJoins[0].joinKind == jkInner
test "Parse LEFT JOIN":
let ast = parse("SELECT u.name FROM users u LEFT JOIN orders o ON u.id = o.user_id")
check ast.stmts[0].selJoins.len == 1
check ast.stmts[0].selJoins[0].joinKind == jkLeft
test "Parse multiple JOINs":
let ast = parse("SELECT * FROM a JOIN b ON a.id = b.aid JOIN c ON b.id = c.bid")
check ast.stmts[0].selJoins.len == 2
test "Parse CTE (WITH)":
let ast = parse("WITH active AS (SELECT * FROM users WHERE active = true) SELECT * FROM active")
check ast.stmts[0].selWith.len == 1
check ast.stmts[0].selWith[0][0] == "active"
test "Parse multiple CTEs":
let ast = parse("WITH a AS (SELECT id FROM t1), b AS (SELECT id FROM t2) SELECT * FROM a")
check ast.stmts[0].selWith.len == 2
test "Parse aggregate functions in SELECT":
let ast = parse("SELECT count(*), sum(amount), avg(price), min(age), max(score) FROM orders")
check ast.stmts[0].selResult.len == 5
test "Parse CASE expression":
let ast = parse("SELECT CASE WHEN age > 18 THEN 'adult' ELSE 'minor' END FROM users")
check ast.stmts[0].selResult.len == 1
test "Parse BETWEEN":
let ast = parse("SELECT * FROM products WHERE price BETWEEN 10 AND 100")
check ast.stmts[0].selWhere != nil
test "Parse subquery in FROM":
let ast = parse("SELECT * FROM (SELECT id FROM users) AS sub")
check ast.stmts[0].selFrom != nil
test "Parse UPDATE SET WHERE":
let ast = parse("UPDATE users SET name = 'Alice' WHERE id = 1")
check ast.stmts[0].updSet.len == 1
test "Parse DELETE WHERE":
let ast = parse("DELETE FROM users WHERE id = 1")
check ast.stmts[0].delWhere != nil
test "Parse CREATE TYPE with properties":
let ast = parse("CREATE TYPE Person { name: str, age: int32 }")
check ast.stmts[0].ctName == "Person"
check ast.stmts[0].ctProperties.len == 2
suite "Raft Consensus":
test "Create cluster with nodes":
var cluster = newRaftCluster()
cluster.addNode("n1")
cluster.addNode("n2")
cluster.addNode("n3")
check cluster.nodes.len == 3
check cluster.nodes["n1"].peers.len == 2
test "Initial state is follower":
var cluster = newRaftCluster()
cluster.addNode("n1")
check cluster.nodes["n1"].state == rsFollower
test "Election — single node becomes leader":
var cluster = newRaftCluster()
cluster.addNode("n1")
let node = cluster.nodes["n1"]
node.becomeCandidate()
node.becomeLeader()
check node.isLeader
check node.leaderId == "n1"
test "Log replication":
var cluster = newRaftCluster()
cluster.addNode("n1")
let node = cluster.nodes["n1"]
node.becomeCandidate()
node.becomeLeader()
let entry = node.appendLog("SET key1 value1")
check entry.index == 1
check node.logLen == 1
let entry2 = node.appendLog("SET key2 value2")
check entry2.index == 2
check node.logLen == 2
test "RequestVote handling":
var cluster = newRaftCluster()
cluster.addNode("n1")
cluster.addNode("n2")
let n1 = cluster.nodes["n1"]
let n2 = cluster.nodes["n2"]
let req = RaftMessage(kind: rmkRequestVote, term: 1, senderId: "n2",
lastLogIndex: 0, lastLogTerm: 0)
let reply = n1.handleRequestVote(req)
check reply.success
check n1.votedFor == "n2"
test "AppendEntries handling":
var cluster = newRaftCluster()
cluster.addNode("n1")
cluster.addNode("n2")
let n2 = cluster.nodes["n2"]
let msg = RaftMessage(kind: rmkAppendEntries, term: 1, senderId: "n1",
prevLogIndex: 0, prevLogTerm: 0,
entries: @[LogEntry(term: 1, index: 1, command: "SET x 1")],
leaderCommit: 0)
let reply = n2.handleAppendEntries(msg)
check reply.success
check n2.logLen == 1
suite "Sharding":
test "Hash-based sharding":
var router = newShardRouter(ShardConfig(numShards: 4, strategy: ssHash))
check router.shardCount == 4
let s1 = router.getShard("user_1")
let s2 = router.getShard("user_2")
check s1 >= 0 and s1 < 4
check s2 >= 0 and s2 < 4
test "Consistent hashing":
var router = newShardRouter(ShardConfig(numShards: 4, strategy: ssConsistent))
router.addVirtualNodes(50)
let s = router.getShard("some_key")
check s >= 0 and s < 4
test "Range-based sharding":
var router = newShardRouter(ShardConfig(numShards: 3, strategy: ssRange))
router.setRangeBounds(@[("a", "f"), ("g", "n"), ("o", "z")])
check router.getShardRange("apple") == 0
check router.getShardRange("hello") == 1
check router.getShardRange("top") == 2
test "Rebalance assigns nodes":
var router = newShardRouter(ShardConfig(numShards: 3, replicas: 2, strategy: ssHash))
router.rebalance(@["node1", "node2", "node3"])
for shard in router.shards:
check shard.nodeIds.len == 2 # 2 replicas
test "Replicas of key":
var router = newShardRouter(ShardConfig(numShards: 2, replicas: 1, strategy: ssHash))
router.rebalance(@["n1", "n2"])
let replicas = router.replicasOf("test_key")
check replicas.len == 1
test "Active shard count":
var router = newShardRouter()
check router.activeShardCount == 4
suite "Schema Inheritance":
test "Inheritance — merge properties from base":
var s = newSchema()
let base = newType("Base")
base.addProperty("id", "str", required = true)
base.addProperty("created", "datetime")
s.addType("default", base)
let child = newType("Person")
child.setBases(@["Base"])
child.addProperty("name", "str", required = true)
s.addType("default", child)
let resolved = s.resolveInheritance(child)
check resolved.properties.len == 3 # id + created + name
check "id" in resolved.properties
check "name" in resolved.properties
check "created" in resolved.properties
test "Multi-level inheritance":
var s = newSchema()
let a = newType("A")
a.addProperty("a1", "str")
s.addType("default", a)
let b = newType("B")
b.setBases(@["A"])
b.addProperty("b1", "int32")
s.addType("default", b)
let c = newType("C")
c.setBases(@["B"])
c.addProperty("c1", "bool")
s.addType("default", c)
let resolved = s.resolveInheritance(c)
check resolved.properties.len == 3 # a1 + b1 + c1
test "Override base property":
var s = newSchema()
let base = newType("Base")
base.addProperty("name", "str")
s.addType("default", base)
let child = newType("Child")
child.setBases(@["Base"])
child.addProperty("name", "text") # override
s.addType("default", child)
let resolved = s.resolveInheritance(child)
check resolved.properties["name"].typeName == "text"
test "isSubtype":
var s = newSchema()
let a = newType("A")
s.addType("default", a)
let b = newType("B")
b.setBases(@["A"])
s.addType("default", b)
let c = newType("C")
c.setBases(@["B"])
s.addType("default", c)
check s.isSubtype("C", "A")
check s.isSubtype("C", "B")
check s.isSubtype("B", "A")
check not s.isSubtype("A", "C")
test "Computed property":
let t = newType("Person")
t.addProperty("firstName", "str")
t.addProperty("lastName", "str")
t.addComputedProperty("fullName", "str", "firstName ++ ' ' ++ lastName")
check t.properties["fullName"].computed
check t.properties["fullName"].expr == "firstName ++ ' ' ++ lastName"
suite "Codegen":
test "Codegen scan":
let plan = IRPlan(kind: irpkScan, scanTable: "users", scanAlias: "u")
let op = codegenPlan(plan)
check op.kind == sokScan
check op.table == "users"
test "Codegen filter with point read optimization":
let filterExpr = IRExpr(kind: irekBinary, binOp: irEq,
binLeft: IRExpr(kind: irekField, fieldPath: @["id"]),
binRight: IRExpr(kind: irekLiteral, literal: IRLiteral(kind: vkInt64, int64Val: 42)))
let scan = IRPlan(kind: irpkScan, scanTable: "users", scanAlias: "u")
let plan = IRPlan(kind: irpkFilter, filterSource: scan, filterCond: filterExpr)
let op = codegenPlan(plan)
# Should optimize to point read
check op.kind == sokPointRead
test "Codegen limit":
let scan = IRPlan(kind: irpkScan, scanTable: "t", scanAlias: "t")
let plan = IRPlan(kind: irpkLimit, limitSource: scan, limitCount: 10, limitOffset: 5)
let op = codegenPlan(plan)
check op.limit == 10
check op.offset == 5
test "Cost estimation":
let scan = newStorageOp(sokScan)
check estimateCost(scan) == 1000.0
let pointRead = newStorageOp(sokPointRead)
check estimateCost(pointRead) == 1.0
test "Explain plan":
let scan = newStorageOp(sokScan)
scan.table = "users"
let explanation = scan.explain()
check "sokScan" in explanation
check "users" in explanation
suite "Replication":
test "Create replication manager":
var rm = newReplicationManager(rmAsync)
check rm.totalReplicaCount == 0
check rm.connectedReplicaCount == 0
test "Add and connect replicas":
var rm = newReplicationManager(rmAsync)
rm.addReplica(newReplica("r1", "10.0.0.1", 5432))
rm.addReplica(newReplica("r2", "10.0.0.2", 5432))
check rm.totalReplicaCount == 2
rm.connectReplica("r1")
check rm.connectedReplicaCount == 1
test "Async replication — write returns immediately":
var rm = newReplicationManager(rmAsync)
rm.addReplica(newReplica("r1", "10.0.0.1", 5432))
rm.connectReplica("r1")
let lsn = rm.writeLsn(@[1'u8, 2, 3])
check lsn == 1
# Async doesn't wait — already "acked"
check rm.isFullyAcked(lsn)
test "Sync replication — wait for ack":
var rm = newReplicationManager(rmSync)
rm.addReplica(newReplica("r1", "10.0.0.1", 5432))
rm.connectReplica("r1")
let lsn = rm.writeLsn(@[1'u8, 2, 3])
check not rm.isFullyAcked(lsn)
rm.ackLsn("r1", lsn)
check rm.isFullyAcked(lsn)
test "Semi-sync replication":
var rm = newReplicationManager(rmSemiSync, syncCount = 2)
rm.addReplica(newReplica("r1", "10.0.0.1", 5432))
rm.addReplica(newReplica("r2", "10.0.0.2", 5432))
rm.addReplica(newReplica("r3", "10.0.0.3", 5432))
rm.connectReplica("r1")
rm.connectReplica("r2")
rm.connectReplica("r3")
let lsn = rm.writeLsn(@[1'u8])
check not rm.isFullyAcked(lsn) # needs 2 acks
rm.ackLsn("r1", lsn)
check not rm.isFullyAcked(lsn) # still needs 1 more
rm.ackLsn("r2", lsn)
check rm.isFullyAcked(lsn) # 2 acks received
test "Replica status":
var rm = newReplicationManager(rmAsync)
rm.addReplica(newReplica("r1", "10.0.0.1", 5432))
rm.connectReplica("r1")
let status = rm.replicaStatus()
check status.len == 1
check status[0][1] == rsStreaming
suite "User Defined Functions":
test "Register and call UDF":
var reg = newUDFRegistry()
reg.register("double", @[UDFParam(name: "x", typeName: "int64", required: true)],
"int64", proc(args: seq[Value]): Value =
if args.len > 0 and args[0].kind == vkInt64:
return Value(kind: vkInt64, int64Val: args[0].int64Val * 2)
return Value(kind: vkNull))
check reg.hasFunction("double")
let result = reg.call("double", @[Value(kind: vkInt64, int64Val: 21)])
check result.kind == vkInt64
check result.int64Val == 42
test "Register expression-based UDF":
var reg = newUDFRegistry()
reg.registerExpr("greet", @[UDFParam(name: "name", typeName: "str")],
"str", "'Hello ' ++ name")
check reg.hasFunction("greet")
check reg.getFunction("greet").expr == "'Hello ' ++ name"
test "Standard library functions":
var reg = newUDFRegistry()
reg.registerStdlib()
# lower
let r1 = reg.call("lower", @[Value(kind: vkString, strVal: "HELLO")])
check r1.strVal == "hello"
# upper
let r2 = reg.call("upper", @[Value(kind: vkString, strVal: "hello")])
check r2.strVal == "HELLO"
# len
let r3 = reg.call("len", @[Value(kind: vkString, strVal: "test")])
check r3.int64Val == 4
# trim
let r4 = reg.call("trim", @[Value(kind: vkString, strVal: " hello ")])
check r4.strVal == "hello"
# toString
let r5 = reg.call("toString", @[Value(kind: vkInt64, int64Val: 42)])
check r5.strVal == "42"
test "Deregister function":
var reg = newUDFRegistry()
reg.register("temp", @[], "int64", proc(args: seq[Value]): Value = Value(kind: vkNull))
check reg.hasFunction("temp")
reg.deregister("temp")
check not reg.hasFunction("temp")
test "Function count":
var reg = newUDFRegistry()
reg.registerStdlib()
check reg.functionCount > 10
suite "Vector SIMD":
test "Dot product":
let a = @[1.0'f32, 2.0'f32, 3.0'f32]
let b = @[4.0'f32, 5.0'f32, 6.0'f32]
let result = dotProductSimd(a, b)
check abs(result - 32.0) < 0.001
test "L2 distance":
let a = @[0.0'f32, 0.0'f32]
let b = @[3.0'f32, 4.0'f32]
let result = l2NormSimd(a, b)
check abs(result - 5.0) < 0.001
test "Cosine distance":
let a = @[1.0'f32, 0.0'f32, 0.0'f32]
let b = @[0.0'f32, 1.0'f32, 0.0'f32]
let result = cosineSimd(a, b)
check abs(result - 1.0) < 0.001 # orthogonal = 1.0
let c = @[1.0'f32, 0.0'f32, 0.0'f32]
let d = @[1.0'f32, 0.0'f32, 0.0'f32]
check cosineSimd(c, d) < 0.001 # same direction = 0.0
test "Manhattan distance":
let a = @[1.0'f32, 2.0'f32]
let b = @[4.0'f32, 6.0'f32]
let result = manhattanSimd(a, b)
check abs(result - 7.0) < 0.001
test "Normalize vector":
let v = @[3.0'f32, 4.0'f32]
let n = normalize(v)
check abs(n[0] - 0.6) < 0.001
check abs(n[1] - 0.8) < 0.001
test "Add vectors":
let a = @[1.0'f32, 2.0'f32]
let b = @[3.0'f32, 4.0'f32]
let c = addVectors(a, b)
check c[0] == 4.0
check c[1] == 6.0
test "Scale vector":
let v = @[1.0'f32, 2.0'f32, 3.0'f32]
let s = scaleVector(v, 2.0)
check s[0] == 2.0
check s[1] == 4.0
check s[2] == 6.0
test "TopK":
let distances = @[5.0'f32, 1.0'f32, 3.0'f32, 2.0'f32, 4.0'f32]
let top = topK(distances, 3)
check top.len == 3
check top[0][0] == 1 # index 1, value 1.0
check top[1][0] == 3 # index 3, value 2.0
check top[2][0] == 2 # index 2, value 3.0
test "Batch distance":
let queries = @[@[1.0'f32, 0.0'f32], @[0.0'f32, 1.0'f32]]
let corpus = @[@[1.0'f32, 0.0'f32], @[0.0'f32, 1.0'f32], @[1.0'f32, 1.0'f32]]
let results = batchDistance(queries, corpus, "cosine")
check results.len == 2
check results[0].len == 3