From 0c5d80525763a7d4c295dc651da4d0e7f1e2cd38 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 29 Jun 2022 11:04:35 -0500 Subject: [PATCH] refactor base API to be async and refactor Datastore impls and tests accordingly Closes #7 --- .github/workflows/tests.yml | 2 +- datastore.nimble | 2 + datastore/datastore.nim | 11 +- datastore/filesystem_datastore.nim | 38 +++--- datastore/key.nim | 15 ++- datastore/null_datastore.nim | 21 ++-- datastore/sqlite_datastore.nim | 47 ++++---- datastore/tiered_datastore.nim | 61 ++++++---- tests/datastore/templates.nim | 1 + tests/datastore/test_datastore.nim | 25 ++-- tests/datastore/test_filesystem_datastore.nim | 58 ++++----- tests/datastore/test_null_datastore.nim | 41 +++---- tests/datastore/test_sqlite_datastore.nim | 76 ++++++------ tests/datastore/test_tiered_datastore.nim | 114 +++++++++--------- 14 files changed, 273 insertions(+), 239 deletions(-) create mode 100644 tests/datastore/templates.nim diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6d60f79..59082f5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -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: - { diff --git a/datastore.nimble b/datastore.nimble index 5869724..6252c6d 100644 --- a/datastore.nimble +++ b/datastore.nimble @@ -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", diff --git a/datastore/datastore.nim b/datastore/datastore.nim index bd7c1f8..13c1a0f 100644 --- a/datastore/datastore.nim +++ b/datastore/datastore.nim @@ -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!") diff --git a/datastore/filesystem_datastore.nim b/datastore/filesystem_datastore.nim index e9e66ad..3e9ad16 100644 --- a/datastore/filesystem_datastore.nim +++ b/datastore/filesystem_datastore.nim @@ -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 diff --git a/datastore/key.nim b/datastore/key.nim index 1203d75..70b317c 100644 --- a/datastore/key.nim +++ b/datastore/key.nim @@ -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 diff --git a/datastore/null_datastore.nim b/datastore/null_datastore.nim index c98eabc..66d4082 100644 --- a/datastore/null_datastore.nim +++ b/datastore/null_datastore.nim @@ -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 diff --git a/datastore/sqlite_datastore.nim b/datastore/sqlite_datastore.nim index 789ece4..40d3535 100644 --- a/datastore/sqlite_datastore.nim +++ b/datastore/sqlite_datastore.nim @@ -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 diff --git a/datastore/tiered_datastore.nim b/datastore/tiered_datastore.nim index 2814ed9..6b2b50a 100644 --- a/datastore/tiered_datastore.nim +++ b/datastore/tiered_datastore.nim @@ -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 diff --git a/tests/datastore/templates.nim b/tests/datastore/templates.nim new file mode 100644 index 0000000..9847e1f --- /dev/null +++ b/tests/datastore/templates.nim @@ -0,0 +1 @@ +template asyncTest*(name, body: untyped) = test(name, body) diff --git a/tests/datastore/test_datastore.nim b/tests/datastore/test_datastore.nim index 1e217c5..1ca3ef5 100644 --- a/tests/datastore/test_datastore.nim +++ b/tests/datastore/test_datastore.nim @@ -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(...) diff --git a/tests/datastore/test_filesystem_datastore.nim b/tests/datastore/test_filesystem_datastore.nim index 35079d0..9e037ae 100644 --- a/tests/datastore/test_filesystem_datastore.nim +++ b/tests/datastore/test_filesystem_datastore.nim @@ -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 diff --git a/tests/datastore/test_null_datastore.nim b/tests/datastore/test_null_datastore.nim index 5452476..2b65878 100644 --- a/tests/datastore/test_null_datastore.nim +++ b/tests/datastore/test_null_datastore.nim @@ -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 diff --git a/tests/datastore/test_sqlite_datastore.nim b/tests/datastore/test_sqlite_datastore.nim index a50410d..ce45fb7 100644 --- a/tests/datastore/test_sqlite_datastore.nim +++ b/tests/datastore/test_sqlite_datastore.nim @@ -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 diff --git a/tests/datastore/test_tiered_datastore.nim b/tests/datastore/test_tiered_datastore.nim index 781360e..85bd7a5 100644 --- a/tests/datastore/test_tiered_datastore.nim +++ b/tests/datastore/test_tiered_datastore.nim @@ -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