mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-05 23:43:07 +00:00
test(autosharding): Functional Tests (#2318)
* Implement autosharding tests.
This commit is contained in:
parent
7708082809
commit
09129f56ab
28
tests/testlib/tables.nim
Normal file
28
tests/testlib/tables.nim
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import
|
||||||
|
std/[
|
||||||
|
tables,
|
||||||
|
sequtils,
|
||||||
|
options
|
||||||
|
]
|
||||||
|
|
||||||
|
import
|
||||||
|
../../../waku/waku_core/topics,
|
||||||
|
../testlib/wakucore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc `==`*(table: Table[pubsub_topic.NsPubsubTopic, seq[NsContentTopic]], other: array[0..0, (string, seq[string])]): bool =
|
||||||
|
let otherTyped = other.map(
|
||||||
|
proc(item: (string, seq[string])): (NsPubsubTopic, seq[NsContentTopic]) =
|
||||||
|
let
|
||||||
|
(pubsubTopic, contentTopics) = item
|
||||||
|
nsPubsubTopic = NsPubsubTopic.parse(pubsubTopic).value()
|
||||||
|
nsContentTopics = contentTopics.map(
|
||||||
|
proc(contentTopic: string): NsContentTopic = NsContentTopic.parse(contentTopic).value()
|
||||||
|
)
|
||||||
|
return (nsPubsubTopic, nsContentTopics)
|
||||||
|
)
|
||||||
|
|
||||||
|
table == otherTyped.toTable()
|
||||||
@ -1,145 +0,0 @@
|
|||||||
{.used.}
|
|
||||||
|
|
||||||
import
|
|
||||||
std/options,
|
|
||||||
std/strutils,
|
|
||||||
std/sugar,
|
|
||||||
std/random,
|
|
||||||
stew/results,
|
|
||||||
testutils/unittests
|
|
||||||
import
|
|
||||||
../../../waku/waku_core/topics
|
|
||||||
|
|
||||||
suite "Waku Sharding":
|
|
||||||
|
|
||||||
randomize()
|
|
||||||
|
|
||||||
const WordLength = 5
|
|
||||||
|
|
||||||
proc randomContentTopic(): NsContentTopic =
|
|
||||||
var app = ""
|
|
||||||
|
|
||||||
for n in 0..<WordLength:
|
|
||||||
let letter = sample(Letters)
|
|
||||||
app.add(letter)
|
|
||||||
|
|
||||||
let version = "1"
|
|
||||||
|
|
||||||
var name = ""
|
|
||||||
|
|
||||||
for n in 0..<WordLength:
|
|
||||||
let letter = sample(Letters)
|
|
||||||
name.add(letter)
|
|
||||||
|
|
||||||
let enc = "cbor"
|
|
||||||
|
|
||||||
NsContentTopic.init(none(int), app, version, name, enc)
|
|
||||||
|
|
||||||
test "Implicit content topic generation":
|
|
||||||
## Given
|
|
||||||
let topic = "/toychat/2/huilong/proto"
|
|
||||||
|
|
||||||
## When
|
|
||||||
let parseRes = NsContentTopic.parse(topic)
|
|
||||||
|
|
||||||
## Then
|
|
||||||
assert parseRes.isOk(), $parseRes.error
|
|
||||||
|
|
||||||
let nsTopic = parseRes.get()
|
|
||||||
check:
|
|
||||||
nsTopic.generation == none(int)
|
|
||||||
|
|
||||||
test "Valid content topic":
|
|
||||||
## Given
|
|
||||||
let topic = "/0/toychat/2/huilong/proto"
|
|
||||||
|
|
||||||
## When
|
|
||||||
let parseRes = NsContentTopic.parse(topic)
|
|
||||||
|
|
||||||
## Then
|
|
||||||
assert parseRes.isOk(), $parseRes.error
|
|
||||||
|
|
||||||
let nsTopic = parseRes.get()
|
|
||||||
check:
|
|
||||||
nsTopic.generation.get() == 0
|
|
||||||
|
|
||||||
test "Invalid content topic generation":
|
|
||||||
## Given
|
|
||||||
let topic = "/1/toychat/2/huilong/proto"
|
|
||||||
|
|
||||||
## When
|
|
||||||
let ns = NsContentTopic.parse(topic).expect("Parsing")
|
|
||||||
|
|
||||||
let shardRes = getShard(ns)
|
|
||||||
|
|
||||||
## Then
|
|
||||||
assert shardRes.isErr(), $shardRes.get()
|
|
||||||
|
|
||||||
let err = shardRes.error
|
|
||||||
check:
|
|
||||||
err == "Generation > 0 are not supported yet"
|
|
||||||
|
|
||||||
#[ test "Sorted shard list":
|
|
||||||
## Given
|
|
||||||
let topic = "/0/toychat/2/huilong/proto"
|
|
||||||
|
|
||||||
## When
|
|
||||||
let contentTopic = NsContentTopic.parse(topic).expect("Parsing")
|
|
||||||
let count = shardCount(contentTopic).expect("Valid parameters")
|
|
||||||
let weights = repeat(1.0, count)
|
|
||||||
|
|
||||||
let shardsRes = weightedShardList(contentTopic, count, weights)
|
|
||||||
|
|
||||||
## Then
|
|
||||||
assert shardsRes.isOk(), shardsRes.error
|
|
||||||
|
|
||||||
let shards = shardsRes.get()
|
|
||||||
check:
|
|
||||||
shards.len == count
|
|
||||||
isSorted(shards, hashOrder) ]#
|
|
||||||
|
|
||||||
test "Shard Choice Reproducibility":
|
|
||||||
## Given
|
|
||||||
let topic = "/toychat/2/huilong/proto"
|
|
||||||
|
|
||||||
## When
|
|
||||||
let contentTopic = NsContentTopic.parse(topic).expect("Parsing")
|
|
||||||
|
|
||||||
let pubsub = getGenZeroShard(contentTopic, GenerationZeroShardsCount)
|
|
||||||
|
|
||||||
## Then
|
|
||||||
check:
|
|
||||||
pubsub == NsPubsubTopic.staticSharding(ClusterId, 3)
|
|
||||||
|
|
||||||
test "Shard Choice Simulation":
|
|
||||||
## Given
|
|
||||||
let topics = collect:
|
|
||||||
for i in 0..<100000:
|
|
||||||
randomContentTopic()
|
|
||||||
|
|
||||||
var counts = newSeq[0](GenerationZeroShardsCount)
|
|
||||||
|
|
||||||
## When
|
|
||||||
for topic in topics:
|
|
||||||
let pubsub = getShard(topic).expect("Valid Topic")
|
|
||||||
counts[pubsub.shard] += 1
|
|
||||||
|
|
||||||
## Then
|
|
||||||
for i in 1..<GenerationZeroShardsCount:
|
|
||||||
check:
|
|
||||||
float64(counts[i - 1]) <= (float64(counts[i]) * 1.05)
|
|
||||||
float64(counts[i]) <= (float64(counts[i - 1]) * 1.05)
|
|
||||||
float64(counts[i - 1]) >= (float64(counts[i]) * 0.95)
|
|
||||||
float64(counts[i]) >= (float64(counts[i - 1]) * 0.95)
|
|
||||||
|
|
||||||
#echo counts
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
153
tests/waku_core/topics/test_sharding.nim
Normal file
153
tests/waku_core/topics/test_sharding.nim
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import
|
||||||
|
std/[
|
||||||
|
options,
|
||||||
|
tables
|
||||||
|
],
|
||||||
|
testutils/unittests
|
||||||
|
|
||||||
|
|
||||||
|
import
|
||||||
|
../../../../waku/waku_core/topics,
|
||||||
|
../../testlib/[
|
||||||
|
wakucore,
|
||||||
|
tables,
|
||||||
|
testutils
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
suite "Autosharding":
|
||||||
|
const
|
||||||
|
pubsubTopic04 = "/waku/2/rs/0/4"
|
||||||
|
pubsubTopic13 = "/waku/2/rs/1/3"
|
||||||
|
contentTopicShort = "/toychat/2/huilong/proto"
|
||||||
|
contentTopicFull = "/0/toychat/2/huilong/proto"
|
||||||
|
contentTopicInvalid = "/1/toychat/2/huilong/proto"
|
||||||
|
|
||||||
|
|
||||||
|
suite "getGenZeroShard":
|
||||||
|
test "Generate Gen0 Shard":
|
||||||
|
# Given two valid topics
|
||||||
|
let
|
||||||
|
nsContentTopic1 = NsContentTopic.parse(contentTopicShort).value()
|
||||||
|
nsContentTopic2 = NsContentTopic.parse(contentTopicFull).value()
|
||||||
|
|
||||||
|
# When we generate a gen0 shard from them
|
||||||
|
let
|
||||||
|
nsPubsubTopic1 = getGenZeroShard(nsContentTopic1, GenerationZeroShardsCount)
|
||||||
|
nsPubsubTopic2 = getGenZeroShard(nsContentTopic2, GenerationZeroShardsCount)
|
||||||
|
|
||||||
|
# Then the generated shards are valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic1 == NsPubsubTopic.staticSharding(ClusterId, 3)
|
||||||
|
nsPubsubTopic2 == NsPubsubTopic.staticSharding(ClusterId, 3)
|
||||||
|
|
||||||
|
suite "getShard from NsContentTopic":
|
||||||
|
test "Generate Gen0 Shard with topic.generation==none":
|
||||||
|
# When we get a shard from a topic without generation
|
||||||
|
let nsPubsubTopic = getShard(contentTopicShort)
|
||||||
|
|
||||||
|
# Then the generated shard is valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic.value() == NsPubsubTopic.staticSharding(ClusterId, 3)
|
||||||
|
|
||||||
|
test "Generate Gen0 Shard with topic.generation==0":
|
||||||
|
# When we get a shard from a gen0 topic
|
||||||
|
let nsPubsubTopic = getShard(contentTopicFull)
|
||||||
|
|
||||||
|
# Then the generated shard is valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic.value() == NsPubsubTopic.staticSharding(ClusterId, 3)
|
||||||
|
|
||||||
|
test "Generate Gen0 Shard with topic.generation==other":
|
||||||
|
# When we get a shard from ain invalid content topic
|
||||||
|
let nsPubsubTopic = getShard(contentTopicInvalid)
|
||||||
|
|
||||||
|
# Then the generated shard is valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic.error() == "Generation > 0 are not supported yet"
|
||||||
|
|
||||||
|
suite "getShard from ContentTopic":
|
||||||
|
test "Generate Gen0 Shard with topic.generation==none":
|
||||||
|
# When we get a shard from it
|
||||||
|
let nsPubsubTopic = getShard(contentTopicShort)
|
||||||
|
|
||||||
|
# Then the generated shard is valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic.value() == NsPubsubTopic.staticSharding(ClusterId, 3)
|
||||||
|
|
||||||
|
test "Generate Gen0 Shard with topic.generation==0":
|
||||||
|
# When we get a shard from it
|
||||||
|
let nsPubsubTopic = getShard(contentTopicFull)
|
||||||
|
|
||||||
|
# Then the generated shard is valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic.value() == NsPubsubTopic.staticSharding(ClusterId, 3)
|
||||||
|
|
||||||
|
test "Generate Gen0 Shard with topic.generation==other":
|
||||||
|
# When we get a shard from it
|
||||||
|
let nsPubsubTopic = getShard(contentTopicInvalid)
|
||||||
|
|
||||||
|
# Then the generated shard is valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic.error() == "Generation > 0 are not supported yet"
|
||||||
|
|
||||||
|
test "Generate Gen0 Shard invalid topic":
|
||||||
|
# When we get a shard from it
|
||||||
|
let nsPubsubTopic = getShard("invalid")
|
||||||
|
|
||||||
|
# Then the generated shard is valid
|
||||||
|
check:
|
||||||
|
nsPubsubTopic.error() == "invalid format: topic must start with slash"
|
||||||
|
|
||||||
|
suite "parseSharding":
|
||||||
|
test "contentTopics is ContentTopic":
|
||||||
|
# When calling with contentTopic as string
|
||||||
|
let topicMap = parseSharding(some(pubsubTopic04), contentTopicShort)
|
||||||
|
|
||||||
|
# Then the topicMap is valid
|
||||||
|
check:
|
||||||
|
topicMap.value() == {pubsubTopic04: @[contentTopicShort]}
|
||||||
|
|
||||||
|
test "contentTopics is seq[ContentTopic]":
|
||||||
|
# When calling with contentTopic as string seq
|
||||||
|
let topicMap = parseSharding(some(pubsubTopic04), @[contentTopicShort, "/0/foo/1/bar/proto"])
|
||||||
|
|
||||||
|
# Then the topicMap is valid
|
||||||
|
check:
|
||||||
|
topicMap.value() == {pubsubTopic04: @[contentTopicShort, "/0/foo/1/bar/proto"]}
|
||||||
|
|
||||||
|
test "pubsubTopic is none":
|
||||||
|
# When calling with pubsubTopic as none
|
||||||
|
let topicMap = parseSharding(PubsubTopic.none(), contentTopicShort)
|
||||||
|
|
||||||
|
# Then the topicMap is valid
|
||||||
|
check:
|
||||||
|
topicMap.value() == {pubsubTopic13: @[contentTopicShort]}
|
||||||
|
|
||||||
|
test "content parse error":
|
||||||
|
# When calling with pubsubTopic as none with invalid content
|
||||||
|
let topicMap = parseSharding(PubsubTopic.none(), "invalid")
|
||||||
|
|
||||||
|
# Then the topicMap is valid
|
||||||
|
check:
|
||||||
|
topicMap.error() == "Cannot parse content topic: invalid format: topic must start with slash"
|
||||||
|
|
||||||
|
test "pubsubTopic parse error":
|
||||||
|
# When calling with pubsubTopic as none with invalid content
|
||||||
|
let topicMap = parseSharding(some("invalid"), contentTopicShort)
|
||||||
|
|
||||||
|
# Then the topicMap is valid
|
||||||
|
check:
|
||||||
|
topicMap.error() == "Cannot parse pubsub topic: invalid format: must start with /waku/2"
|
||||||
|
|
||||||
|
test "pubsubTopic getShard error":
|
||||||
|
# When calling with pubsubTopic as none with invalid content
|
||||||
|
let topicMap = parseSharding(PubsubTopic.none(), contentTopicInvalid)
|
||||||
|
|
||||||
|
# Then the topicMap is valid
|
||||||
|
check:
|
||||||
|
topicMap.error() == "Cannot autoshard content topic: Generation > 0 are not supported yet"
|
||||||
|
|
||||||
|
xtest "catchable error on add to topicMap":
|
||||||
|
# TODO: Trigger a CatchableError or mock
|
||||||
|
discard
|
||||||
@ -84,7 +84,7 @@ proc parseSharding*(pubsubTopic: Option[PubsubTopic], contentTopics: ContentTopi
|
|||||||
if shardsRes.isErr():
|
if shardsRes.isErr():
|
||||||
return err("Cannot autoshard content topic: " & $shardsRes.error)
|
return err("Cannot autoshard content topic: " & $shardsRes.error)
|
||||||
else: shardsRes.get()
|
else: shardsRes.get()
|
||||||
|
|
||||||
if not topicMap.hasKey(pubsub):
|
if not topicMap.hasKey(pubsub):
|
||||||
topicMap[pubsub] = @[]
|
topicMap[pubsub] = @[]
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user