From e9d083b2786c95a0fc1def1b4f21f4d73ae56402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Cabeza=20Romero?= Date: Fri, 5 Jan 2024 14:49:04 +0100 Subject: [PATCH] test(peer-connection-managenent): Functional Tests (#2321) * Add simple mock mechanism. * Implement migrations tests. * Implement peer storage tests. * Add simple protobuf serialisation testcase. --- .../peer_store/test_migrations.nim | 56 +++++++++ .../peer_store/test_peer_storage.nim | 25 ++++ .../peer_store/test_waku_peer_storage.nim | 114 ++++++++++++++++++ tests/testlib/sequtils.nim | 3 + tests/testlib/simple_mock.nim | 50 ++++++++ 5 files changed, 248 insertions(+) create mode 100644 tests/node/peer_manager/peer_store/test_migrations.nim create mode 100644 tests/node/peer_manager/peer_store/test_peer_storage.nim create mode 100644 tests/node/peer_manager/peer_store/test_waku_peer_storage.nim create mode 100644 tests/testlib/simple_mock.nim diff --git a/tests/node/peer_manager/peer_store/test_migrations.nim b/tests/node/peer_manager/peer_store/test_migrations.nim new file mode 100644 index 000000000..3f782589a --- /dev/null +++ b/tests/node/peer_manager/peer_store/test_migrations.nim @@ -0,0 +1,56 @@ +import std/[options], stew/results, testutils/unittests + +import + ../../../../waku/[node/peer_manager/peer_store/migrations], + ../../waku_archive/archive_utils, + ../../testlib/[simple_mock] + +import std/[tables, strutils, os], stew/results, chronicles + +import ../../../../waku/[common/databases/db_sqlite, common/databases/common] + +suite "Migrations": + test "migrate ok": + # Given the db_sqlite.migrate function returns ok + let backup = db_sqlite.migrate + mock(db_sqlite.migrate): + proc mockedMigrate( + db: SqliteDatabase, targetVersion: int64, migrationsScriptsDir: string + ): DatabaseResult[void] = + ok() + + mockedMigrate + + # When we call the migrate function + let migrationResult = migrations.migrate(newSqliteDatabase(), 1) + + # Then we expect the result to be ok + check: + migrationResult == DatabaseResult[void].ok() + + # Cleanup + mock(db_sqlite.migrate): + backup + + test "migrate error": + # Given the db_sqlite.migrate function returns an error + let backup = db_sqlite.migrate + mock(db_sqlite.migrate): + proc mockedMigrate( + db: SqliteDatabase, targetVersion: int64, migrationsScriptsDir: string + ): DatabaseResult[void] = + err("mock error") + + mockedMigrate + + # When we call the migrate function + let migrationResult = migrations.migrate(newSqliteDatabase(), 1) + + # Then we expect the result to be an error + check: + migrationResult == + DatabaseResult[void].err("failed to execute migration scripts: mock error") + + # Cleanup + mock(db_sqlite.migrate): + backup diff --git a/tests/node/peer_manager/peer_store/test_peer_storage.nim b/tests/node/peer_manager/peer_store/test_peer_storage.nim new file mode 100644 index 000000000..9c77cb912 --- /dev/null +++ b/tests/node/peer_manager/peer_store/test_peer_storage.nim @@ -0,0 +1,25 @@ +import stew/results, testutils/unittests + +import + ../../../../waku/node/peer_manager/peer_store/peer_storage, + ../../../../waku/waku_core/peers + +suite "PeerStorage": + var peerStorage {.threadvar.}: PeerStorage + + setup: + peerStorage = PeerStorage() + + suite "put": + test "unimplemented": + check: + peerStorage.put(nil) == PeerStorageResult[void].err("Unimplemented") + + suite "getAll": + test "unimplemented": + let + emptyClosure = + proc(remotePeerInfo: RemotePeerInfo) = + discard + check: + peerStorage.getAll(emptyClosure) == PeerStorageResult[void].err("Unimplemented") diff --git a/tests/node/peer_manager/peer_store/test_waku_peer_storage.nim b/tests/node/peer_manager/peer_store/test_waku_peer_storage.nim new file mode 100644 index 000000000..466ed0a0a --- /dev/null +++ b/tests/node/peer_manager/peer_store/test_waku_peer_storage.nim @@ -0,0 +1,114 @@ +import + std/[nativesockets, options, sequtils], + testutils/unittests, + libp2p/[multiaddress, peerid], + libp2p/crypto/crypto, + stew/shims/net, + eth/keys, + eth/p2p/discoveryv5/enr, + nimcrypto/utils + +import + ../../../../waku/waku_core/peers, + ../../../../waku/node/peer_manager/peer_store/waku_peer_storage + +proc `==`(a, b: RemotePeerInfo): bool = + let + comparisons = + @[ + a.peerId == b.peerId, + a.addrs == b.addrs, + a.enr == b.enr, + a.protocols == b.protocols, + a.agent == b.agent, + a.protoVersion == b.protoVersion, + a.publicKey == b.publicKey, + a.connectedness == b.connectedness, + a.disconnectTime == b.disconnectTime, + a.origin == b.origin, + a.direction == b.direction, + a.lastFailedConn == b.lastFailedConn, + a.numberFailedConn == b.numberFailedConn + ] + + allIt(comparisons, it == true) + +suite "Protobuf Serialisation": + let + privateKeyStr = + "08031279307702010104203E5B1FE9712E6C314942A750BD67485DE3C1EFE85B1BFB520AE8F9AE3DFA4A4CA00A06082A8648CE3D030107A14403420004DE3D300FA36AE0E8F5D530899D83ABAB44ABF3161F162A4BC901D8E6ECDA020E8B6D5F8DA30525E71D6851510C098E5C47C646A597FB4DCEC034E9F77C409E62" + publicKeyStr = + "0803125b3059301306072a8648ce3d020106082a8648ce3d03010703420004de3d300fa36ae0e8f5d530899d83abab44abf3161f162a4bc901d8e6ecda020e8b6d5f8da30525e71d6851510c098e5c47c646a597fb4dcec034e9f77c409e62" + + var remotePeerInfo {.threadvar.}: RemotePeerInfo + + setup: + let + port = Port(8080) + ipAddress = IpAddress(family: IPv4, address_v4: [192, 168, 0, 1]) + multiAddress: MultiAddress = + MultiAddress.init(ipAddress, IpTransportProtocol.tcpProtocol, port) + encodedPeerIdStr = "16Uiu2HAmFccGe5iezmyRDQZuLPRP7FqpqXLjnocmMRk18pmTZs2j" + + var peerId: PeerID + assert init(peerId, encodedPeerIdStr) + + let + publicKey = + crypto.PublicKey.init(utils.fromHex(publicKeyStr)).expect("public key") + privateKey = + crypto.PrivateKey.init(utils.fromHex(privateKeyStr)).expect("private key") + + remotePeerInfo = RemotePeerInfo.init(peerId, @[multiAddress]) + remotePeerInfo.publicKey = publicKey + + suite "encode": + test "simple": + # Given the expected bytes representation of a valid RemotePeerInfo + let + expectedBuffer: seq[byte] = + @[ + 10, 39, 0, 37, 8, 2, 18, 33, 3, 43, 246, 238, 219, 109, 147, 79, 129, 40, + 145, 217, 209, 109, 105, 185, 186, 200, 180, 203, 72, 166, 220, 196, 232, + 170, 74, 141, 125, 255, 112, 238, 204, 18, 8, 4, 192, 168, 0, 1, 6, 31, 144, + 34, 95, 8, 3, 18, 91, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, + 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 222, 61, 48, 15, 163, 106, + 224, 232, 245, 213, 48, 137, 157, 131, 171, 171, 68, 171, 243, 22, 31, 22, + 42, 75, 201, 1, 216, 230, 236, 218, 2, 14, 139, 109, 95, 141, 163, 5, 37, + 231, 29, 104, 81, 81, 12, 9, 142, 92, 71, 198, 70, 165, 151, 251, 77, 206, + 192, 52, 233, 247, 124, 64, 158, 98, 40, 0, 48, 0 + ] + + # When converting a valid RemotePeerInfo to a ProtoBuffer + let encodedRemotePeerInfo = encode(remotePeerInfo).get() + + # Then the encoded RemotePeerInfo should be equal to the expected bytes + check: + encodedRemotePeerInfo.buffer == expectedBuffer + encodedRemotePeerInfo.offset == 152 + encodedRemotePeerInfo.length == 0 + encodedRemotePeerInfo.maxSize == 4194304 + + suite "decode": + test "simple": + # Given the bytes representation of a valid RemotePeerInfo + let + buffer: seq[byte] = + @[ + 10, 39, 0, 37, 8, 2, 18, 33, 3, 43, 246, 238, 219, 109, 147, 79, 129, 40, + 145, 217, 209, 109, 105, 185, 186, 200, 180, 203, 72, 166, 220, 196, 232, + 170, 74, 141, 125, 255, 112, 238, 204, 18, 8, 4, 192, 168, 0, 1, 6, 31, 144, + 34, 95, 8, 3, 18, 91, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, + 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 222, 61, 48, 15, 163, 106, + 224, 232, 245, 213, 48, 137, 157, 131, 171, 171, 68, 171, 243, 22, 31, 22, + 42, 75, 201, 1, 216, 230, 236, 218, 2, 14, 139, 109, 95, 141, 163, 5, 37, + 231, 29, 104, 81, 81, 12, 9, 142, 92, 71, 198, 70, 165, 151, 251, 77, 206, + 192, 52, 233, 247, 124, 64, 158, 98, 40, 0, 48, 0 + ] + + # When converting a valid buffer to RemotePeerInfo + let decodedRemotePeerInfo = RemotePeerInfo.decode(buffer).get() + + # Then the decoded RemotePeerInfo should be equal to the original RemotePeerInfo + check: + decodedRemotePeerInfo == remotePeerInfo diff --git a/tests/testlib/sequtils.nim b/tests/testlib/sequtils.nim index 5fd3d414f..b47ba75f5 100644 --- a/tests/testlib/sequtils.nim +++ b/tests/testlib/sequtils.nim @@ -1,2 +1,5 @@ proc toString*(bytes: seq[byte]): string = cast[string](bytes) + +proc toBytes*(str: string): seq[byte] = + cast[seq[byte]](str) diff --git a/tests/testlib/simple_mock.nim b/tests/testlib/simple_mock.nim new file mode 100644 index 000000000..62a385574 --- /dev/null +++ b/tests/testlib/simple_mock.nim @@ -0,0 +1,50 @@ +# Sourced from https://forum.nim-lang.org/t/9255#60617 + +import posix + +type + Instr {.union.} = object + bytes: array[8, byte] + value: uint64 + +proc mockImpl(target, replacement: pointer) = + # YOLO who needs alignment + #doAssert (cast[ByteAddress](target) and ByteAddress(0x07)) == 0 + var page = cast[pointer](cast[ByteAddress](target) and (not 0xfff)) + doAssert mprotect(page, 4096, PROT_WRITE or PROT_EXEC) == 0 + let rel = cast[ByteAddress](replacement) - cast[ByteAddress](target) - 5 + var + instr = + Instr( + bytes: [ + 0xe9.byte, + (rel shr 0).byte, + (rel shr 8).byte, + (rel shr 16).byte, + (rel shr 24).byte, + 0, + 0, + 0 + ] + ) + cast[ptr uint64](target)[] = instr.value + doAssert mprotect(page, 4096, PROT_EXEC) == 0 + +# Note: Requires manual cleanup +# Usage Example: +# proc helloWorld(): string = +# "Hello, World!" +# +# echo helloWorld() # "Hello, World!" +# +# let backup = helloWorld +# mock(helloWorld): +# proc mockedHellWorld(): string = +# "Mocked Hello, World!" +# mockedMigrate +# +# echo helloWorld() # "Mocked Hello, World!" +# +# helloWorld = backup # Restore the original function +template mock*(target, replacement: untyped): untyped = + mockImpl(cast[pointer](target), cast[pointer](replacement))