mirror of
https://github.com/logos-storage/nim-datastore.git
synced 2026-01-02 13:43:11 +00:00
rename sql tests
This commit is contained in:
parent
5d29ad905d
commit
c7df334836
@ -1,586 +0,0 @@
|
||||
import std/algorithm
|
||||
import std/options
|
||||
import std/os
|
||||
|
||||
import pkg/asynctest/unittest2
|
||||
import pkg/chronos
|
||||
import pkg/stew/results
|
||||
|
||||
import ../../datastore/sqlite_datastore
|
||||
import ./templates
|
||||
|
||||
suite "SQLiteDatastore":
|
||||
var
|
||||
ds: SQLiteDatastore
|
||||
|
||||
# assumes tests/test_all is run from project root, e.g. with `nimble test`
|
||||
let
|
||||
basePath = "tests" / "test_data"
|
||||
basePathAbs = getCurrentDir() / basePath
|
||||
filename = "test_store" & dbExt
|
||||
dbPathAbs = basePathAbs / filename
|
||||
|
||||
setup:
|
||||
removeDir(basePathAbs)
|
||||
require(not dirExists(basePathAbs))
|
||||
createDir(basePathAbs)
|
||||
|
||||
teardown:
|
||||
if not ds.isNil: ds.close
|
||||
ds = nil
|
||||
removeDir(basePathAbs)
|
||||
require(not dirExists(basePathAbs))
|
||||
|
||||
asyncTest "new":
|
||||
var
|
||||
dsRes = SQLiteDatastore.new(basePathAbs, filename, readOnly = true)
|
||||
|
||||
# for `readOnly = true` to succeed the database file must already exist
|
||||
check: dsRes.isErr
|
||||
|
||||
dsRes = SQLiteDatastore.new(basePathAbs / "missing", filename)
|
||||
|
||||
check: dsRes.isErr
|
||||
|
||||
dsRes = SQLiteDatastore.new(basePathAbs, filename)
|
||||
|
||||
check:
|
||||
dsRes.isOk
|
||||
fileExists(dbPathAbs)
|
||||
|
||||
dsRes.get.close
|
||||
removeDir(basePathAbs)
|
||||
assert not dirExists(basePathAbs)
|
||||
createDir(basePathAbs)
|
||||
|
||||
dsRes = SQLiteDatastore.new(basePath, filename)
|
||||
|
||||
check:
|
||||
dsRes.isOk
|
||||
fileExists(dbPathAbs)
|
||||
|
||||
dsRes.get.close
|
||||
|
||||
# for `readOnly = true` to succeed the database file must already exist, so
|
||||
# the existing file (per previous step) is not deleted prior to the next
|
||||
# invocation of `SQLiteDatastore.new`
|
||||
|
||||
dsRes = SQLiteDatastore.new(basePath, filename, readOnly = true)
|
||||
|
||||
check: dsRes.isOk
|
||||
|
||||
dsRes.get.close
|
||||
removeDir(basePathAbs)
|
||||
assert not dirExists(basePathAbs)
|
||||
createDir(basePathAbs)
|
||||
|
||||
dsRes = SQLiteDatastore.new(memory)
|
||||
|
||||
check: dsRes.isOk
|
||||
|
||||
dsRes.get.close
|
||||
|
||||
dsRes = SQLiteDatastore.new(memory, readOnly = true)
|
||||
|
||||
check: dsRes.isErr
|
||||
|
||||
asyncTest "accessors":
|
||||
ds = SQLiteDatastore.new(basePath).get
|
||||
|
||||
check:
|
||||
parentDir(ds.dbPath) == basePathAbs
|
||||
not ds.env.isNil
|
||||
|
||||
asyncTest "helpers":
|
||||
ds = SQLiteDatastore.new(basePath).get
|
||||
|
||||
ds.close
|
||||
|
||||
check:
|
||||
ds.env.isNil
|
||||
timestamp(10.123_456) == 10_123_456.int64
|
||||
|
||||
asyncTest "put":
|
||||
let
|
||||
key = Key.init("a:b/c/d:e").get
|
||||
|
||||
# for `readOnly = true` to succeed the database file must already exist
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename).get
|
||||
ds.close
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename, readOnly = true).get
|
||||
|
||||
var
|
||||
bytes: seq[byte]
|
||||
timestamp = timestamp()
|
||||
putRes = await ds.put(key, bytes, timestamp)
|
||||
|
||||
check: putRes.isErr
|
||||
|
||||
ds.close
|
||||
removeDir(basePathAbs)
|
||||
assert not dirExists(basePathAbs)
|
||||
createDir(basePathAbs)
|
||||
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename).get
|
||||
|
||||
timestamp = timestamp()
|
||||
putRes = await ds.put(key, bytes, timestamp)
|
||||
|
||||
check: putRes.isOk
|
||||
|
||||
let
|
||||
prequeryRes = NoParamsStmt.prepare(
|
||||
ds.env, "SELECT timestamp AS foo, id AS baz, data AS bar FROM " &
|
||||
tableName & ";")
|
||||
|
||||
assert prequeryRes.isOk
|
||||
|
||||
let
|
||||
prequery = prequeryRes.get
|
||||
idCol = idCol(RawStmtPtr(prequery), 1)
|
||||
dataCol = dataCol(RawStmtPtr(prequery), 2)
|
||||
timestampCol = timestampCol(RawStmtPtr(prequery), 0)
|
||||
|
||||
var
|
||||
qId: string
|
||||
qData: seq[byte]
|
||||
qTimestamp: int64
|
||||
rowCount = 0
|
||||
|
||||
proc onData(s: RawStmtPtr) {.closure.} =
|
||||
qId = idCol()
|
||||
qData = dataCol()
|
||||
qTimestamp = timestampCol()
|
||||
inc rowCount
|
||||
|
||||
var
|
||||
qRes = prequery.query((), onData)
|
||||
|
||||
assert qRes.isOk
|
||||
|
||||
check:
|
||||
qRes.get
|
||||
qId == key.id
|
||||
qData == bytes
|
||||
qTimestamp == timestamp
|
||||
rowCount == 1
|
||||
|
||||
bytes = @[1.byte, 2.byte, 3.byte]
|
||||
timestamp = timestamp()
|
||||
putRes = await ds.put(key, bytes, timestamp)
|
||||
|
||||
check: putRes.isOk
|
||||
|
||||
rowCount = 0
|
||||
qRes = prequery.query((), onData)
|
||||
assert qRes.isOk
|
||||
|
||||
check:
|
||||
qRes.get
|
||||
qId == key.id
|
||||
qData == bytes
|
||||
qTimestamp == timestamp
|
||||
rowCount == 1
|
||||
|
||||
bytes = @[4.byte, 5.byte, 6.byte]
|
||||
timestamp = timestamp()
|
||||
putRes = await ds.put(key, bytes, timestamp)
|
||||
|
||||
check: putRes.isOk
|
||||
|
||||
rowCount = 0
|
||||
qRes = prequery.query((), onData)
|
||||
assert qRes.isOk
|
||||
|
||||
check:
|
||||
qRes.get
|
||||
qId == key.id
|
||||
qData == bytes
|
||||
qTimestamp == timestamp
|
||||
rowCount == 1
|
||||
|
||||
prequery.dispose
|
||||
|
||||
asyncTest "delete":
|
||||
let
|
||||
bytes = @[1.byte, 2.byte, 3.byte]
|
||||
|
||||
var
|
||||
key = Key.init("a:b/c/d:e").get
|
||||
|
||||
# for `readOnly = true` to succeed the database file must already exist
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename).get
|
||||
ds.close
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename, readOnly = true).get
|
||||
|
||||
var
|
||||
delRes = await ds.delete(key)
|
||||
|
||||
check: delRes.isErr
|
||||
|
||||
ds.close
|
||||
removeDir(basePathAbs)
|
||||
assert not dirExists(basePathAbs)
|
||||
createDir(basePathAbs)
|
||||
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename).get
|
||||
|
||||
let
|
||||
putRes = await ds.put(key, bytes)
|
||||
|
||||
assert putRes.isOk
|
||||
|
||||
let
|
||||
query = "SELECT * FROM " & tableName & ";"
|
||||
|
||||
var
|
||||
rowCount = 0
|
||||
|
||||
proc onData(s: RawStmtPtr) {.closure.} =
|
||||
inc rowCount
|
||||
|
||||
var
|
||||
qRes = ds.env.query(query, onData)
|
||||
|
||||
assert qRes.isOk
|
||||
check: rowCount == 1
|
||||
delRes = await ds.delete(key)
|
||||
|
||||
check: delRes.isOk
|
||||
|
||||
rowCount = 0
|
||||
qRes = ds.env.query(query, onData)
|
||||
assert qRes.isOk
|
||||
|
||||
check:
|
||||
delRes.isOk
|
||||
rowCount == 0
|
||||
|
||||
key = Key.init("X/Y/Z").get
|
||||
|
||||
delRes = await ds.delete(key)
|
||||
|
||||
check: delRes.isOk
|
||||
|
||||
asyncTest "contains":
|
||||
let
|
||||
bytes = @[1.byte, 2.byte, 3.byte]
|
||||
|
||||
var
|
||||
key = Key.init("a:b/c/d:e").get
|
||||
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename).get
|
||||
|
||||
let
|
||||
putRes = await ds.put(key, bytes)
|
||||
|
||||
assert putRes.isOk
|
||||
|
||||
var
|
||||
containsRes = await ds.contains(key)
|
||||
|
||||
check:
|
||||
containsRes.isOk
|
||||
containsRes.get == true
|
||||
|
||||
key = Key.init("X/Y/Z").get
|
||||
|
||||
containsRes = await ds.contains(key)
|
||||
|
||||
check:
|
||||
containsRes.isOk
|
||||
containsRes.get == false
|
||||
|
||||
asyncTest "get":
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename).get
|
||||
|
||||
var
|
||||
bytes: seq[byte]
|
||||
key = Key.init("a:b/c/d:e").get
|
||||
putRes = await ds.put(key, bytes)
|
||||
|
||||
assert putRes.isOk
|
||||
|
||||
var
|
||||
getRes = await ds.get(key)
|
||||
getOpt = getRes.get
|
||||
|
||||
check: getOpt.isSome and getOpt.get == bytes
|
||||
|
||||
bytes = @[1.byte, 2.byte, 3.byte]
|
||||
putRes = await ds.put(key, bytes)
|
||||
|
||||
assert putRes.isOk
|
||||
|
||||
getRes = await ds.get(key)
|
||||
getOpt = getRes.get
|
||||
|
||||
check: getOpt.isSome and getOpt.get == bytes
|
||||
|
||||
key = Key.init("X/Y/Z").get
|
||||
|
||||
assert not (await ds.contains(key)).get
|
||||
|
||||
getRes = await ds.get(key)
|
||||
getOpt = getRes.get
|
||||
|
||||
check: getOpt.isNone
|
||||
|
||||
asyncTest "query":
|
||||
ds = SQLiteDatastore.new(basePathAbs, filename).get
|
||||
|
||||
var
|
||||
key1 = Key.init("a").get
|
||||
key2 = Key.init("a/b").get
|
||||
key3 = Key.init("a/b:c").get
|
||||
key4 = Key.init("a:b").get
|
||||
key5 = Key.init("a:b/c").get
|
||||
key6 = Key.init("a:b/c:d").get
|
||||
key7 = Key.init("A").get
|
||||
key8 = Key.init("A/B").get
|
||||
key9 = Key.init("A/B:C").get
|
||||
key10 = Key.init("A:B").get
|
||||
key11 = Key.init("A:B/C").get
|
||||
key12 = Key.init("A:B/C:D").get
|
||||
|
||||
bytes1 = @[1.byte, 2.byte, 3.byte]
|
||||
bytes2 = @[4.byte, 5.byte, 6.byte]
|
||||
bytes3: seq[byte] = @[]
|
||||
bytes4 = bytes1
|
||||
bytes5 = bytes2
|
||||
bytes6 = bytes3
|
||||
bytes7 = bytes1
|
||||
bytes8 = bytes2
|
||||
bytes9 = bytes3
|
||||
bytes10 = bytes1
|
||||
bytes11 = bytes2
|
||||
bytes12 = bytes3
|
||||
|
||||
queryKey = Key.init("*").get
|
||||
|
||||
var
|
||||
putRes = await ds.put(key1, bytes1)
|
||||
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key2, bytes2)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key3, bytes3)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key4, bytes4)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key5, bytes5)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key6, bytes6)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key7, bytes7)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key8, bytes8)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key9, bytes9)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key10, bytes10)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key11, bytes11)
|
||||
assert putRes.isOk
|
||||
putRes = await ds.put(key12, bytes12)
|
||||
assert putRes.isOk
|
||||
|
||||
var
|
||||
kds: seq[QueryResponse]
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
# see https://sqlite.org/lang_select.html#the_order_by_clause
|
||||
# If a SELECT statement that returns more than one row does not have an
|
||||
# ORDER BY clause, the order in which the rows are returned is undefined.
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key1, data: bytes1),
|
||||
(key: key2, data: bytes2),
|
||||
(key: key3, data: bytes3),
|
||||
(key: key4, data: bytes4),
|
||||
(key: key5, data: bytes5),
|
||||
(key: key6, data: bytes6),
|
||||
(key: key7, data: bytes7),
|
||||
(key: key8, data: bytes8),
|
||||
(key: key9, data: bytes9),
|
||||
(key: key10, data: bytes10),
|
||||
(key: key11, data: bytes11),
|
||||
(key: key12, data: bytes12)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
queryKey = Key.init("a*").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key1, data: bytes1),
|
||||
(key: key2, data: bytes2),
|
||||
(key: key3, data: bytes3),
|
||||
(key: key4, data: bytes4),
|
||||
(key: key5, data: bytes5),
|
||||
(key: key6, data: bytes6)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
queryKey = Key.init("A*").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key7, data: bytes7),
|
||||
(key: key8, data: bytes8),
|
||||
(key: key9, data: bytes9),
|
||||
(key: key10, data: bytes10),
|
||||
(key: key11, data: bytes11),
|
||||
(key: key12, data: bytes12)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
queryKey = Key.init("a/?").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key2, data: bytes2)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
queryKey = Key.init("A/?").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key8, data: bytes8)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
queryKey = Key.init("*/?").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key2, data: bytes2),
|
||||
(key: key5, data: bytes5),
|
||||
(key: key8, data: bytes8),
|
||||
(key: key11, data: bytes11)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
queryKey = Key.init("[Aa]/?").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key2, data: bytes2),
|
||||
(key: key8, data: bytes8)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
# SQLite's GLOB operator, akin to Unix file globbing syntax, is greedy re:
|
||||
# wildcard "*". So a pattern such as "a:*[^/]" will not restrict results to
|
||||
# "/a:b", i.e. it will match on "/a:b", "/a:b/c" and "/a:b/c:d".
|
||||
|
||||
queryKey = Key.init("a:*[^/]").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key4, data: bytes4),
|
||||
(key: key5, data: bytes5),
|
||||
(key: key6, data: bytes6)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
queryKey = Key.init("a:*[Bb]").get
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds.sortedByIt(it.key.id) == @[
|
||||
(key: key4, data: bytes4)
|
||||
].sortedByIt(it.key.id)
|
||||
|
||||
kds = @[]
|
||||
|
||||
var
|
||||
deleteRes = await ds.delete(key1)
|
||||
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key2)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key3)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key4)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key5)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key6)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key7)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key8)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key9)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key10)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key11)
|
||||
assert deleteRes.isOk
|
||||
deleteRes = await ds.delete(key12)
|
||||
assert deleteRes.isOk
|
||||
|
||||
let
|
||||
emptyKds: seq[QueryResponse] = @[]
|
||||
|
||||
for kd in ds.query(Query.init(queryKey)):
|
||||
let
|
||||
(key, data) = await kd
|
||||
|
||||
kds.add (key, data)
|
||||
|
||||
check: kds == emptyKds
|
||||
Loading…
x
Reference in New Issue
Block a user