Files
dimgigov 4e54075078
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
Add Ormin ORM support for BaraDB (Nim client)
2026-05-15 21:49:08 +03:00

509 lines
13 KiB
Nim

import unittest, json, strutils, strformat, sequtils, macros, times, os, math, unicode
import ormin
import ormin/db_utils
when defined(postgre):
when defined(macosx):
{.passL: "-Wl,-rpath,/opt/homebrew/lib/postgresql@14".}
import db_connector/db_postgres as db_postgres
const backend = DbBackend.postgre
importModel(backend, "model_postgre")
const sqlFileName = "model_postgre.sql"
let db {.global.} = db_postgres.open("localhost", "test", "test", "test_ormin")
else:
from db_connector/db_sqlite import exec, getValue
const backend = DbBackend.sqlite
importModel(backend, "model_sqlite")
const sqlFileName = "model_sqlite.sql"
# let db {.global.} = open(":memory:", "", "", "")
let db {.global.} = open("test.db", "", "", "")
let
testDir = currentSourcePath.parentDir()
sqlFile = Path(testDir / sqlFileName)
proc substr(s: string; start, length: int): string {.importSql.}
let
min = -3
max = 3
serial_int_seqs = [(1, min), (2, 1), (3, 2), (4, max)]
suite &"Test common database types and functions of {backend}":
discard
suite "serial_insert":
setup:
db.dropTable(sqlFile, "tb_serial")
db.createTable(sqlFile, "tb_serial")
test "insert":
for i in serial_int_seqs:
query:
insert tb_serial(typinteger = ?i[1])
check db.getValue(sql"select count(*) from tb_serial") == $serial_int_seqs.len
test "json":
for i in serial_int_seqs:
let v = %*{"typinteger": i[1]}
query:
insert tb_serial(typinteger = %v["typinteger"])
check db.getValue(sql"select count(*) from tb_serial") == $serial_int_seqs.len
suite "serial":
db.dropTable(sqlFile, "tb_serial")
db.createTable(sqlFile, "tb_serial")
let insertSql = sql"insert into tb_serial(typinteger) values (?)"
for (_, v) in serial_int_seqs:
db.exec(insertSql, v)
doAssert db.getValue(sql"select count(*) from tb_serial") == $serial_int_seqs.len
test "query":
let res = query:
select tb_serial(typserial, typinteger)
check res == serial_int_seqs
test "where":
let
id = 1
res = query:
select tb_serial(typserial, typinteger)
where typserial == ?id
check res == serial_int_seqs.filterIt(it[0] == id)
test "json":
let res = query:
select tb_serial(typserial, typinteger)
produce json
check res == %*serial_int_seqs.mapIt(%*{"typserial": it[0], "typinteger": it[1]})
test "count":
let res = query:
select tb_serial(count(_))
check res[0] == serial_int_seqs.len
test "sum":
let res = query:
select tb_serial(sum(typinteger))
check res[0] == serial_int_seqs.mapIt(it[1]).sum()
test "avg":
let res = query:
select tb_serial(avg(typinteger))
check res[0] == serial_int_seqs.mapIt(it[1]).sum() / serial_int_seqs.len()
test "min":
let res = query:
select tb_serial(min(typinteger))
check res[0] == min
test "max":
let res = query:
select tb_serial(max(typinteger))
check res[0] == max
test "abs":
let res = query:
select tb_serial(abs(typinteger))
check res == serial_int_seqs.mapIt(abs(it[1]))
let seqs = toSeq(1..3)
suite "boolean_insert":
setup:
db.dropTable(sqlFile, "tb_boolean")
db.createTable(sqlFile, "tb_boolean")
test "insert":
for i in seqs:
let b = (i mod 2 == 0)
query:
insert tb_boolean(typboolean = ?b)
check db.getValue(sql"select count(*) from tb_boolean") == $seqs.len
test "json":
for i in seqs:
let b = %*{"typboolean": (i mod 2 == 0)}
query:
insert tb_boolean(typboolean = %b["typboolean"])
check db.getValue(sql"select count(*) from tb_boolean") == $seqs.len
suite "boolean":
db.dropTable(sqlFile, "tb_boolean")
db.createTable(sqlFile, "tb_boolean")
proc toBool(x: int): bool =
if x mod 2 == 0: true else: false
proc toDbValue(x: int): auto =
when defined(postgre):
if x mod 2 == 0: "t" else: "f"
else:
if x mod 2 == 0: 1 else: 0
let insertSql = sql"insert into tb_boolean(typboolean) values (?)"
for i in seqs:
db.exec(insertSql, toDbValue(i))
doAssert db.getValue(sql"select count(*) from tb_boolean") == $seqs.len
test "query":
let res = query:
select tb_boolean(typboolean)
check res == seqs.mapIt(it mod 2 == 0)
test "where":
let
b = true
res = query:
select tb_boolean(typboolean)
where typboolean == ?b
check res == seqs.map(toBool).filterIt(it == b)
test "json":
let res = query:
select tb_boolean(typboolean)
produce json
check res == %*seqs.mapIt(%*{"typboolean": toBool(it)})
when defined(sqlite):
test "importSql substr wrong type":
## TODO: should this be allowed?
let res = query:
select tb_boolean(substr(typboolean, 1, 2))
echo "res: ", res
let fs = [-3.14, 2.56, 10.45]
suite "float_insert":
setup:
db.dropTable(sqlFile, "tb_float")
db.createTable(sqlFile, "tb_float")
test "insert":
for f in fs:
query:
insert tb_float(typfloat = ?f)
check db.getValue(sql"select count(*) from tb_float") == $fs.len
test "json":
for f in fs:
let v = %*{"typfloat": f}
query:
insert tb_float(typfloat = %v["typfloat"])
check db.getValue(sql"select count(*) from tb_float") == $fs.len
suite "float":
db.dropTable(sqlFile, "tb_float")
db.createTable(sqlFile, "tb_float")
let insertSql = sql"insert into tb_float(typfloat) values (?)"
for f in fs:
db.exec(insertSql, f)
doAssert db.getValue(sql"select count(*) from tb_float") == $fs.len
test "query":
let res = query:
select tb_float(typfloat)
check res == fs
test "where":
let res = query:
select tb_float(typfloat)
where typfloat == ?fs[0]
check res == [fs[0]]
test "json":
let res = query:
select tb_float(typfloat)
produce json
check res == %*fs.mapIt(%*{"typfloat": it})
test "abs":
let res = query:
select tb_float(abs(typfloat))
check res == fs.mapIt(abs(it))
let ss = ["one", "Two", "three", "第四", "four'th"]
suite "string_insert":
setup:
db.dropTable(sqlFile, "tb_string")
db.createTable(sqlFile, "tb_string")
test "insert":
for v in ss:
query:
insert tb_string(typstring = ?v)
check db.getValue(sql"select count(*) from tb_string") == $ss.len
test "insert":
for v in ss:
query:
insert tb_string(typstring = "can't touch this")
check db.getValue(sql"select count(*) from tb_string") == $ss.len
test "json":
for v in ss:
let j = %*{"typstring": v}
query:
insert tb_string(typstring = %j["typstring"])
check db.getValue(sql"select count(*) from tb_string") == $ss.len
suite "string":
db.dropTable(sqlFile, "tb_string")
db.createTable(sqlFile, "tb_string")
let insertsql = sql"insert into tb_string(typstring) values (?)"
for v in ss:
db.exec(insertsql, v)
doAssert db.getValue(sql"select count(*) from tb_string") == $ss.len
test "query":
let res = query:
select tb_string(typstring)
check res == ss
test "where":
let res = query:
select tb_string(typstring)
where typstring == ?ss[0]
check res[0] == ss[0]
test "json":
let res = query:
select tb_string(typstring)
produce json
check res == %*ss.mapIt(%*{"typstring": it})
test "concat_op":
let
s = "typstring: "
let res = query:
select tb_string(?s & typstring)
check res == ss.mapIt(s & it)
test "length":
let res = query:
select tb_string(length(typstring))
check res == ss.mapIt(it.runeLen)
test "lower":
let res = query:
select tb_string(lower(typstring))
check res == ss.mapIt(it.toLowerAscii)
test "upper":
let res = query:
select tb_string(upper(typstring))
check res == ss.mapIt(it.toUpperAscii)
test "replace":
let res = query:
select tb_string(replace(typstring, "e", "o"))
check res == ss.mapIt(it.replace("e", "o"))
test "importSql substr length 0":
let res = query:
select tb_string(substr(typstring, 1, 0))
let expected = ss.mapIt($(toRunes(it)[0..<0]))
check res == expected
test "importSql substr length no arg types checked":
# TODO: fixme!
# the `functions` array only contains the types of the return
# but sqlite doesn't really care...
let res = query:
select tb_string(substr(typstring, "1", 2))
let expected = ss.mapIt($(toRunes(it)[0..<2]))
check res == expected
let js = [
%*{"name": "tom", "age": 30},
%*{"name": "bob", "age": 35},
%*{"name": "jack", "age": 23}
]
suite "insert_json":
setup:
db.dropTable(sqlFile, "tb_json")
db.createTable(sqlFile, "tb_json")
test "insert":
for j in js:
query:
insert tb_json(typjson = ?j)
check db.getValue(sql"select count(*) from tb_json") == $js.len
suite "json":
db.dropTable(sqlFile, "tb_json")
db.createTable(sqlFile, "tb_json")
let insertSql = sql"insert into tb_json(typjson) values (?)"
for j in js:
db.exec(insertSql, j)
doAssert db.getValue(sql"select count(*) from tb_json") == $js.len
test "query":
let res = query:
select tb_json(typjson)
check res == js
test "json":
let res = query:
select tb_json(typjson)
produce json
check res == %*js.mapIt(%*{"typjson": it})
let cps = [
(1, 1, "one-one"),
(1, 2, "one-two"),
(2, 1, "two-one")
]
suite "composite_pk_insert":
setup:
db.dropTable(sqlFile, "tb_composite_pk")
db.createTable(sqlFile, "tb_composite_pk")
test "insert":
for r in cps:
query:
insert tb_composite_pk(pk1 = ?r[0], pk2 = ?r[1], message = ?r[2])
check db.getValue(sql"select count(*) from tb_composite_pk") == $cps.len
test "json":
for r in cps:
let v = %*{"pk1": r[0], "pk2": r[1], "message": r[2]}
query:
insert tb_composite_pk(pk1 = %v["pk1"], pk2 = %v["pk2"], message = %v["message"])
check db.getValue(sql"select count(*) from tb_composite_pk") == $cps.len
suite "composite_pk":
db.dropTable(sqlFile, "tb_composite_pk")
db.createTable(sqlFile, "tb_composite_pk")
let insertSql = sql"insert into tb_composite_pk(pk1, pk2, message) values (?, ?, ?)"
for r in cps:
db.exec(insertSql, r[0], r[1], r[2])
doAssert db.getValue(sql"select count(*) from tb_composite_pk") == $cps.len
test "query":
let res = query:
select tb_composite_pk(pk1, pk2, message)
check res == cps
test "where":
let res = query:
select tb_composite_pk(pk1, pk2, message)
where pk1 == ?cps[0][0]
check res == cps.filterIt(it[0] == cps[0][0])
test "json":
let res = query:
select tb_composite_pk(pk1, pk2, message)
produce json
check res == %*cps.mapIt(%*{"pk1": it[0], "pk2": it[1], "message": it[2]})
suite "nullable":
setup:
db.dropTable(sqlFile, "tb_nullable")
db.createTable(sqlFile, "tb_nullable")
query:
insert tb_nullable(id = 1, note = nil)
query:
insert tb_nullable(id = 2, note = "hello")
test "where_null":
let res = query:
select tb_nullable(id)
where note == nil
check res == @[1]
test "where_not_null":
let res = query:
select tb_nullable(id)
where note != null
check res == @[2]
test "update_to_null":
query:
update tb_nullable(note = null)
where id == 2
let res = query:
select tb_nullable(id)
where note == nil
check res == @[1, 2]
test "json_null":
let res = query:
select tb_nullable(id, note)
orderby id
produce json
check res[0]["note"].kind == JNull
check res[1]["note"].getStr == "hello"
suite "upsert":
setup:
db.dropTable(sqlFile, "tb_nullable")
db.createTable(sqlFile, "tb_nullable")
test "onconflict do nothing":
let first = "first value"
query:
insert tb_nullable(id = 1, note = ?first)
let ignored = "should not overwrite"
query:
insert tb_nullable(id = 1, note = ?ignored)
onconflict(id)
donothing()
let note = db.getValue(sql"select note from tb_nullable where id = 1")
check note == first
test "onconflict do update":
let first = "old note"
query:
insert tb_nullable(id = 2, note = ?first)
let replacement = "new note"
query:
insert tb_nullable(id = 2, note = ?replacement)
onconflict(id)
doupdate(note = ?replacement)
let note = db.getValue(sql"select note from tb_nullable where id = 2")
check note == replacement
test "onconflict do update where":
query:
insert tb_nullable(id = 3, note = "keep-me")
query:
insert tb_nullable(id = 3, note = "replace-me")
onconflict(id)
doupdate(note = "replace-me")
where note != "keep-me"
let note = db.getValue(sql"select note from tb_nullable where id = 3")
check note == "keep-me"
static:
doAssert not compiles(
(block:
query:
insert tb_nullable(id = 100, note = "x")
where id == 100
)
)