test(autosharding): Functional Tests (#2318)

* Implement autosharding tests.
This commit is contained in:
Álex Cabeza Romero 2024-01-04 16:26:27 +01:00 committed by GitHub
parent b0c780528b
commit 3bbe234962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 182 additions and 146 deletions

28
tests/testlib/tables.nim Normal file
View 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()

View File

@ -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

View 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

View File

@ -84,7 +84,7 @@ proc parseSharding*(pubsubTopic: Option[PubsubTopic], contentTopics: ContentTopi
if shardsRes.isErr():
return err("Cannot autoshard content topic: " & $shardsRes.error)
else: shardsRes.get()
if not topicMap.hasKey(pubsub):
topicMap[pubsub] = @[]