Files
Baradb/tests/test_all.nim
T

334 lines
14 KiB
Nim

## BaraDB — Test Suite
import std/unittest
import std/tables
import std/strutils
import std/os
import std/asyncdispatch
import std/times
import std/random
import std/monotimes
import std/base64
import barabadb/core/types
import barabadb/core/mvcc
import barabadb/core/deadlock
import barabadb/core/config
import barabadb/core/server
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/core/crossmodal
import barabadb/core/gossip
import barabadb/client/client
import barabadb/client/fileops
import barabadb/fts/multilang as mlang
import barabadb/protocol/zerocopy
import barabadb/query/adaptive
import barabadb/query/executor as qexec
import barabadb/core/disttxn
import barabadb/vector/engine as vengine
import barabadb/graph/cypher
import barabadb/vector/quant as vquant
import barabadb/storage/recovery
import barabadb/cli/shell
import barabadb/protocol/ssl
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/scram
import barabadb/protocol/ratelimit
import barabadb/schema/schema as schema
suite "Enhanced Migrations":
test "Parse CREATE MIGRATION with UP/DOWN":
let ast = parse("CREATE MIGRATION add_users { UP: CREATE TABLE users (id INTEGER PRIMARY KEY); DOWN: DROP TABLE users; }")
check ast.stmts.len == 1
check ast.stmts[0].kind == nkCreateMigration
check ast.stmts[0].cmName == "add_users"
check ast.stmts[0].cmBody.contains("CREATE TABLE users")
check ast.stmts[0].cmDownBody.contains("DROP TABLE users")
test "Parse MIGRATION STATUS":
let ast = parse("MIGRATION STATUS")
check ast.stmts[0].kind == nkMigrationStatus
test "Parse MIGRATION UP":
let ast = parse("MIGRATION UP")
check ast.stmts[0].kind == nkMigrationUp
check ast.stmts[0].muCount == 0
test "Parse MIGRATION UP 5":
let ast = parse("MIGRATION UP 5")
check ast.stmts[0].kind == nkMigrationUp
check ast.stmts[0].muCount == 5
test "Parse MIGRATION DOWN":
let ast = parse("MIGRATION DOWN")
check ast.stmts[0].kind == nkMigrationDown
check ast.stmts[0].mdCount == 1
test "Parse MIGRATION DOWN 3":
let ast = parse("MIGRATION DOWN 3")
check ast.stmts[0].kind == nkMigrationDown
check ast.stmts[0].mdCount == 3
test "Parse MIGRATION DRYRUN":
let ast = parse("MIGRATION DRYRUN add_users")
check ast.stmts[0].kind == nkMigrationDryRun
check ast.stmts[0].mdrName == "add_users"
test "Create and apply migration with checksum":
var testDir = getTempDir() / "baradb_migration_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
createDir(testDir)
var db = newLSMTree(testDir)
var ctx = qexec.newExecutionContext(db)
# Create migration
let createRes = qexec.executeQuery(ctx, parse("CREATE MIGRATION add_users { UP: CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT); DOWN: DROP TABLE users; }"))
check createRes.success
check createRes.message.contains("checksum")
# Apply migration
let applyRes = qexec.executeQuery(ctx, parse("APPLY MIGRATION add_users"))
check applyRes.success
check applyRes.message.contains("ms")
# Check table exists
let tableRes = qexec.executeQuery(ctx, parse("SELECT name FROM users"))
check tableRes.success # table exists (empty result is OK)
# Re-apply should be idempotent
let reapplyRes = qexec.executeQuery(ctx, parse("APPLY MIGRATION add_users"))
check reapplyRes.success
check reapplyRes.message.contains("already applied")
test "Migration STATUS shows applied migrations":
var testDir = getTempDir() / "baradb_migration_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
createDir(testDir)
var db = newLSMTree(testDir)
var ctx = qexec.newExecutionContext(db)
discard qexec.executeQuery(ctx, parse("CREATE MIGRATION m1 { UP: CREATE TABLE t1 (id INTEGER); }"))
discard qexec.executeQuery(ctx, parse("CREATE MIGRATION m2 { UP: CREATE TABLE t2 (id INTEGER); }"))
discard qexec.executeQuery(ctx, parse("APPLY MIGRATION m1"))
let statusRes = qexec.executeQuery(ctx, parse("MIGRATION STATUS"))
check statusRes.success
check statusRes.rows.len == 2
check statusRes.rows[0]["status"] == "applied"
check statusRes.rows[1]["status"] == "pending"
test "Migration UP applies all pending":
var testDir = getTempDir() / "baradb_migration_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
createDir(testDir)
var db = newLSMTree(testDir)
var ctx = qexec.newExecutionContext(db)
discard qexec.executeQuery(ctx, parse("CREATE MIGRATION m1 { UP: CREATE TABLE t1 (id INTEGER); }"))
discard qexec.executeQuery(ctx, parse("CREATE MIGRATION m2 { UP: CREATE TABLE t2 (id INTEGER); }"))
let upRes = qexec.executeQuery(ctx, parse("MIGRATION UP"))
check upRes.success
check upRes.message.contains("Applied 2 migrations")
test "Migration DOWN rollback":
var testDir = getTempDir() / "baradb_migration_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
createDir(testDir)
var db = newLSMTree(testDir)
var ctx = qexec.newExecutionContext(db)
discard qexec.executeQuery(ctx, parse("CREATE MIGRATION add_t { UP: CREATE TABLE t (id INTEGER); DOWN: DROP TABLE t; }"))
discard qexec.executeQuery(ctx, parse("APPLY MIGRATION add_t"))
let downRes = qexec.executeQuery(ctx, parse("MIGRATION DOWN"))
check downRes.success
check downRes.message.contains("Rolled back 1 migrations")
# After rollback, table should be gone (check by listing tables)
let tableRes = qexec.executeQuery(ctx, parse("SELECT name FROM __tables WHERE name = 't'"))
check tableRes.success
check tableRes.rows.len == 0 # table does not exist
test "Migration DRYRUN":
var testDir = getTempDir() / "baradb_migration_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
createDir(testDir)
var db = newLSMTree(testDir)
var ctx = qexec.newExecutionContext(db)
discard qexec.executeQuery(ctx, parse("CREATE MIGRATION add_t { UP: CREATE TABLE t (id INTEGER); CREATE INDEX idx ON t(id); DOWN: DROP TABLE t; }"))
let dryRes = qexec.executeQuery(ctx, parse("MIGRATION DRYRUN add_t"))
check dryRes.success
check dryRes.message.contains("DRY RUN")
check dryRes.message.contains("Statements: 2")
check dryRes.message.contains("DOWN script: yes")
suite "Parameterized queries":
var db: LSMTree
var ctx: qexec.ExecutionContext
setup:
db = newLSMTree("")
ctx = qexec.newExecutionContext(db)
discard qexec.executeQuery(ctx, parse("CREATE TABLE users (id INT, name TEXT, age INT)"))
discard qexec.executeQuery(ctx, parse("INSERT INTO users (id, name, age) VALUES (1, 'Alice', 30)"))
discard qexec.executeQuery(ctx, parse("INSERT INTO users (id, name, age) VALUES (2, 'Bob', 25)"))
test "SELECT with placeholder params":
let sql = "SELECT * FROM users WHERE id = ?"
let tokens = lex.tokenize(sql)
let ast = parse(tokens)
let params = @[WireValue(kind: fkInt64, int64Val: 1)]
let r = qexec.executeQuery(ctx, ast, params)
check r.success
check r.rows.len == 1
check r.rows[0]["name"] == "Alice"
test "INSERT with placeholder params":
let sql = "INSERT INTO users (id, name, age) VALUES (?, ?, ?)"
let tokens = lex.tokenize(sql)
let ast = parse(tokens)
let params = @[
WireValue(kind: fkInt64, int64Val: 3),
WireValue(kind: fkString, strVal: "Charlie"),
WireValue(kind: fkInt64, int64Val: 35)
]
let r = qexec.executeQuery(ctx, ast, params)
check r.success
let selectR = qexec.executeQuery(ctx, parse("SELECT * FROM users WHERE id = 3"))
check selectR.rows.len == 1
check selectR.rows[0]["name"] == "Charlie"
test "SELECT with multiple placeholders":
let sql = "SELECT * FROM users WHERE age > ? AND name = ?"
let tokens = lex.tokenize(sql)
let ast = parse(tokens)
let params = @[WireValue(kind: fkInt64, int64Val: 25), WireValue(kind: fkString, strVal: "Alice")]
let r = qexec.executeQuery(ctx, ast, params)
check r.success
check r.rows.len == 1
check r.rows[0]["name"] == "Alice"
test "JSON type validation":
let createTbl = parse("CREATE TABLE json_test (id INT PRIMARY KEY, data JSON)")
discard qexec.executeQuery(ctx, createTbl)
let valid = parse("INSERT INTO json_test (id, data) VALUES (1, '{\"key\": \"value\"}')")
let r1 = qexec.executeQuery(ctx, valid)
check r1.success
let invalid = parse("INSERT INTO json_test (id, data) VALUES (2, 'not json')")
let r2 = qexec.executeQuery(ctx, invalid)
check not r2.success
check r2.message.contains("JSON")
test "Multi-column index parse and create":
let ast = parse("CREATE INDEX idx_mc ON users (name, age)")
check ast.stmts[0].kind == nkCreateIndex
check ast.stmts[0].ciColumns.len == 2
check ast.stmts[0].ciColumns[0] == "name"
check ast.stmts[0].ciColumns[1] == "age"
let r = qexec.executeQuery(ctx, ast)
check r.success
check r.message.contains("CREATE INDEX")
test "CTE non-recursive execution":
let ast = parse("WITH active AS (SELECT * FROM users WHERE active = true) SELECT * FROM active")
let r = qexec.executeQuery(ctx, ast)
check r.success
check r.rows.len >= 1
test "CTE recursive parse":
let ast = parse("WITH RECURSIVE nums AS (SELECT 1 AS n) SELECT * FROM nums")
check ast.stmts[0].selWith.len == 1
check ast.stmts[0].selWith[0][0] == "nums"
check ast.stmts[0].selWith[0][2] == true
test "UNION ALL parse":
let ast = parse("SELECT 1 AS n UNION ALL SELECT 2 AS n")
check ast.stmts[0].kind == nkSetOp
check ast.stmts[0].setOpKind == sdkUnion
check ast.stmts[0].setOpAll == true
check ast.stmts[0].setOpLeft.kind == nkSelect
check ast.stmts[0].setOpRight.kind == nkSelect
test "UNION ALL execution":
discard qexec.executeQuery(ctx, parse("INSERT INTO users (name, age, active) VALUES ('union_a', '30', 'true')"))
discard qexec.executeQuery(ctx, parse("INSERT INTO users (name, age, active) VALUES ('union_b', '25', 'false')"))
let ast = parse("SELECT name FROM users WHERE name = 'union_a' UNION ALL SELECT name FROM users WHERE name = 'union_b'")
let r = qexec.executeQuery(ctx, ast)
check r.success
check r.rows.len == 2
test "Simple recursive CTE execution":
let ast = parse("WITH RECURSIVE nums AS (SELECT 0 AS n FROM users LIMIT 1 UNION ALL SELECT n + 1 FROM nums WHERE n < 2) SELECT n FROM nums ORDER BY n ASC")
let r = qexec.executeQuery(ctx, ast)
check r.success
test "DROP INDEX parse":
let ast = parse("DROP INDEX myidx")
check ast.stmts[0].kind == nkDropIndex
check ast.stmts[0].diName == "myidx"
test "DROP INDEX execution":
let tbl = ctx.tables["users"]
let colKey = "users.name"
ctx.btrees[colKey] = newBTreeIndex[string, IndexEntry]()
let dropAst = parse("DROP INDEX users.name")
let r = qexec.executeQuery(ctx, dropAst)
check r.success
test "JSON path operators parse":
let ast = parse("SELECT data->'name' FROM users")
check ast.stmts[0].kind == nkSelect
test "JSON path operator ->> parse":
let ast = parse("SELECT data->>'name' FROM users")
check ast.stmts[0].kind == nkSelect
test "JSON path execution":
discard qexec.executeQuery(ctx, parse("CREATE TABLE IF NOT EXISTS jsontest (id INT PRIMARY KEY, data JSON)"))
discard qexec.executeQuery(ctx, parse("INSERT INTO jsontest (id, data) VALUES (1, '{\"name\": \"Alice\", \"age\": 30}')"))
let r = qexec.executeQuery(ctx, parse("SELECT data->'name' AS json_name, data->>'name' AS text_name FROM jsontest"))
check r.success
check r.rows.len >= 1
test "FTS match operator @@ parse":
let ast = parse("SELECT * FROM docs WHERE content @@ 'hello'")
check ast.stmts[0].kind == nkSelect
test "FTS match operator @@ execution":
discard qexec.executeQuery(ctx, parse("INSERT INTO users (name, age, active) VALUES ('full text search', '30', 'true')"))
let r = qexec.executeQuery(ctx, parse("SELECT name FROM users WHERE name @@ 'text'"))
check r.success
# Should find the row because 'text' is in 'full text search'
test "RECOVER TO TIMESTAMP parse":
let ast = parse("RECOVER TO TIMESTAMP '2026-05-07T12:00:00'")
check ast.stmts[0].kind == nkRecoverToTimestamp
test "RECOVER FROM WAL execution":
let r = qexec.executeQuery(ctx, parse("RECOVER TO TIMESTAMP '2026-12-31T23:59:59'"))
check r.success
test "FTS index creation USING FTS":
discard qexec.executeQuery(ctx, parse("CREATE TABLE IF NOT EXISTS fts_test (id INT PRIMARY KEY, body TEXT)"))
discard qexec.executeQuery(ctx, parse("INSERT INTO fts_test (id, body) VALUES (1, 'the quick brown fox jumps')"))
discard qexec.executeQuery(ctx, parse("INSERT INTO fts_test (id, body) VALUES (2, 'lazy dog sleeps all day')"))
discard qexec.executeQuery(ctx, parse("INSERT INTO fts_test (id, body) VALUES (3, 'quick brown dog plays fetch')"))
let r = qexec.executeQuery(ctx, parse("CREATE INDEX idx_fts_body ON fts_test(body) USING FTS"))
check r.success
check r.message.contains("USING FTS")
test "FTS index @@ uses BM25":
let r = qexec.executeQuery(ctx, parse("SELECT id FROM fts_test WHERE body @@ 'quick brown'"))
check r.success
check r.rows.len >= 2
# JOIN tests
include "join_tests"
# TLA+ faithfulness tests
include "tla_faithfulness"