From 4e7e5685252a5249b88940a62431c4377057b1e2 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Wed, 13 May 2026 15:05:59 +0300 Subject: [PATCH] =?UTF-8?q?release:=20v1.1.0=20=E2=80=94=20bug=20fixes,=20?= =?UTF-8?q?Docker,=20clients,=20SCRAM=20auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump version to 1.1.0 across all components - Remove debug CI artifacts (debug.yml, test_all.nim.bak, test_lock) - Update CHANGELOG with v1.1.0 release notes - Bug fixes: irNeg, distributed txn, sharding, Raft majority, MVCC, LSM-Tree, auth (JWT + SCRAM-SHA-256), wire protocol, SQL injection, ReDoS, graph, vector, FTS, build warnings - Client SDKs: JS/TS, Python, Nim, Rust with tests and examples - Docker: production + source + test compose configs - TLA+: crossmodal, backup, recovery specs with symmetry reduction --- .github/workflows/debug.yml | 59 - Dockerfile | 2 +- Dockerfile.source | 2 +- README.md | 2 +- baradadb.nimble | 2 +- clients/javascript/package.json | 2 +- clients/nim/baradb.nimble | 2 +- clients/python/pyproject.toml | 2 +- clients/rust/Cargo.toml | 2 +- docs/en/changelog.md | 39 + src/barabadb/cli/shell.nim | 2 +- src/barabadb/core/httpserver.nim | 4 +- src/barabadb/core/tracing.nim | 2 +- src/baradadb.nim | 2 +- tests/test_all.nim.bak | 2571 ------------------------------ tests/test_lock | Bin 396512 -> 0 bytes 16 files changed, 52 insertions(+), 2643 deletions(-) delete mode 100644 .github/workflows/debug.yml delete mode 100644 tests/test_all.nim.bak delete mode 100755 tests/test_lock diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml deleted file mode 100644 index edc8a55..0000000 --- a/.github/workflows/debug.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Debug Tests - -on: - push: - branches: [main] - -jobs: - debug: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Setup Nim - uses: jiro4989/setup-nim-action@v1 - with: - nim-version: '2.2.10' - - - name: Install system dependencies - run: sudo apt-get update -qq && sudo apt-get install -y -qq libssl-dev libpcre3-dev openssl ca-certificates - - - name: Install Nim dependencies - run: nimble install --depsOnly -y - - - name: Check nim binary - run: | - which nim - nim --version - echo "NIM_OK=1" - - - name: Compile and run lock test - run: nim c --threads:on --path:src -r tests/test_lock.nim - - - name: Run join_tests - run: timeout 30 nim c -d:ssl --threads:on --path:src -r tests/join_tests.nim - - - name: Run tla_faithfulness - run: nim c -d:ssl --threads:on --path:src -r tests/tla_faithfulness.nim - - - name: Compile test_all - run: nim c -d:ssl --threads:on --path:src tests/test_all.nim - - - name: Run test_all binary - run: ./tests/test_all > test_all.log 2>&1 - - - name: Upload test_all log to repo - if: failure() - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - URL=$(curl -s -F'file=@test_all.log' https://0x0.st) - echo "::notice::test_all.log uploaded to $URL" - sudo apt-get install -y -qq git - git config user.name "CI Bot" - git config user.email "ci@baradb" - git checkout -b ci-logs-${{ github.run_id }} || true - cp test_all.log ci_log.txt - git add ci_log.txt - git commit -m "CI log for run ${{ github.run_id }}" || true - git push https://$GITHUB_TOKEN@github.com/${{ github.repository }}.git ci-logs-${{ github.run_id }} || echo "PUSH_FAILED=1" diff --git a/Dockerfile b/Dockerfile index 4fa1f4b..aec3e9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ FROM debian:bookworm-slim LABEL maintainer="BaraDB Team" LABEL description="BaraDB — Multimodal Database Engine" -LABEL version="1.0.0" +LABEL version="1.1.0" # Инсталираме runtime зависимости # libpcre3 — нужна за Nim regex (зарежда се динамично) diff --git a/Dockerfile.source b/Dockerfile.source index 40fa9bb..96603a1 100644 --- a/Dockerfile.source +++ b/Dockerfile.source @@ -40,7 +40,7 @@ FROM alpine:3.19 LABEL maintainer="BaraDB Team" LABEL description="BaraDB — Multimodal Database Engine (source build)" -LABEL version="1.0.0" +LABEL version="1.1.0" # Инсталираме runtime зависимости RUN apk add --no-cache ca-certificates su-exec wget pcre diff --git a/README.md b/README.md index 86582f6..1ad8403 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **A multimodal database engine written in Nim — 100% native, zero dependencies.** -[![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](baradadb.nimble) +[![Version](https://img.shields.io/badge/version-1.1.0-blue.svg)](baradadb.nimble) [![Documentation](https://img.shields.io/badge/docs-7_languages-blue.svg)](docs/index.md) ## Documentation diff --git a/baradadb.nimble b/baradadb.nimble index 9c53dce..8f2b601 100644 --- a/baradadb.nimble +++ b/baradadb.nimble @@ -1,5 +1,5 @@ # Package -version = "1.0.0" +version = "1.1.0" author = "BaraDB Team" description = "BaraDB — Multimodal database written in Nim" license = "Apache-2.0" diff --git a/clients/javascript/package.json b/clients/javascript/package.json index 4ca5ab3..25b1a45 100644 --- a/clients/javascript/package.json +++ b/clients/javascript/package.json @@ -1,6 +1,6 @@ { "name": "baradb", - "version": "1.0.0", + "version": "1.1.0", "description": "Official JavaScript/Node.js client for BaraDB — Multimodal Database Engine", "main": "baradb.js", "types": "baradb.d.ts", diff --git a/clients/nim/baradb.nimble b/clients/nim/baradb.nimble index f2aa1f0..5701aca 100644 --- a/clients/nim/baradb.nimble +++ b/clients/nim/baradb.nimble @@ -1,6 +1,6 @@ # Package -version = "1.0.0" +version = "1.1.0" author = "BaraDB Team" description = "Official Nim client for BaraDB — async binary protocol client" license = "Apache-2.0" diff --git a/clients/python/pyproject.toml b/clients/python/pyproject.toml index c981b17..9c84c3a 100644 --- a/clients/python/pyproject.toml +++ b/clients/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "baradb" -version = "1.0.0" +version = "1.1.0" description = "Official Python client for BaraDB — Multimodal Database Engine" readme = "README.md" license = { text = "Apache-2.0" } diff --git a/clients/rust/Cargo.toml b/clients/rust/Cargo.toml index 75c866f..e4a3324 100644 --- a/clients/rust/Cargo.toml +++ b/clients/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "baradb" -version = "1.0.0" +version = "1.1.0" edition = "2021" authors = ["BaraDB Team "] description = "Official Rust client for BaraDB — binary protocol client" diff --git a/docs/en/changelog.md b/docs/en/changelog.md index dce1463..a60ec2d 100644 --- a/docs/en/changelog.md +++ b/docs/en/changelog.md @@ -2,6 +2,45 @@ All notable changes to BaraDB are documented in this file. +## [1.1.0] — 2026-05-13 + +### Added + +- **Client SDKs v1.1.0** — Full-featured clients for all languages: + - JavaScript: TypeScript definitions, package.json, examples, unit & integration tests + - Python: Restructured as proper package (`baradb/` with `__init__.py` and `core.py`), pyproject.toml, examples, tests (query builder, wire protocol, integration) + - Nim: Examples, integration tests, README + - Rust: Examples, integration tests, improved Cargo.toml +- **SCRAM-SHA-256 Authentication** — RFC 7677 compliant authentication with PBKDF2 + HMAC + SHA-256 + nonce/salt generation +- **HTTP SCRAM Endpoints** — `/auth/scram/start` + `/auth/scram/finish` in HTTP server +- **Docker Compose Test Configuration** — `docker-compose.test.yml` for test environments +- **CI/CD Clients Pipeline** — `.github/workflows/clients-ci.yml` for automated client testing + +### Fixed + +- **Query Executor** — Unary minus (`irNeg`) evaluation now works correctly in SELECT and WHERE clauses +- **Distributed Transactions** — Rollback after commit attempt no longer violates atomicity +- **Sharding** — Data migration protocol with TCP + `scanAll` on LSM +- **Raft** — Majority calculation for even number of nodes fixed +- **MVCC** — Aborted transactions no longer become visible +- **LSM-Tree** — Data loss on immutable memtable overwrite fixed; SSTable lookup sorting fixed +- **Auth** — JWT signature changed to HMAC-SHA256 (no longer trivially forgeable); token expiration (`exp`/`nbf`/`iat`) now validated; signature comparison is now constant-time +- **Recovery** — `summary()` no longer mutates the database +- **Wire Protocol** — 64MB limit + bounds checking + max depth to prevent OOM/DoS +- **SQL Injection** — `exprToSql` now escapes single quotes +- **ReDoS** — `irLike`/`irILike` now escape regex metacharacters +- **Graph** — `addEdge` now checks node existence +- **Vector** — Dimension mismatch validation + HNSW locking +- **FTS** — UTF-8 tokenization now uses runes instead of bytes +- **Build** — `nim.cfg` adds `-d:ssl` so `nimble build` works without flags; `--threads:on` added to all CI commands + +### Changed + +- **Version bumped to 1.1.0** across all components (server, Docker images, clients, CLI) +- **README** — Version badge updated; all feature tables now reference v1.1.0 +- **TLA+ Formal Verification** — Added `crossmodal.tla`, `backup.tla`, `recovery.tla`; symmetry reduction in all 9 specs +- **Clean build** — 0 compiler warnings on Nim 2.2.10 + ## [0.1.0] — 2025-01-15 ### Added diff --git a/src/barabadb/cli/shell.nim b/src/barabadb/cli/shell.nim index 88d7698..128a160 100644 --- a/src/barabadb/cli/shell.nim +++ b/src/barabadb/cli/shell.nim @@ -5,7 +5,7 @@ import ../query/lexer import ../query/parser const - Version = "0.1.0" + Version = "1.1.0" Prompt = "bara> " ContinuationPrompt = " .. > " diff --git a/src/barabadb/core/httpserver.nim b/src/barabadb/core/httpserver.nim index f6e8711..9f89e56 100644 --- a/src/barabadb/core/httpserver.nim +++ b/src/barabadb/core/httpserver.nim @@ -178,7 +178,7 @@ proc queryHandler(server: HttpServer): RequestHandler = proc healthHandler(): RequestHandler = return proc(request: Request) {.gcsafe.} = let ctx = newContext(request) - ctx.json(%*{"status": "ok", "version": "0.1.0"}) + ctx.json(%*{"status": "ok", "version": "1.1.0"}) proc metricsHandler(server: HttpServer): RequestHandler = return proc(request: Request) {.gcsafe.} = @@ -514,7 +514,7 @@ function showTab(idx){ } setInterval(() => { if(document.querySelectorAll('.panel')[4].classList.contains('active')) loadMetrics() }, 5000) -
BaraDB v1.0.0 — Multimodal Database Engine
+
BaraDB v1.1.0 — Multimodal Database Engine
""" request.respond(200, @[("Content-Type", "text/html; charset=utf-8")], html) diff --git a/src/barabadb/core/tracing.nim b/src/barabadb/core/tracing.nim index e687e31..dfe438f 100644 --- a/src/barabadb/core/tracing.nim +++ b/src/barabadb/core/tracing.nim @@ -116,7 +116,7 @@ proc exportOtlp*(tracer: Tracer, endpoint: string = "http://localhost:4318/v1/tr {"key": "service.name", "value": {"stringValue": "baradadb"}} ]}, "scopeSpans": [{ - "scope": {"name": "baradadb-tracer", "version": "0.1.0"}, + "scope": {"name": "baradadb-tracer", "version": "1.1.0"}, "spans": otlpSpans }] }] diff --git a/src/baradadb.nim b/src/baradadb.nim index e78f54d..73b8a22 100644 --- a/src/baradadb.nim +++ b/src/baradadb.nim @@ -86,7 +86,7 @@ proc main() = # Init structured logger from config let logLvl = parseEnum[LogLevel]("ll" & capitalizeAscii(config.logLevel)) defaultLogger = newLogger(logLvl, config.logFile) - info("BaraDB v1.0.0 — Multimodal Database Engine") + info("BaraDB v1.1.0 — Multimodal Database Engine") # Security check: warn if JWT secret is not configured if config.jwtSecret.len == 0: diff --git a/tests/test_all.nim.bak b/tests/test_all.nim.bak deleted file mode 100644 index 2942370..0000000 --- a/tests/test_all.nim.bak +++ /dev/null @@ -1,2571 +0,0 @@ -## 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 "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": - removeDir("/tmp/baradb_test_lsm") - 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": - removeDir("/tmp/baradb_test_lsm2") - 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": - removeDir("/tmp/baradb_test_lsm3") - 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 - - test "Save and load graph": - var g = gengine.newGraph() - let n1 = gengine.addNode(g, "Person", {"name": "Alice"}.toTable) - let n2 = gengine.addNode(g, "Person", {"name": "Bob"}.toTable) - let n3 = gengine.addNode(g, "City", {"name": "Sofia"}.toTable) - discard gengine.addEdge(g, n1, n2, "knows", {"since": "2020"}.toTable, 1.5) - discard gengine.addEdge(g, n2, n3, "lives_in", weight = 2.0) - - let path = "/tmp/baradb_test_graph.bin" - removeFile(path) - gengine.saveToFile(g, path) - - let g2 = gengine.loadFromFile(path) - check gengine.nodeCount(g2) == 3 - check gengine.edgeCount(g2) == 2 - check gengine.neighbors(g2, n1).len == 1 - check gengine.neighbors(g2, n1)[0] == n2 - check gengine.neighbors(g2, n2)[0] == n3 - - let loadedNode = gengine.getNode(g2, n1) - check loadedNode.label == "Person" - check loadedNode.properties["name"] == "Alice" - - let sp = gengine.shortestPath(g2, n1, n3) - check sp.len == 3 - - removeFile(path) - -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 "MVCC Deadlock Detection": - test "TxnManager detects and breaks deadlock": - var tm = newTxnManager() - var t1 = tm.beginTxn() - var t2 = tm.beginTxn() - # t1 writes key "a" - check tm.write(t1, "a", @[1'u8]) - # t2 writes key "b" - check tm.write(t2, "b", @[2'u8]) - # t2 tries to write "a" — conflicts with t1 (active), adds wait edge t2->t1 - check not tm.write(t2, "a", @[3'u8]) - # t1 tries to write "b" — conflicts with t2 (active), adds wait edge t1->t2 - # This creates a cycle: t1->t2->t1 - check not tm.write(t1, "b", @[4'u8]) - # One of the transactions should have been aborted as victim - let t1Active = t1.state == tsActive - let t2Active = t2.state == tsActive - # At least one victim must be aborted - check (not t1Active) or (not t2Active) - # The survivor should be able to commit - if t1Active: - check tm.commit(t1) - if t2Active: - check tm.commit(t2) - - test "No false deadlock on sequential writes": - var tm = newTxnManager() - var t1 = tm.beginTxn() - # t1 writes key "a" - check tm.write(t1, "a", @[1'u8]) - # t1 commits - check tm.commit(t1) - # t2 begins after t1 committed - var t2 = tm.beginTxn() - # t2 writes same key — no active conflict - check tm.write(t2, "a", @[2'u8]) - check tm.commit(t2) - -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", 9472) - let conn = pool.acquire() - check conn != nil - check conn.host == "127.0.0.1" - check conn.port == 9472 - pool.release(conn) - - test "Pool stats": - var cfg = defaultPoolConfig() - cfg.minConnections = 1 - cfg.maxConnections = 10 - var pool = newConnectionPool("127.0.0.1", 9472, "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 - - test "SCRAM-SHA-256 full handshake": - var am = newAuthManager() - am.registerScramUser("alice", "wonderland") - - # Step 1: ClientFirstMessage - let clientNonce = generateNonce() - let clientFirst = "n,,n=alice,r=" & clientNonce - - # Step 2: ServerFirstMessage - let serverFirst = am.startScram(clientFirst) - check serverFirst.startsWith("r=") - check serverFirst.contains("s=") - check serverFirst.contains("i=4096") - - # Parse serverFirst - var combinedNonce = "" - var saltB64 = "" - var iterCount = 0 - for part in serverFirst.split(","): - if part.startsWith("r="): combinedNonce = part[2..^1] - elif part.startsWith("s="): saltB64 = part[2..^1] - elif part.startsWith("i="): iterCount = parseInt(part[2..^1]) - check combinedNonce.len > 0 - check saltB64.len > 0 - check iterCount == 4096 - - # Step 3: Compute client proof - let salt = decode(saltB64) - let saltedPassword = pbkdf2HmacSha256("wonderland", salt, iterCount) - let clientKey = hmacSha256(saltedPassword, "Client Key") - let storedKey = sha256(clientKey) - let serverKey = hmacSha256(saltedPassword, "Server Key") - - let clientFirstBare = "n=alice,r=" & clientNonce - let clientFinalWithoutProof = "c=biws,r=" & combinedNonce - let authMessage = clientFirstBare & "," & serverFirst & "," & clientFinalWithoutProof - - let clientSignature = hmacSha256(storedKey, authMessage) - var clientProof = newSeq[byte](32) - for i in 0..<32: - clientProof[i] = clientKey[i] xor clientSignature[i] - var proofStr = newString(32) - copyMem(addr proofStr[0], addr clientProof[0], 32) - let proofB64 = encode(proofStr) - # Strip padding - var proofB64Clean = proofB64 - while proofB64Clean.endsWith("="): - proofB64Clean.setLen(proofB64Clean.len - 1) - proofB64Clean = proofB64Clean.replace("+", "-").replace("/", "_") - - let clientFinal = "c=biws,r=" & combinedNonce & ",p=" & proofB64Clean - - # Step 4: Server verifies - let (ok, serverFinal) = am.finishScram(clientFinal) - check ok - check serverFinal.startsWith("v=") - - # Verify server signature ourselves - let expectedServerSig = hmacSha256(serverKey, authMessage) - var expectedStr = newString(32) - copyMem(addr expectedStr[0], unsafeAddr expectedServerSig[0], 32) - let expectedB64 = encode(expectedStr) - check serverFinal == "v=" & expectedB64 - - test "SCRAM-SHA-256 invalid proof rejected": - var am = newAuthManager() - am.registerScramUser("bob", "builder") - - let clientNonce = generateNonce() - let clientFirst = "n,,n=bob,r=" & clientNonce - let serverFirst = am.startScram(clientFirst) - - var combinedNonce = "" - for part in serverFirst.split(","): - if part.startsWith("r="): combinedNonce = part[2..^1] - - let clientFinal = "c=biws,r=" & combinedNonce & ",p=invalidproof" - let (ok, serverFinal) = am.finishScram(clientFinal) - check not ok - check serverFinal == "e=invalid-proof" - -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" - check ast.stmts[0].selWith[0][2] == false - - 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 - check ast.stmts[0].selWith[0][2] == false - - 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", 9472)) - rm.addReplica(newReplica("r2", "10.0.0.2", 9472)) - 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", 9472)) - 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", 9472)) - 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", 9472)) - rm.addReplica(newReplica("r2", "10.0.0.2", 9472)) - rm.addReplica(newReplica("r3", "10.0.0.3", 9472)) - 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", 9472)) - 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 - -suite "Cross-Modal Engine": - test "Create engine": - let engine = newCrossModalEngine("/tmp/baradb_test_crossmodal") - check engine != nil - - test "Document operations": - let engine = newCrossModalEngine("/tmp/baradb_test_crossmodal2") - engine.put("key1", cast[seq[byte]]("value1")) - let (found, val) = engine.get("key1") - check found - check cast[string](val) == "value1" - - test "Vector operations": - let engine = newCrossModalEngine("/tmp/baradb_test_crossmodal3") - engine.insertVector(1, @[1.0'f32, 0.0'f32, 0.0'f32], {"cat": "A"}.toTable) - engine.insertVector(2, @[0.0'f32, 1.0'f32, 0.0'f32], {"cat": "B"}.toTable) - let results = engine.searchVector(@[1.0'f32, 0.1'f32, 0.0'f32], 2) - check results.len == 2 - - test "Graph operations": - let engine = newCrossModalEngine("/tmp/baradb_test_crossmodal4") - let n1 = engine.addNode("Person") - let n2 = engine.addNode("Person") - discard engine.addEdge(n1, n2, "knows") - let traversal = engine.traverseGraph(n1, "bfs") - check traversal.len >= 1 - - test "FTS operations": - let engine = newCrossModalEngine("/tmp/baradb_test_crossmodal5") - engine.indexText(1, "Nim programming language") - engine.indexText(2, "Python data science") - let results = engine.searchText("programming") - check results.len >= 1 - - test "2PC transaction": - var txn = newTPCTransaction(1) - txn.addParticipant("storage") - txn.addParticipant("vector") - txn.addParticipant("graph") - check txn.participantCount == 3 - check txn.prepare() - check txn.isPrepared - check txn.commit() - check txn.isCommitted - - test "2PC rollback": - var txn = newTPCTransaction(2) - txn.addParticipant("storage") - txn.addParticipant("vector") - check txn.prepare() - check txn.rollback() - check txn.isAborted - - test "Hybrid query": - let engine = newCrossModalEngine("/tmp/baradb_test_crossmodal6") - engine.insertVector(1, @[1.0'f32, 0.0'f32], {"cat": "A"}.toTable) - engine.indexText(1, "fast database") - var query = newCrossModalQuery(qmHybrid) - query.vector = @[1.0'f32, 0.0'f32] - query.vectorK = 5 - query.searchQuery = "fast" - query.vecWeight = 1.0 - query.ftsWeight = 1.0 - let result = engine.hybridSearch(query) - check result.totalResults >= 0 - -suite "Gossip Protocol": - test "Create gossip node": - var gp = newGossipProtocol("node1", "10.0.0.1", 7946) - check gp.self.id == "node1" - check gp.memberCount == 0 - - test "Add members": - var gp = newGossipProtocol("node1", "10.0.0.1", 7946) - let node2 = newGossipNode("node2", "10.0.0.2", 7946) - let node3 = newGossipNode("node3", "10.0.0.3", 7946) - gp.addMember(node2) - gp.addMember(node3) - check gp.memberCount == 2 - check gp.aliveCount == 2 - - test "Suspect and declare dead": - var gp = newGossipProtocol("node1", "10.0.0.1", 7946) - let node2 = newGossipNode("node2", "10.0.0.2", 7946) - gp.addMember(node2) - gp.suspect("node2") - check gp.getMember("node2").state == nsSuspect - gp.declareDead("node2") - check gp.memberCount == 1 - - test "Gossip message": - var gp = newGossipProtocol("node1", "10.0.0.1", 7946) - let node2 = newGossipNode("node2", "10.0.0.2", 7946) - gp.addMember(node2) - let msg = gp.createGossipMessage() - check msg.senderId == "node1" - check msg.nodes.len == 1 - - test "Select gossip targets": - var gp = newGossipProtocol("node1", "10.0.0.1", 7946, fanout = 2) - for i in 2..5: - gp.addMember(newGossipNode("node" & $i, "10.0.0." & $i, 7946)) - let targets = gp.selectGossipTargets() - check targets.len <= 2 - - test "Member IDs": - var gp = newGossipProtocol("node1", "10.0.0.1", 7946) - gp.addMember(newGossipNode("node2", "10.0.0.2", 7946)) - check gp.isMember("node2") - check not gp.isMember("node99") - -suite "Client Library": - test "Connection string parser": - let config = parseConnectionString("host=localhost port=9472 dbname=test user=admin") - check config.host == "localhost" - check config.port == 9472 - check config.database == "test" - check config.username == "admin" - - test "Client config defaults": - let config = defaultClientConfig() - check config.host == "127.0.0.1" - check config.port == 9472 - - test "Query builder": - let client = newBaraClient() - let qb = newQueryBuilder(client) - let sql = qb.select("name", "age").from("users") - .where("age > 18").orderBy("name", "ASC").limit(10).build() - check sql == "SELECT name, age FROM users WHERE age > 18 ORDER BY name ASC LIMIT 10" - - test "Query builder with JOIN": - let client = newBaraClient() - let qb = newQueryBuilder(client) - let sql = qb.select("u.name", "o.total") - .from("users u").join("orders o", "u.id = o.user_id") - .where("o.total > 100").build() - check "JOIN" in sql - check "WHERE" in sql - - test "Query builder with GROUP BY": - let client = newBaraClient() - 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 - -suite "Import/Export": - test "JSON export": - let columns = @["name", "age"] - let rows = @[@["Alice", "30"], @["Bob", "25"]] - let json = fileops.toJson(columns, rows) - check json.startsWith("[") - check "Alice" in json - - test "CSV export": - let columns = @["name", "age"] - let rows = @[@["Alice", "30"], @["Bob", "25"]] - let csv = fileops.toCsv(columns, rows) - check csv.startsWith("name,age") - check "Alice" in csv - - test "JSON import": - let json = """[{"name": "Alice", "age": "30"}, {"name": "Bob", "age": "25"}]""" - let (columns, rows) = fileops.parseJsonTable(json) - check columns.len == 2 - check rows.len == 2 - - test "CSV import": - let csv = "name,age\nAlice,30\nBob,25" - let (columns, rows) = fileops.parseCsvTable(csv) - check columns.len == 2 - check rows.len == 2 - check rows[0][0] == "Alice" - - test "NDJSON export/import": - let columns = @["name", "age"] - let rows = @[@["Alice", "30"]] - let ndjson = fileops.toNdjson(columns, rows) - check "Alice" in ndjson - - test "CSV with quoted fields": - let csv = "name,bio\nAlice,\"Software engineer, Nim\"\nBob,Data scientist" - let (columns, rows) = fileops.parseCsvTable(csv) - check rows.len == 2 - -suite "Multi-Language FTS": - test "English tokenizer": - let config = mlang.getLanguageConfig(mlang.langEnglish) - let tokens = mlang.tokenize("The quick brown fox jumps over the lazy dog", config) - check tokens.len > 0 - check "the" notin tokens # stop word - - test "Bulgarian tokenizer": - let config = mlang.getLanguageConfig(mlang.langBulgarian) - let tokens = mlang.tokenize("Бързата кафява лисица прескача мързеливото куче", config) - check tokens.len > 0 - - test "German tokenizer": - let config = mlang.getLanguageConfig(mlang.langGerman) - let tokens = mlang.tokenize("Der schnelle braune Fuchs springt über den faulen Hund", config) - check tokens.len > 0 - check "der" notin tokens - - test "Russian tokenizer": - let config = mlang.getLanguageConfig(mlang.langRussian) - let tokens = mlang.tokenize("Быстрая браун лиса прыгает через ленивую собаку", config) - check tokens.len > 0 - - test "Language detection": - check mlang.detectLanguage("Hello world how are you") == mlang.langEnglish - # Bulgarian text is also Cyrillic — detected as Russian by default - check mlang.detectLanguage("Здравей свят как си") == mlang.langRussian - - test "English stemming": - check mlang.stemEnglish("running") == "runn" - check mlang.stemEnglish("cats") == "cat" - check mlang.stemEnglish("programming") == "programm" - - test "Bulgarian stemming": - check mlang.stemBulgarian("красота") == "красо" - -suite "Zero-Copy Serialization": - test "Write and read int32": - var buf = newZeroBuf(64) - buf.writeInt32(42) - check buf.readInt32(0) == 42 - buf.free() - - test "Write and read int64": - var buf = newZeroBuf(64) - buf.writeInt64(12345) - check buf.readInt64(0) == 12345 - buf.free() - - test "Write and read bool": - var buf = newZeroBuf(64) - buf.writeBool(true) - check buf.readBool(0) - buf.free() - - test "ZcSchema field offsets": - var schema = newZcSchema("user") - schema.addField("id", ztInt64) - schema.addField("name", ztString) - check schema.fields.len == 2 - check schema.totalSize > 0 - - test "Encode and decode record": - var schema = newZcSchema("user") - schema.addField("id", ztInt32) - var buf = newZeroBuf(schema.totalSize) - buf.pos = schema.totalSize # pretend we wrote - buf.encodeRecord(schema, {"id": "42"}.toTable) - # Reset pos for reading at offsets - var pos = 0 - let row = buf.decodeRecord(schema) - check row["id"] == "42" - buf.free() - - test "ZcTable batch operations": - var schema = newZcSchema("user") - schema.addField("id", ztInt32) - var table = newZcTable(schema) - var buf1 = newZeroBuf(schema.totalSize) - buf1.encodeRecord(schema, {"id": "1"}.toTable) - table.records.add(buf1) - var buf2 = newZeroBuf(schema.totalSize) - buf2.encodeRecord(schema, {"id": "2"}.toTable) - table.records.add(buf2) - table.totalRows = 2 - check table.totalRows == 2 - check table.getRecord(1)["id"] == "2" - for i in 0.. 0 - check cypher.returnExprs.len == 2 - check cypher.orderBy.len > 0 - check cypher.limit == 10 - - test "Execute basic MATCH": - var g = newGraph() - discard g.addNode("Person", {"name": "Alice"}.toTable) - discard g.addNode("Person", {"name": "Bob"}.toTable) - discard g.addNode("Company", {"name": "Acme"}.toTable) - - let query = parseCypher("MATCH (p:Person) RETURN p") - let result = executeCypher(g, query) - check result.rows.len == 2 - - test "Match nodes with properties": - var g = newGraph() - discard g.addNode("Person", {"name": "Alice", "age": "30"}.toTable) - discard g.addNode("Person", {"name": "Bob", "age": "25"}.toTable) - - let matches = matchNodes(g, "Person", {"name": "Alice"}.toTable) - check matches.len == 1 - check matches[0].properties["name"] == "Alice" - -suite "Crash Recovery": - test "Scan WAL file": - var walDir = "/tmp/baradb_test_recovery_wal" - var wal = newWriteAheadLog(walDir) - wal.writePut(@[1'u8], @[2'u8], 1) - wal.sync() - wal.close() - - var rec = newCrashRecovery(walDir, "/tmp") - let entries = rec.scanWAL() - check entries.len >= 1 # at least the put entry - - test "Analyze recovery": - var walDir = "/tmp/baradb_test_recovery_wal2" - var wal = newWriteAheadLog(walDir) - wal.writePut(@[1'u8], @[2'u8], 1) - wal.writeCommit(1) - wal.close() - - var rec = newCrashRecovery(walDir, "/tmp") - let result = rec.analyze() - check result.totalEntries >= 1 - check result.applied == true - - test "Recover returns summary": - var walDir = "/tmp/baradb_test_recovery_wal3" - var wal = newWriteAheadLog(walDir) - wal.writePut(@[1'u8], @[2'u8], 1) - wal.writeCommit(1) - wal.close() - - var rec = newCrashRecovery(walDir, "/tmp") - let summary = rec.summary() - check "WAL Recovery" in summary - check "Total" in summary - -suite "Raft Election Timer": - test "Election timer tick": - var cluster = newRaftCluster() - cluster.addNode("n1") - cluster.addNode("n2") - cluster.addNode("n3") - - let n1 = cluster.nodes["n1"] - var timer = newElectionTimer(n1, timeoutMs = 0) # immediate timeout - - # Force election - n1.becomeCandidate() - n1.becomeLeader() - timer.tick() - check n1.isLeader - - test "Timer reset": - var cluster = newRaftCluster() - cluster.addNode("n1") - let n1 = cluster.nodes["n1"] - var timer = newElectionTimer(n1, timeoutMs = 1000) - timer.resetTimeout() - check not timer.checkTimeout() - - test "Multi-node election with timer": - var cluster = newRaftCluster() - cluster.addNode("n1") - cluster.addNode("n2") - - let n2 = cluster.nodes["n2"] - var timer2 = newElectionTimer(n2, timeoutMs = 0) - n2.becomeCandidate() - let req = n2.requestVote() - check req.len == 1 # n2 requests vote from n1 - let reply = cluster.nodes["n1"].handleRequestVote(req[0]) - check reply.success - -suite "Raft Network Transport": - test "3-node election over TCP": - var n1 = newRaftNode("n1", @["n2", "n3"], raftPort = 19001) - var n2 = newRaftNode("n2", @["n1", "n3"], raftPort = 19002) - var n3 = newRaftNode("n3", @["n1", "n2"], raftPort = 19003) - - n1.peerAddrs["n2"] = ("127.0.0.1", 19002) - n1.peerAddrs["n3"] = ("127.0.0.1", 19003) - n2.peerAddrs["n1"] = ("127.0.0.1", 19001) - n2.peerAddrs["n3"] = ("127.0.0.1", 19003) - n3.peerAddrs["n1"] = ("127.0.0.1", 19001) - n3.peerAddrs["n2"] = ("127.0.0.1", 19002) - - let net1 = newRaftNetwork(n1) - let net2 = newRaftNetwork(n2) - let net3 = newRaftNetwork(n3) - - asyncCheck net1.run() - asyncCheck net2.run() - asyncCheck net3.run() - waitFor sleepAsync(50) - - var timer1 = newElectionTimer(n1, timeoutMs = 50) - var timer2 = newElectionTimer(n2, timeoutMs = 100) - var timer3 = newElectionTimer(n3, timeoutMs = 150) - - for i in 0 ..< 30: - timer1.tick(net1) - timer2.tick(net2) - timer3.tick(net3) - waitFor sleepAsync(20) - - net1.stop() - net2.stop() - net3.stop() - waitFor sleepAsync(50) - - var leaderCount = 0 - if n1.isLeader: inc leaderCount - if n2.isLeader: inc leaderCount - if n3.isLeader: inc leaderCount - check leaderCount == 1 - -suite "CLI Autocomplete": - test "Autocomplete commands": - let res = autocomplete("he") - check "help" in res - - test "Autocomplete keywords": - let res = autocomplete("SEL") - check "SELECT" in res - let res2 = autocomplete("SELECT FRO") - check "FROM" in res2 - - test "Suggest returns completions": - let suggestion = suggest("SE") - check suggestion.len > 0 - - test "Autocomplete empty": - let res = autocomplete("") - check res.len == 0 - -suite "TLS/SSL": - test "Create TLS config": - let config = newTLSConfig("cert.pem", "key.pem", "ca.pem", verifyPeer = true) - check config.certFile == "cert.pem" - check config.verifyPeer == true - - test "Validate cert — missing file": - let errors = validateCert("nonexistent.pem") - check errors.len > 0 - - test "Certificate info parsing": - # Write a dummy PEM cert - let testCert = "/tmp/baradb_test_cert.pem" - writeFile(testCert, "Subject: CN=localhost\nIssuer: CN=localhost\n") - let info = parseCertInfo(testCert) - check info.subject.len > 0 - check info.isSelfSigned # subject == issuer - - test "TLS context creation with missing cert raises": - var raised = false - try: - discard newTLSContext(newTLSConfig("nonexistent.pem", "nonexistent.key")) - except IOError: - raised = true - check raised - - test "Generate self-signed cert": - let (certPath, keyPath) = generateSelfSignedCert("/tmp/baradb_test_tls", "test.local") - # May fail if openssl not installed - if certPath.len > 0: - check fileExists(certPath) - check fileExists(keyPath) - # Should be able to create TLS context from generated cert - let ctx = newTLSContext(newTLSConfig(certPath, keyPath)) - check ctx != nil - - test "Server with TLS config": - var cfg = defaultConfig() - cfg.tlsEnabled = true - let (certPath, keyPath) = generateSelfSignedCert("/tmp/baradb_test_tls2", "test.local") - if certPath.len > 0: - cfg.certFile = certPath - cfg.keyFile = keyPath - var srv = newServer(cfg) - check srv != nil - check srv.tls != nil - -suite "Triggers": - test "Parse CREATE TRIGGER": - let ast = parse("CREATE TRIGGER log_insert BEFORE INSERT ON users AS INSERT INTO audit_log VALUES ('insert', 'users')") - check ast.stmts.len == 1 - check ast.stmts[0].kind == nkCreateTrigger - check ast.stmts[0].trigName == "log_insert" - check ast.stmts[0].trigTable == "users" - check ast.stmts[0].trigTiming == "before" - check ast.stmts[0].trigEvent == "INSERT" - check ast.stmts[0].trigAction.strVal.contains("INSERT") - check ast.stmts[0].trigAction.strVal.contains("audit_log") - - test "Parse CREATE TRIGGER AFTER UPDATE": - let ast = parse("CREATE TRIGGER audit_update AFTER UPDATE ON orders AS INSERT INTO audit VALUES ('updated')") - check ast.stmts[0].kind == nkCreateTrigger - check ast.stmts[0].trigTiming == "after" - check ast.stmts[0].trigEvent == "UPDATE" - - test "Parse CREATE TRIGGER INSTEAD OF DELETE": - let ast = parse("CREATE TRIGGER soft_delete INSTEAD OF DELETE ON users AS UPDATE users SET deleted = true WHERE id = OLD.id") - check ast.stmts[0].kind == nkCreateTrigger - check ast.stmts[0].trigTiming == "instead of" - check ast.stmts[0].trigEvent == "DELETE" - - test "Parse DROP TRIGGER": - let ast = parse("DROP TRIGGER log_insert") - check ast.stmts.len == 1 - check ast.stmts[0].kind == nkDropTrigger - check ast.stmts[0].trigDropName == "log_insert" - check ast.stmts[0].trigDropIfExists == false - - test "Parse DROP TRIGGER IF EXISTS": - let ast = parse("DROP TRIGGER IF EXISTS old_trigger") - check ast.stmts[0].kind == nkDropTrigger - check ast.stmts[0].trigDropName == "old_trigger" - check ast.stmts[0].trigDropIfExists == true - -suite "Row-Level Security": - test "Parse CREATE USER": - let ast = parse("CREATE USER admin WITH PASSWORD 'secret' SUPERUSER") - check ast.stmts.len == 1 - check ast.stmts[0].kind == nkCreateUser - check ast.stmts[0].cuName == "admin" - check ast.stmts[0].cuPassword == "secret" - check ast.stmts[0].cuSuperuser == true - - test "Parse CREATE USER without superuser": - let ast = parse("CREATE USER reader WITH PASSWORD 'reader123'") - check ast.stmts[0].kind == nkCreateUser - check ast.stmts[0].cuName == "reader" - check ast.stmts[0].cuPassword == "reader123" - check ast.stmts[0].cuSuperuser == false - - test "Parse DROP USER": - let ast = parse("DROP USER admin") - check ast.stmts[0].kind == nkDropUser - check ast.stmts[0].duName == "admin" - - test "Parse CREATE POLICY": - let ast = parse("CREATE POLICY user_isolation ON accounts FOR SELECT USING (user_id = current_user)") - check ast.stmts[0].kind == nkCreatePolicy - check ast.stmts[0].cpName == "user_isolation" - check ast.stmts[0].cpTable == "accounts" - check ast.stmts[0].cpCommand == "SELECT" - - test "Parse CREATE POLICY with WITH CHECK": - let ast = parse("CREATE POLICY insert_check ON accounts FOR INSERT WITH CHECK (amount > 0)") - check ast.stmts[0].kind == nkCreatePolicy - check ast.stmts[0].cpCommand == "INSERT" - - test "Parse DROP POLICY": - let ast = parse("DROP POLICY user_isolation ON accounts") - check ast.stmts[0].kind == nkDropPolicy - check ast.stmts[0].dpName == "user_isolation" - check ast.stmts[0].dpTable == "accounts" - - test "Parse GRANT": - let ast = parse("GRANT SELECT ON accounts TO reader") - check ast.stmts[0].kind == nkGrant - check ast.stmts[0].grPrivilege == "SELECT" - check ast.stmts[0].grTable == "accounts" - check ast.stmts[0].grGrantee == "reader" - - test "Parse REVOKE": - let ast = parse("REVOKE INSERT ON accounts FROM reader") - check ast.stmts[0].kind == nkRevoke - check ast.stmts[0].rvPrivilege == "INSERT" - check ast.stmts[0].rvTable == "accounts" - check ast.stmts[0].rvGrantee == "reader" - - test "Parse ENABLE ROW LEVEL SECURITY": - let ast = parse("ALTER TABLE accounts ENABLE ROW LEVEL SECURITY") - check ast.stmts[0].kind == nkEnableRLS - check ast.stmts[0].erlsTable == "accounts" - - test "Parse DISABLE ROW LEVEL SECURITY": - let ast = parse("ALTER TABLE accounts DISABLE ROW LEVEL SECURITY") - check ast.stmts[0].kind == nkDisableRLS - check ast.stmts[0].drlsTable == "accounts" - - test "RLS filter on SELECT": - var testDir = getTempDir() / "baradb_rls_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks - createDir(testDir) - var db = newLSMTree(testDir) - var ctx = qexec.newExecutionContext(db) - # Create table and insert data - discard qexec.executeQuery(ctx, parse("CREATE TABLE docs (id INTEGER, owner TEXT)")) - discard qexec.executeQuery(ctx, parse("INSERT INTO docs (id, owner) VALUES (1, 'alice'), (2, 'bob')")) - # Create user and policy - ctx.currentUser = "alice" - ctx.users["alice"] = qexec.UserDef(name: "alice", passwordHash: "", isSuperuser: false, roles: @[]) - ctx.policies["docs"] = @[ - qexec.PolicyDef(name: "owner_only", tableName: "docs", command: "SELECT", - usingExpr: Node(kind: nkBinOp, binOp: bkEq, - binLeft: Node(kind: nkIdent, identName: "owner"), - binRight: Node(kind: nkStringLit, strVal: "alice")), - withCheckExpr: nil) - ] - # Query should only return alice's row - let res = qexec.executeQuery(ctx, parse("SELECT id, owner FROM docs")) - check res.success - check res.rows.len == 1 - check res.rows[0]["owner"] == "alice" - - test "RLS superuser bypass": - var testDir = getTempDir() / "baradb_rls_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks - createDir(testDir) - var db = newLSMTree(testDir) - var ctx = qexec.newExecutionContext(db) - discard qexec.executeQuery(ctx, parse("CREATE TABLE docs (id INTEGER, owner TEXT)")) - discard qexec.executeQuery(ctx, parse("INSERT INTO docs (id, owner) VALUES (1, 'alice')")) - ctx.currentUser = "admin" - ctx.users["admin"] = qexec.UserDef(name: "admin", passwordHash: "", isSuperuser: true, roles: @[]) - ctx.policies["docs"] = @[ - qexec.PolicyDef(name: "owner_only", tableName: "docs", command: "SELECT", - usingExpr: Node(kind: nkBinOp, binOp: bkEq, - binLeft: Node(kind: nkIdent, identName: "owner"), - binRight: Node(kind: nkStringLit, strVal: "alice")), - withCheckExpr: nil) - ] - let res = qexec.executeQuery(ctx, parse("SELECT id, owner FROM docs")) - check res.success - check res.rows.len == 1 # superuser sees all (only 1 row exists) - -suite "UTF-8 Support": - test "Tokenize UTF-8 identifiers": - let tokens = lex.tokenize("SELECT имя FROM потребители") - check tokens[1].kind == tkIdent - check tokens[1].value == "имя" - check tokens[3].kind == tkIdent - check tokens[3].value == "потребители" - - test "Parse UTF-8 table and column names": - let ast = parse("SELECT имя, възраст FROM потребители WHERE град = 'София'") - check ast.stmts[0].kind == nkSelect - check ast.stmts[0].selFrom.fromTable == "потребители" - check ast.stmts[0].selResult[0].identName == "имя" - check ast.stmts[0].selWhere.whereExpr.binRight.strVal == "София" - - test "Execute query with UTF-8 data": - var db = newLSMTree("") - var ctx = qexec.newExecutionContext(db) - discard qexec.executeQuery(ctx, parse("CREATE TABLE потребители (имя TEXT, град TEXT)")) - discard qexec.executeQuery(ctx, parse("INSERT INTO потребители (имя, град) VALUES ('Иван', 'София'), ('Мария', 'Пловдив')")) - let res = qexec.executeQuery(ctx, parse("SELECT имя, град FROM потребители WHERE град = 'София'")) - check res.success - check res.rows.len == 1 - check res.rows[0]["имя"] == "Иван" - check res.rows[0]["град"] == "София" - -suite "B-Tree Range Scan": - test "BETWEEN uses index range scan": - var db = newLSMTree("") - var ctx = qexec.newExecutionContext(db) - discard qexec.executeQuery(ctx, parse("CREATE TABLE products (id INTEGER, name TEXT)")) - discard qexec.executeQuery(ctx, parse("INSERT INTO products (id, name) VALUES (1, 'apple'), (2, 'banana'), (3, 'cherry'), (4, 'date'), (5, 'elderberry')")) - discard qexec.executeQuery(ctx, parse("CREATE INDEX idx_products_name ON products(name)")) - let res = qexec.executeQuery(ctx, parse("SELECT name FROM products WHERE name BETWEEN 'banana' AND 'date'")) - check res.success - check res.rows.len == 3 - - test "Greater than uses index range scan": - var db = newLSMTree("") - var ctx = qexec.newExecutionContext(db) - discard qexec.executeQuery(ctx, parse("CREATE TABLE nums (id INTEGER, val TEXT)")) - discard qexec.executeQuery(ctx, parse("INSERT INTO nums (id, val) VALUES (1, '10'), (2, '20'), (3, '30'), (4, '40'), (5, '50')")) - discard qexec.executeQuery(ctx, parse("CREATE INDEX idx_nums_val ON nums(val)")) - let res = qexec.executeQuery(ctx, parse("SELECT val FROM nums WHERE val > '20'")) - check res.success - check res.rows.len == 3 - - test "Less than or equal uses index range scan": - var db = newLSMTree("") - var ctx = qexec.newExecutionContext(db) - discard qexec.executeQuery(ctx, parse("CREATE TABLE nums2 (id INTEGER, val TEXT)")) - discard qexec.executeQuery(ctx, parse("INSERT INTO nums2 (id, val) VALUES (1, '10'), (2, '20'), (3, '30'), (4, '40'), (5, '50')")) - discard qexec.executeQuery(ctx, parse("CREATE INDEX idx_nums2_val ON nums2(val)")) - let res = qexec.executeQuery(ctx, parse("SELECT val FROM nums2 WHERE val <= '30'")) - check res.success - check res.rows.len == 3 - -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" diff --git a/tests/test_lock b/tests/test_lock deleted file mode 100755 index 9824d928e083198eacf77b45bfcc9ecc072d2527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 396512 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⪻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�#2dQ!iiOD9x5OB4`$Q?w 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�clhMhk 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 Gb^af-{;R0~