refactor base API to be async
and refactor Datastore impls and tests accordingly Closes #7
This commit is contained in:
parent
3237e87130
commit
0c5d805257
|
@ -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:
|
||||
- {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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!")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
template asyncTest*(name, body: untyped) = test(name, body)
|
|
@ -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(...)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue