refactor base API to be async

and refactor Datastore impls and tests accordingly

Closes #7
This commit is contained in:
Michael Bradley, Jr 2022-06-29 11:04:35 -05:00
parent 3237e87130
commit 0c5d805257
No known key found for this signature in database
GPG Key ID: D0307DBCF21A9A58
14 changed files with 273 additions and 239 deletions

View File

@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
cache_nonce: [ 0 ]
cache_nonce: [ 1 ]
nim_version: [ 1.2.18, 1.4.8, 1.6.6 ]
platform:
- {

View File

@ -7,6 +7,8 @@ description = "Simple, unified API for multiple data stores"
license = "Apache License 2.0 or MIT"
requires "nim >= 1.2.0",
"asynctest >= 0.3.1 & < 0.4.0",
"chronos",
"questionable >= 0.10.3 & < 0.11.0",
"sqlite3_abi",
"stew",

View File

@ -1,3 +1,4 @@
import pkg/chronos
import pkg/questionable
import pkg/questionable/results
import pkg/upraises
@ -13,31 +14,31 @@ type
method contains*(
self: Datastore,
key: Key): ?!bool {.base, locks: "unknown".} =
key: Key): Future[?!bool] {.async, base, locks: "unknown".} =
raiseAssert("Not implemented!")
method delete*(
self: Datastore,
key: Key): ?!void {.base, locks: "unknown".} =
key: Key): Future[?!void] {.async, base, locks: "unknown".} =
raiseAssert("Not implemented!")
method get*(
self: Datastore,
key: Key): ?!(?seq[byte]) {.base, locks: "unknown".} =
key: Key): Future[?!(?seq[byte])] {.async, base, locks: "unknown".} =
raiseAssert("Not implemented!")
method put*(
self: Datastore,
key: Key,
data: openArray[byte]): ?!void {.base, locks: "unknown".} =
data: seq[byte]): Future[?!void] {.async, base, locks: "unknown".} =
raiseAssert("Not implemented!")
# method query*(
# self: Datastore,
# query: ...): ?!(?...) {.base, locks: "unknown".} =
# query: ...): Future[?!(?...)] {.async, base, locks: "unknown".} =
#
# raiseAssert("Not implemented!")

View File

@ -1,7 +1,9 @@
import std/os
import pkg/chronos
import pkg/questionable
import pkg/questionable/results
from pkg/stew/results as stewResults import get, isErr
import pkg/upraises
import ./datastore
@ -60,31 +62,31 @@ proc path*(
method contains*(
self: FileSystemDatastore,
key: Key): ?!bool {.locks: "unknown".} =
key: Key): Future[?!bool] {.async, locks: "unknown".} =
success fileExists(self.path(key))
return success fileExists(self.path(key))
method delete*(
self: FileSystemDatastore,
key: Key): ?!void {.locks: "unknown".} =
key: Key): Future[?!void] {.async, locks: "unknown".} =
let
path = self.path(key)
try:
removeFile(path)
success()
return success()
# removing an empty directory might lead to surprising behavior depending
# on what the user specified as the `root` of the FileSystemDatastore, so
# until further consideration, empty directories will be left in place
except OSError as e:
failure e
return failure e
method get*(
self: FileSystemDatastore,
key: Key): ?!(?seq[byte]) {.locks: "unknown".} =
key: Key): Future[?!(?seq[byte])] {.async, locks: "unknown".} =
# to support finer control of memory allocation, maybe could/should change
# the signature of `get` so that it has a 3rd parameter
@ -95,9 +97,11 @@ method get*(
let
path = self.path(key)
exists = ? self.contains(key)
containsRes = await self.contains(key)
if exists:
if containsRes.isErr: return failure containsRes.error.msg
if containsRes.get:
var
file: File
@ -121,21 +125,21 @@ method get*(
return failure $bytesRead & " bytes were read from " & path &
" but " & $size & " bytes were expected"
success bytes.some
return success bytes.some
except IOError as e:
failure e
return failure e
finally:
file.close
else:
success seq[byte].none
return success seq[byte].none
method put*(
self: FileSystemDatastore,
key: Key,
data: openArray[byte]): ?!void {.locks: "unknown".} =
data: seq[byte]): Future[?!void] {.async, locks: "unknown".} =
let
path = self.path(key)
@ -144,16 +148,16 @@ method put*(
createDir(parentDir(path))
if data.len > 0: writeFile(path, data)
else: writeFile(path, "")
success()
return success()
except IOError as e:
failure e
return failure e
except OSError as e:
failure e
return failure e
# method query*(
# self: FileSystemDatastore,
# query: ...): ?!(?...) {.locks: "unknown".} =
# query: ...): Future[?!(?...)] {.async, locks: "unknown".} =
#
# success ....none
# return success ....some

View File

@ -6,6 +6,7 @@ import std/strutils
import pkg/questionable
import pkg/questionable/results
from pkg/stew/results as stewResults import get, isErr
import pkg/upraises
export hashes
@ -157,13 +158,12 @@ proc init*(
for s in namespaces:
let
nsRes = Namespace.init(s)
# if `without ns =? Namespace.init(s), e:` is used `e` is nil in the body
# at runtime, why?
without ns =? nsRes:
if nsRes.isErr:
return failure "namespaces contains an invalid Namespace: " &
nsRes.error.msg
nss.add ns
nss.add nsRes.get
success T(namespaces: nss)
@ -186,13 +186,12 @@ proc init*(
let
keyRes = Key.init(nsStrs)
# if `without key =? Key.init(nsStrs), e:` is used `e` is nil in the body
# at runtime, why?
without key =? keyRes:
if keyRes.isErr:
return failure "id string contains an invalid Namespace:" &
keyRes.error.msg.split(":")[1..^1].join("").replace("\"\"", "\":\"")
success key
keyRes
proc namespaces*(self: Key): seq[Namespace] =
self.namespaces

View File

@ -1,3 +1,4 @@
import pkg/chronos
import pkg/questionable
import pkg/questionable/results
import pkg/upraises
@ -16,31 +17,31 @@ proc new*(T: type NullDatastore): T =
method contains*(
self: NullDatastore,
key: Key): ?!bool {.locks: "unknown".} =
key: Key): Future[?!bool] {.async, locks: "unknown".} =
success false
return success false
method delete*(
self: NullDatastore,
key: Key): ?!void {.locks: "unknown".} =
key: Key): Future[?!void] {.async, locks: "unknown".} =
success()
return success()
method get*(
self: NullDatastore,
key: Key): ?!(?seq[byte]) {.locks: "unknown".} =
key: Key): Future[?!(?seq[byte])] {.async, locks: "unknown".} =
success seq[byte].none
return success seq[byte].none
method put*(
self: NullDatastore,
key: Key,
data: openArray[byte]): ?!void {.locks: "unknown".} =
data: seq[byte]): Future[?!void] {.async, locks: "unknown".} =
success()
return success()
# method query*(
# self: NullDatastore,
# query: ...): ?!(?...) {.locks: "unknown".} =
# query: ...): Future[?!(?...)] {.async, locks: "unknown".} =
#
# success ....none
# return success ....none

View File

@ -1,10 +1,12 @@
import std/os
import std/times
import pkg/chronos
import pkg/questionable
import pkg/questionable/results
import pkg/sqlite3_abi
import pkg/stew/byteutils
from pkg/stew/results as stewResults import get, isErr
import pkg/upraises
import ./datastore
@ -204,7 +206,7 @@ proc timestampCol*(
method contains*(
self: SQLiteDatastore,
key: Key): ?!bool {.locks: "unknown".} =
key: Key): Future[?!bool] {.async, locks: "unknown".} =
var
exists = false
@ -212,61 +214,64 @@ method contains*(
proc onData(s: RawStmtPtr) {.closure.} =
exists = sqlite3_column_int64(s, 0).bool
discard ? self.containsStmt.query((key.id), onData)
let
queryRes = self.containsStmt.query((key.id), onData)
success exists
if queryRes.isErr: return queryRes
return success exists
method delete*(
self: SQLiteDatastore,
key: Key): ?!void {.locks: "unknown".} =
key: Key): Future[?!void] {.async, locks: "unknown".} =
if self.readOnly:
failure "database is read-only":
return failure "database is read-only":
else:
self.deleteStmt.exec((key.id))
return self.deleteStmt.exec((key.id))
method get*(
self: SQLiteDatastore,
key: Key): ?!(?seq[byte]) {.locks: "unknown".} =
key: Key): Future[?!(?seq[byte])] {.async, locks: "unknown".} =
# see comment in ./filesystem_datastore re: finer control of memory
# allocation in `method get`, could apply here as well if bytes were read
# incrementally with `sqlite3_blob_read`
var
bytes: seq[byte]
bytes: ?seq[byte]
proc onData(s: RawStmtPtr) {.closure.} =
bytes = dataCol(s, 0)
bytes = dataCol(s, 0).some
let
exists = ? self.getStmt.query((key.id), onData)
queryRes = self.getStmt.query((key.id), onData)
if exists:
success bytes.some
if queryRes.isErr:
return failure queryRes.error.msg
else:
success seq[byte].none
return success bytes
proc put*(
self: SQLiteDatastore,
key: Key,
data: openArray[byte],
timestamp: int64): ?!void =
data: seq[byte],
timestamp: int64): Future[?!void] {.async.} =
if self.readOnly:
failure "database is read-only"
return failure "database is read-only"
else:
self.putStmt.exec((key.id, @data, timestamp))
return self.putStmt.exec((key.id, @data, timestamp))
method put*(
self: SQLiteDatastore,
key: Key,
data: openArray[byte]): ?!void {.locks: "unknown".} =
data: seq[byte]): Future[?!void] {.async, locks: "unknown".} =
self.put(key, data, timestamp())
return await self.put(key, data, timestamp())
# method query*(
# self: SQLiteDatastore,
# query: ...): ?!(?...) {.locks: "unknown".} =
# query: ...): Future[?!(?...)] {.async, locks: "unknown".} =
#
# success ....none
# return success ....some

View File

@ -1,5 +1,9 @@
import std/sequtils
import pkg/chronos
import pkg/questionable
import pkg/questionable/results
from pkg/stew/results as stewResults import get, isErr
import pkg/upraises
import ./datastore
@ -26,57 +30,72 @@ proc stores*(self: TieredDatastore): seq[Datastore] =
method contains*(
self: TieredDatastore,
key: Key): ?!bool {.locks: "unknown".} =
var
exists = false
key: Key): Future[?!bool] {.async, locks: "unknown".} =
for store in self.stores:
exists = ? store.contains(key)
if exists: break
let
containsRes = await store.contains(key)
success exists
if containsRes.isErr: return containsRes
if containsRes.get == true: return success true
return success false
method delete*(
self: TieredDatastore,
key: Key): ?!void {.locks: "unknown".} =
key: Key): Future[?!void] {.async, locks: "unknown".} =
for store in self.stores:
? store.delete(key)
let
pending = await allFinished(self.stores.mapIt(it.delete(key)))
success()
for fut in pending:
if fut.read().isErr: return fut.read()
return success()
method get*(
self: TieredDatastore,
key: Key): ?!(?seq[byte]) {.locks: "unknown".} =
key: Key): Future[?!(?seq[byte])] {.async, locks: "unknown".} =
var
bytesOpt: ?seq[byte]
for store in self.stores:
bytesOpt = ? store.get(key)
let
getRes = await store.get(key)
if getRes.isErr: return getRes
bytesOpt = getRes.get
# put found data into stores logically in front of the current store
if bytes =? bytesOpt:
for s in self.stores:
if s == store: break
? s.put(key, bytes)
let
putRes = await s.put(key, bytes)
if putRes.isErr: return failure putRes.error.msg
break
success bytesOpt
return success bytesOpt
method put*(
self: TieredDatastore,
key: Key,
data: openArray[byte]): ?!void {.locks: "unknown".} =
data: seq[byte]): Future[?!void] {.async, locks: "unknown".} =
for store in self.stores:
? store.put(key, data)
let
pending = await allFinished(self.stores.mapIt(it.put(key, data)))
success()
for fut in pending:
if fut.read().isErr: return fut.read()
return success()
# method query*(
# self: TieredDatastore,
# query: ...): ?!(?...) {.locks: "unknown".} =
# query: ...): Future[?!(?...)] {.async, locks: "unknown".} =
#
# success ....none
# return success ....some

View File

@ -0,0 +1 @@
template asyncTest*(name, body: untyped) = test(name, body)

View File

@ -1,30 +1,29 @@
import std/options
import pkg/asynctest/unittest2
import pkg/chronos
import pkg/stew/results
import pkg/unittest2
import ../../datastore
const
oneByte = @[1.byte]
import ./templates
suite "Datastore (base)":
setup:
let
key = Key.init("a").get
ds = Datastore()
let
key = Key.init("a").get
ds = Datastore()
oneByte = @[1.byte]
test "put":
asyncTest "put":
expect Defect: discard ds.put(key, oneByte)
test "delete":
asyncTest "delete":
expect Defect: discard ds.delete(key)
test "contains":
asyncTest "contains":
expect Defect: discard ds.contains(key)
test "get":
asyncTest "get":
expect Defect: discard ds.get(key)
# test "query":
# asyncTest "query":
# expect Defect: discard ds.query(...)

View File

@ -1,19 +1,21 @@
import std/options
import std/os
import pkg/asynctest/unittest2
import pkg/chronos
import pkg/stew/byteutils
import pkg/stew/results
import pkg/unittest2
import ../../datastore/filesystem_datastore
import ./templates
suite "FileSystemDatastore":
setup:
# assumes tests/test_all is run from project root, e.g. with `nimble test`
let
root = "tests" / "test_data"
rootAbs = getCurrentDir() / root
# assumes tests/test_all is run from project root, e.g. with `nimble test`
let
root = "tests" / "test_data"
rootAbs = getCurrentDir() / root
setup:
removeDir(rootAbs)
require(not dirExists(rootAbs))
@ -21,7 +23,7 @@ suite "FileSystemDatastore":
removeDir(rootAbs)
require(not dirExists(rootAbs))
test "new":
asyncTest "new":
var
dsRes: Result[FileSystemDatastore, ref CatchableError]
ds: FileSystemDatastore
@ -43,13 +45,13 @@ suite "FileSystemDatastore":
check: dirExists(rootAbs)
test "accessors":
asyncTest "accessors":
let
ds = FileSystemDatastore.new(root).get
check: ds.root == rootAbs
test "helpers":
asyncTest "helpers":
let
ds = FileSystemDatastore.new(root).get
@ -65,7 +67,7 @@ suite "FileSystemDatastore":
ds.path(Key.init("a/b/c:d").get) == rootAbs / "a" / "b" / "c" / "d" & objExt
ds.path(Key.init("a/b/c/d").get) == rootAbs / "a" / "b" / "c" / "d" & objExt
test "put":
asyncTest "put":
let
ds = FileSystemDatastore.new(root).get
key = Key.init("a:b/c/d:e").get
@ -73,7 +75,7 @@ suite "FileSystemDatastore":
var
bytes: seq[byte]
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
check:
putRes.isOk
@ -81,7 +83,7 @@ suite "FileSystemDatastore":
bytes = @[1.byte, 2.byte, 3.byte]
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
check:
putRes.isOk
@ -89,13 +91,13 @@ suite "FileSystemDatastore":
bytes = @[4.byte, 5.byte, 6.byte]
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
check:
putRes.isOk
readFile(path).toBytes == bytes
test "delete":
asyncTest "delete":
let
bytes = @[1.byte, 2.byte, 3.byte]
ds = FileSystemDatastore.new(root).get
@ -105,12 +107,12 @@ suite "FileSystemDatastore":
path = ds.path(key)
let
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
var
delRes = ds.delete(key)
delRes = await ds.delete(key)
check:
delRes.isOk
@ -121,11 +123,11 @@ suite "FileSystemDatastore":
path = ds.path(key)
assert not fileExists(path)
delRes = ds.delete(key)
delRes = await ds.delete(key)
check: delRes.isOk
test "contains":
asyncTest "contains":
let
bytes = @[1.byte, 2.byte, 3.byte]
ds = FileSystemDatastore.new(root).get
@ -133,12 +135,12 @@ suite "FileSystemDatastore":
var
key = Key.init("a:b/c/d:e").get
path = ds.path(key)
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
var
containsRes = ds.contains(key)
containsRes = await ds.contains(key)
assert containsRes.isOk
@ -148,12 +150,12 @@ suite "FileSystemDatastore":
path = ds.path(key)
assert not fileExists(path)
containsRes = ds.contains(key)
containsRes = await ds.contains(key)
assert containsRes.isOk
check: containsRes.get == false
test "get":
asyncTest "get":
let
ds = FileSystemDatastore.new(root).get
@ -161,22 +163,22 @@ suite "FileSystemDatastore":
bytes: seq[byte]
key = Key.init("a:b/c/d:e").get
path = ds.path(key)
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
var
getRes = ds.get(key)
getRes = await ds.get(key)
getOpt = getRes.get
check: getOpt.isSome and getOpt.get == bytes
bytes = @[1.byte, 2.byte, 3.byte]
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
getRes = ds.get(key)
getRes = await ds.get(key)
getOpt = getRes.get
check: getOpt.isSome and getOpt.get == bytes
@ -186,11 +188,11 @@ suite "FileSystemDatastore":
assert not fileExists(path)
getRes = ds.get(key)
getRes = await ds.get(key)
getOpt = getRes.get
check: getOpt.isNone
# test "query":
# asyncTest "query":
# check:
# true

View File

@ -1,38 +1,37 @@
import std/options
import pkg/asynctest/unittest2
import pkg/chronos
import pkg/stew/results
import pkg/unittest2
import ../../datastore/null_datastore
import ./templates
suite "NullDatastore":
setup:
let
key = Key.init("a").get
ds = NullDatastore.new()
let
key = Key.init("a").get
ds = NullDatastore.new()
discard key # suppresses "declared but not used" re: key
test "new":
asyncTest "new":
check: not ds.isNil
test "put":
check: ds.put(key, [1.byte]).isOk
asyncTest "put":
check: (await ds.put(key, @[1.byte])).isOk
test "delete":
check: ds.delete(key).isOk
asyncTest "delete":
check: (await ds.delete(key)).isOk
test "contains":
asyncTest "contains":
check:
ds.contains(key).isOk
ds.contains(key).get == false
(await ds.contains(key)).isOk
(await ds.contains(key)).get == false
test "get":
asyncTest "get":
check:
ds.get(key).isOk
ds.get(key).get.isNone
(await ds.get(key)).isOk
(await ds.get(key)).get.isNone
# test "query":
# asyncTest "query":
# check:
# ds.query(...).isOk
# ds.query(...).get.isNone
# (await ds.query(...)).isOk
# (await ds.query(...)).get.isNone

View File

@ -1,33 +1,35 @@
import std/options
import std/os
import pkg/asynctest/unittest2
import pkg/chronos
import pkg/stew/results
import pkg/unittest2
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:
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
removeDir(basePathAbs)
require(not dirExists(basePathAbs))
discard dbPathAbs # suppresses "declared but not used" re: dbPathAbs
teardown:
if not ds.isNil: ds.close
ds = nil
removeDir(basePathAbs)
require(not dirExists(basePathAbs))
test "new":
asyncTest "new":
var
dsRes = SQLiteDatastore.new(basePathAbs, filename, readOnly = true)
@ -90,14 +92,14 @@ suite "SQLiteDatastore":
check: dsRes.isErr
test "accessors":
asyncTest "accessors":
ds = SQLiteDatastore.new(basePath).get
check:
parentDir(ds.dbPath) == basePathAbs
not ds.env.isNil
test "helpers":
asyncTest "helpers":
ds = SQLiteDatastore.new(basePath).get
ds.close
@ -106,7 +108,7 @@ suite "SQLiteDatastore":
ds.env.isNil
timestamp(10.123_456) == 10_123_456.int64
test "put":
asyncTest "put":
let
key = Key.init("a:b/c/d:e").get
@ -118,7 +120,7 @@ suite "SQLiteDatastore":
var
bytes: seq[byte]
timestamp = timestamp()
putRes = ds.put(key, bytes, timestamp)
putRes = await ds.put(key, bytes, timestamp)
check: putRes.isErr
@ -129,7 +131,7 @@ suite "SQLiteDatastore":
ds = SQLiteDatastore.new(basePathAbs, filename).get
timestamp = timestamp()
putRes = ds.put(key, bytes, timestamp)
putRes = await ds.put(key, bytes, timestamp)
check: putRes.isOk
@ -162,7 +164,7 @@ suite "SQLiteDatastore":
bytes = @[1.byte, 2.byte, 3.byte]
timestamp = timestamp()
putRes = ds.put(key, bytes, timestamp)
putRes = await ds.put(key, bytes, timestamp)
check: putRes.isOk
@ -179,7 +181,7 @@ suite "SQLiteDatastore":
bytes = @[4.byte, 5.byte, 6.byte]
timestamp = timestamp()
putRes = ds.put(key, bytes, timestamp)
putRes = await ds.put(key, bytes, timestamp)
check: putRes.isOk
@ -194,7 +196,7 @@ suite "SQLiteDatastore":
qTimestamp == timestamp
rowCount == 1
test "delete":
asyncTest "delete":
let
bytes = @[1.byte, 2.byte, 3.byte]
@ -207,7 +209,7 @@ suite "SQLiteDatastore":
ds = SQLiteDatastore.new(basePathAbs, filename, readOnly = true).get
var
delRes = ds.delete(key)
delRes = await ds.delete(key)
check: delRes.isErr
@ -218,7 +220,7 @@ suite "SQLiteDatastore":
ds = SQLiteDatastore.new(basePathAbs, filename).get
let
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
@ -236,7 +238,7 @@ suite "SQLiteDatastore":
assert qRes.isOk
check: rowCount == 1
delRes = ds.delete(key)
delRes = await ds.delete(key)
check: delRes.isOk
@ -250,11 +252,11 @@ suite "SQLiteDatastore":
key = Key.init("X/Y/Z").get
delRes = ds.delete(key)
delRes = await ds.delete(key)
check: delRes.isOk
test "contains":
asyncTest "contains":
let
bytes = @[1.byte, 2.byte, 3.byte]
@ -264,12 +266,12 @@ suite "SQLiteDatastore":
ds = SQLiteDatastore.new(basePathAbs, filename).get
let
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
var
containsRes = ds.contains(key)
containsRes = await ds.contains(key)
assert containsRes.isOk
@ -277,46 +279,46 @@ suite "SQLiteDatastore":
key = Key.init("X/Y/Z").get
containsRes = ds.contains(key)
containsRes = await ds.contains(key)
assert containsRes.isOk
check: containsRes.get == false
test "get":
asyncTest "get":
ds = SQLiteDatastore.new(basePathAbs, filename).get
var
bytes: seq[byte]
key = Key.init("a:b/c/d:e").get
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
var
getRes = ds.get(key)
getRes = await ds.get(key)
getOpt = getRes.get
check: getOpt.isSome and getOpt.get == bytes
bytes = @[1.byte, 2.byte, 3.byte]
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
getRes = ds.get(key)
getRes = await ds.get(key)
getOpt = getRes.get
check: getOpt.isSome and getOpt.get == bytes
key = Key.init("X/Y/Z").get
assert not ds.contains(key).get
assert not (await ds.contains(key)).get
getRes = ds.get(key)
getRes = await ds.get(key)
getOpt = getRes.get
check: getOpt.isNone
# test "query":
# asyncTest "query":
# check:
# true

View File

@ -1,40 +1,40 @@
import std/options
import std/os
import pkg/asynctest/unittest2
import pkg/chronos
import pkg/stew/results
import pkg/unittest2
import ../../datastore/filesystem_datastore
import ../../datastore/sqlite_datastore
import ../../datastore/tiered_datastore
import ./templates
suite "TieredDatastore":
# assumes tests/test_all is run from project root, e.g. with `nimble test`
let
bytes = @[1.byte, 2.byte, 3.byte]
key = Key.init("a:b/c/d:e").get
root = "tests" / "test_data"
rootAbs = getCurrentDir() / root
var
ds1: SQLiteDatastore
ds2: FileSystemDatastore
setup:
# assumes tests/test_all is run from project root, e.g. with `nimble test`
let
bytes = @[1.byte, 2.byte, 3.byte]
key = Key.init("a:b/c/d:e").get
root = "tests" / "test_data"
rootAbs = getCurrentDir() / root
discard bytes # suppresses "declared but not used" re: bytes
discard key # # suppresses "declared but not used" re: key
ds1 = SQLiteDatastore.new(inMemory = true).get
ds2 = FileSystemDatastore.new(rootAbs).get
removeDir(rootAbs)
require(not dirExists(rootAbs))
var
ds1 = SQLiteDatastore.new(inMemory = true).get
ds2 = FileSystemDatastore.new(rootAbs).get
discard ds2 # suppresses "declared but not used" re: ds2
teardown:
ds1.close
if not ds1.isNil: ds1.close
ds1 = nil
removeDir(rootAbs)
require(not dirExists(rootAbs))
test "new":
asyncTest "new":
check:
TieredDatastore.new().isErr
TieredDatastore.new([]).isErr
@ -43,7 +43,7 @@ suite "TieredDatastore":
TieredDatastore.new([ds1, ds2]).isOk
TieredDatastore.new(@[ds1, ds2]).isOk
test "accessors":
asyncTest "accessors":
let
stores = @[ds1, ds2]
@ -52,102 +52,102 @@ suite "TieredDatastore":
TieredDatastore.new([ds1, ds2]).get.stores == stores
TieredDatastore.new(@[ds1, ds2]).get.stores == stores
test "put":
asyncTest "put":
let
ds = TieredDatastore.new(ds1, ds2).get
assert ds1.get(key).get.isNone
assert ds2.get(key).get.isNone
assert (await ds1.get(key)).get.isNone
assert (await ds2.get(key)).get.isNone
let
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
check:
putRes.isOk
ds1.get(key).get.get == bytes
ds2.get(key).get.get == bytes
(await ds1.get(key)).get.get == bytes
(await ds2.get(key)).get.get == bytes
test "delete":
asyncTest "delete":
let
ds = TieredDatastore.new(ds1, ds2).get
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
assert ds1.get(key).get.get == bytes
assert ds2.get(key).get.get == bytes
assert (await ds1.get(key)).get.get == bytes
assert (await ds2.get(key)).get.get == bytes
let
delRes = ds.delete(key)
delRes = await ds.delete(key)
check:
delRes.isOk
ds1.get(key).get.isNone
ds2.get(key).get.isNone
(await ds1.get(key)).get.isNone
(await ds2.get(key)).get.isNone
test "contains":
asyncTest "contains":
let
ds = TieredDatastore.new(ds1, ds2).get
assert not ds1.contains(key).get
assert not ds2.contains(key).get
assert not (await ds1.contains(key)).get
assert not (await ds2.contains(key)).get
let
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
let
containsRes = ds.contains(key)
containsRes = await ds.contains(key)
check:
containsRes.isOk
containsRes.get
ds1.contains(key).get
ds2.contains(key).get
(await ds1.contains(key)).get
(await ds2.contains(key)).get
test "get":
asyncTest "get":
var
ds = TieredDatastore.new(ds1, ds2).get
assert ds1.get(key).get.isNone
assert ds2.get(key).get.isNone
assert (await ds1.get(key)).get.isNone
assert (await ds2.get(key)).get.isNone
check: ds.get(key).get.isNone
check: (await ds.get(key)).get.isNone
let
putRes = ds.put(key, bytes)
putRes = await ds.put(key, bytes)
assert putRes.isOk
var
getRes = ds.get(key)
getRes = await ds.get(key)
check:
getRes.isOk
getRes.get.isSome
getRes.get.get == bytes
ds1.get(key).get.isSome
ds2.get(key).get.isSome
ds1.get(key).get.get == bytes
ds2.get(key).get.get == bytes
(await ds1.get(key)).get.isSome
(await ds2.get(key)).get.isSome
(await ds1.get(key)).get.get == bytes
(await ds2.get(key)).get.get == bytes
ds1.close
ds1 = SQLiteDatastore.new(inMemory = true).get
ds = TieredDatastore.new(ds1, ds2).get
assert ds1.get(key).get.isNone
assert ds2.get(key).get.isSome
assert ds2.get(key).get.get == bytes
assert (await ds1.get(key)).get.isNone
assert (await ds2.get(key)).get.isSome
assert (await ds2.get(key)).get.get == bytes
getRes = ds.get(key)
getRes = await ds.get(key)
check:
getRes.isOk
getRes.get.isSome
getRes.get.get == bytes
ds1.get(key).get.isSome
ds1.get(key).get.get == bytes
(await ds1.get(key)).get.isSome
(await ds1.get(key)).get.get == bytes
# test "query":
# asyncTest "query":
# check:
# true