mirror of
https://github.com/logos-storage/logos-storage-network-crawler.git
synced 2026-01-02 13:33:08 +00:00
277 lines
7.1 KiB
Nim
277 lines
7.1 KiB
Nim
import std/os
|
|
import pkg/chronos
|
|
import pkg/questionable/results
|
|
import pkg/asynctest/chronos/unittest
|
|
import pkg/datastore/typedds
|
|
|
|
import ../../../codexcrawler/components/nodestore
|
|
import ../../../codexcrawler/utils/datastoreutils
|
|
import ../../../codexcrawler/utils/asyncdataevent
|
|
import ../../../codexcrawler/types
|
|
import ../../../codexcrawler/state
|
|
import ../mocks/mockstate
|
|
import ../mocks/mockclock
|
|
import ../helpers
|
|
|
|
suite "Nodestore":
|
|
let
|
|
dsPath = getTempDir() / "testds"
|
|
nodestoreName = "nodestore"
|
|
|
|
var
|
|
ds: TypedDatastore
|
|
state: MockState
|
|
clock: MockClock
|
|
store: NodeStore
|
|
|
|
setup:
|
|
ds = createTypedDatastore(dsPath).tryGet()
|
|
state = createMockState()
|
|
clock = createMockClock()
|
|
|
|
store = NodeStore.new(state, ds, clock)
|
|
(await store.start()).tryGet()
|
|
|
|
teardown:
|
|
(await store.stop()).tryGet()
|
|
(await ds.close()).tryGet()
|
|
state.checkAllUnsubscribed()
|
|
removeDir(dsPath)
|
|
|
|
proc fireNodeFoundEvent(nids: seq[Nid]) {.async: (raises: []).} =
|
|
try:
|
|
(await state.events.nodesFound.fire(nids)).tryGet()
|
|
except CatchableError:
|
|
raiseAssert("CatchableError in fireNodeFoundEvent")
|
|
|
|
proc fireCheckEvent(nid: Nid, isOk: bool) {.async: (raises: []).} =
|
|
try:
|
|
(await state.events.dhtNodeCheck.fire(DhtNodeCheckEventData(id: nid, isOk: isOk))).tryGet()
|
|
except CatchableError:
|
|
raiseAssert("CatchableError in fireCheckEvent")
|
|
|
|
test "nodeEntry encoding":
|
|
let entry =
|
|
NodeEntry(id: genNid(), lastVisit: 123.uint64, firstInactive: 234.uint64)
|
|
|
|
let
|
|
bytes = entry.encode()
|
|
decoded = NodeEntry.decode(bytes).tryGet()
|
|
|
|
check:
|
|
entry.id == decoded.id
|
|
entry.lastVisit == decoded.lastVisit
|
|
entry.firstInactive == decoded.firstInactive
|
|
|
|
test "nodesFound event should store nodes":
|
|
let
|
|
nid = genNid()
|
|
expectedKey = Key.init(nodestoreName / $nid).tryGet()
|
|
|
|
await fireNodeFoundEvent(@[nid])
|
|
|
|
check:
|
|
(await ds.has(expectedKey)).tryGet()
|
|
|
|
let entry = (await get[NodeEntry](ds, expectedKey)).tryGet()
|
|
check:
|
|
entry.id == nid
|
|
|
|
test "nodesFound event should fire newNodesDiscovered":
|
|
var newNodes = newSeq[Nid]()
|
|
proc onNewNodes(
|
|
nids: seq[Nid]
|
|
): Future[?!void] {.async: (raises: [CancelledError]).} =
|
|
newNodes = nids
|
|
return success()
|
|
|
|
let
|
|
sub = state.events.newNodesDiscovered.subscribe(onNewNodes)
|
|
nid = genNid()
|
|
|
|
await fireNodeFoundEvent(@[nid])
|
|
|
|
check:
|
|
newNodes == @[nid]
|
|
|
|
await state.events.newNodesDiscovered.unsubscribe(sub)
|
|
|
|
test "nodesFound event should not fire newNodesDiscovered for previously seen nodes":
|
|
let nid = genNid()
|
|
|
|
# Make nid known first. Then subscribe.
|
|
await fireNodeFoundEvent(@[nid])
|
|
|
|
var
|
|
newNodes = newSeq[Nid]()
|
|
count = 0
|
|
proc onNewNodes(
|
|
nids: seq[Nid]
|
|
): Future[?!void] {.async: (raises: [CancelledError]).} =
|
|
newNodes = nids
|
|
inc count
|
|
return success()
|
|
|
|
let sub = state.events.newNodesDiscovered.subscribe(onNewNodes)
|
|
|
|
# Firing the event again should not trigger newNodesDiscovered for nid
|
|
await fireNodeFoundEvent(@[nid])
|
|
|
|
check:
|
|
newNodes.len == 0
|
|
count == 0
|
|
|
|
await state.events.newNodesDiscovered.unsubscribe(sub)
|
|
|
|
test "iterateAll yields all known nids":
|
|
let
|
|
nid1 = genNid()
|
|
nid2 = genNid()
|
|
nid3 = genNid()
|
|
|
|
await fireNodeFoundEvent(@[nid1, nid2, nid3])
|
|
|
|
var iterNodes = newSeq[Nid]()
|
|
proc onNode(entry: NodeEntry): Future[?!void] {.async: (raises: []), gcsafe.} =
|
|
iterNodes.add(entry.id)
|
|
return success()
|
|
|
|
(await store.iterateAll(onNode)).tryGet()
|
|
|
|
check:
|
|
nid1 in iterNodes
|
|
nid2 in iterNodes
|
|
nid3 in iterNodes
|
|
|
|
test "iterateAll yields no uninitialized entries":
|
|
let
|
|
nid1 = genNid()
|
|
nid2 = genNid()
|
|
nid3 = genNid()
|
|
|
|
await fireNodeFoundEvent(@[nid1, nid2, nid3])
|
|
|
|
var iterNodes = newSeq[Nid]()
|
|
proc onNode(entry: NodeEntry): Future[?!void] {.async: (raises: []), gcsafe.} =
|
|
iterNodes.add(entry.id)
|
|
return success()
|
|
|
|
(await store.iterateAll(onNode)).tryGet()
|
|
|
|
for nid in iterNodes:
|
|
check:
|
|
$nid != "0"
|
|
|
|
test "deleteEntries deletes entries":
|
|
let
|
|
nid1 = genNid()
|
|
nid2 = genNid()
|
|
nid3 = genNid()
|
|
|
|
await fireNodeFoundEvent(@[nid1, nid2, nid3])
|
|
(await store.deleteEntries(@[nid1, nid2])).tryGet()
|
|
|
|
var iterNodes = newSeq[Nid]()
|
|
proc onNode(entry: NodeEntry): Future[?!void] {.async: (raises: []), gcsafe.} =
|
|
iterNodes.add(entry.id)
|
|
return success()
|
|
|
|
(await store.iterateAll(onNode)).tryGet()
|
|
|
|
check:
|
|
nid1 notin iterNodes
|
|
nid2 notin iterNodes
|
|
nid3 in iterNodes
|
|
|
|
test "deleteEntries fires nodesDeleted event":
|
|
var deletedNodes = newSeq[Nid]()
|
|
proc onDeleted(
|
|
nids: seq[Nid]
|
|
): Future[?!void] {.async: (raises: [CancelledError]).} =
|
|
deletedNodes = nids
|
|
return success()
|
|
|
|
let
|
|
sub = state.events.nodesDeleted.subscribe(onDeleted)
|
|
nid1 = genNid()
|
|
nid2 = genNid()
|
|
nid3 = genNid()
|
|
|
|
await fireNodeFoundEvent(@[nid1, nid2, nid3])
|
|
(await store.deleteEntries(@[nid1, nid2])).tryGet()
|
|
|
|
check:
|
|
nid1 in deletedNodes
|
|
nid2 in deletedNodes
|
|
nid3 notin deletedNodes
|
|
|
|
await state.events.nodesDeleted.unsubscribe(sub)
|
|
|
|
test "dhtNodeCheck event should update lastVisit":
|
|
let
|
|
nid = genNid()
|
|
expectedKey = Key.init(nodestoreName / $nid).tryGet()
|
|
|
|
clock.setNow = 123456789.uint64
|
|
|
|
await fireNodeFoundEvent(@[nid])
|
|
|
|
let originalEntry = (await get[NodeEntry](ds, expectedKey)).tryGet()
|
|
check:
|
|
originalEntry.lastVisit == 0
|
|
|
|
await fireCheckEvent(nid, true)
|
|
|
|
let updatedEntry = (await get[NodeEntry](ds, expectedKey)).tryGet()
|
|
check:
|
|
clock.setNow == updatedEntry.lastVisit
|
|
|
|
test "failed dhtNodeCheck event should set firstInactive":
|
|
let
|
|
nid = genNid()
|
|
expectedKey = Key.init(nodestoreName / $nid).tryGet()
|
|
|
|
clock.setNow = 345345.uint64
|
|
|
|
await fireNodeFoundEvent(@[nid])
|
|
await fireCheckEvent(nid, false)
|
|
|
|
let updatedEntry = (await get[NodeEntry](ds, expectedKey)).tryGet()
|
|
check:
|
|
clock.setNow == updatedEntry.firstInactive
|
|
|
|
test "successful dhtNodeCheck event should clear firstInactive":
|
|
let
|
|
nid = genNid()
|
|
expectedKey = Key.init(nodestoreName / $nid).tryGet()
|
|
|
|
clock.setNow = 456456.uint64
|
|
|
|
await fireNodeFoundEvent(@[nid])
|
|
await fireCheckEvent(nid, false)
|
|
await fireCheckEvent(nid, true)
|
|
|
|
let updatedEntry = (await get[NodeEntry](ds, expectedKey)).tryGet()
|
|
check:
|
|
updatedEntry.firstInactive == 0
|
|
|
|
test "dhtNodeCheck event for non-existing node should fire nodesDeleted":
|
|
var deletedNodes = newSeq[Nid]()
|
|
proc onDeleted(
|
|
nids: seq[Nid]
|
|
): Future[?!void] {.async: (raises: [CancelledError]).} =
|
|
deletedNodes = nids
|
|
return success()
|
|
|
|
let
|
|
sub = state.events.nodesDeleted.subscribe(onDeleted)
|
|
nid = genNid()
|
|
|
|
# We don't fire nodeFound first. So the store doesn't know it exists.
|
|
await fireCheckEvent(nid, true)
|
|
|
|
check:
|
|
nid in deletedNodes
|
|
|
|
await state.events.nodesDeleted.unsubscribe(sub)
|