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
509 lines
13 KiB
Nim
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
|
|
)
|
|
)
|