introduce interface for better mocking

As one could fall in a trap of not defining the right methods on the mock
This commit is contained in:
fryorcraken 2025-07-21 14:47:07 +10:00
parent 1e7e0291fa
commit d4429702c2
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
6 changed files with 33 additions and 29 deletions

View File

@ -15,7 +15,7 @@ import { ConnectionManager } from "./connection_manager.js";
import { DiscoveryDialer } from "./discovery_dialer.js"; import { DiscoveryDialer } from "./discovery_dialer.js";
import { KeepAliveManager } from "./keep_alive_manager.js"; import { KeepAliveManager } from "./keep_alive_manager.js";
import { NetworkMonitor } from "./network_monitor.js"; import { NetworkMonitor } from "./network_monitor.js";
import { ShardReader } from "./shard_reader.js"; import { IShardReader, ShardReader } from "./shard_reader.js";
describe("ConnectionManager", () => { describe("ConnectionManager", () => {
let libp2p: Libp2p; let libp2p: Libp2p;
@ -30,7 +30,7 @@ describe("ConnectionManager", () => {
// Mock internal components // Mock internal components
let mockKeepAliveManager: sinon.SinonStubbedInstance<KeepAliveManager>; let mockKeepAliveManager: sinon.SinonStubbedInstance<KeepAliveManager>;
let mockDiscoveryDialer: sinon.SinonStubbedInstance<DiscoveryDialer>; let mockDiscoveryDialer: sinon.SinonStubbedInstance<DiscoveryDialer>;
let mockShardReader: sinon.SinonStubbedInstance<ShardReader>; let mockShardReader: sinon.SinonStubbedInstance<IShardReader>;
let mockNetworkMonitor: sinon.SinonStubbedInstance<NetworkMonitor>; let mockNetworkMonitor: sinon.SinonStubbedInstance<NetworkMonitor>;
let mockConnectionLimiter: sinon.SinonStubbedInstance<ConnectionLimiter>; let mockConnectionLimiter: sinon.SinonStubbedInstance<ConnectionLimiter>;
@ -87,7 +87,7 @@ describe("ConnectionManager", () => {
mockShardReader = { mockShardReader = {
isPeerOnTopic: sinon.stub().resolves(true) isPeerOnTopic: sinon.stub().resolves(true)
} as unknown as sinon.SinonStubbedInstance<ShardReader>; } as unknown as sinon.SinonStubbedInstance<IShardReader>;
mockNetworkMonitor = { mockNetworkMonitor = {
start: sinon.stub(), start: sinon.stub(),

View File

@ -1,7 +1,6 @@
import { type Peer, type PeerId, type Stream } from "@libp2p/interface"; import { type Peer, type PeerId, type Stream } from "@libp2p/interface";
import { MultiaddrInput } from "@multiformats/multiaddr"; import { MultiaddrInput } from "@multiformats/multiaddr";
import { import {
ClusterId,
ConnectionManagerOptions, ConnectionManagerOptions,
IConnectionManager, IConnectionManager,
IRelay, IRelay,
@ -47,7 +46,7 @@ export class ConnectionManager implements IConnectionManager {
private readonly networkMonitor: NetworkMonitor; private readonly networkMonitor: NetworkMonitor;
private readonly connectionLimiter: ConnectionLimiter; private readonly connectionLimiter: ConnectionLimiter;
private options: ConnectionManagerOptions; private readonly options: ConnectionManagerOptions;
private libp2p: Libp2p; private libp2p: Libp2p;
public constructor(options: ConnectionManagerConstructorOptions) { public constructor(options: ConnectionManagerConstructorOptions) {
@ -200,9 +199,8 @@ export class ConnectionManager implements IConnectionManager {
public async isPeerOnShard( public async isPeerOnShard(
peerId: PeerId, peerId: PeerId,
clusterId: ClusterId,
shardId: ShardId shardId: ShardId
): Promise<boolean> { ): Promise<boolean> {
return this.shardReader.isPeerOnShard(peerId, clusterId, shardId); return this.shardReader.isPeerOnShard(peerId, shardId);
} }
} }

View File

@ -177,7 +177,6 @@ describe("ShardReader", function () {
const result = await shardReader.isPeerOnShard( const result = await shardReader.isPeerOnShard(
testPeerId, testPeerId,
testClusterId,
testShardIndex testShardIndex
); );
@ -192,9 +191,13 @@ describe("ShardReader", function () {
mockPeerStore.get.resolves(mockPeer); mockPeerStore.get.resolves(mockPeer);
const result = await shardReader.isPeerOnShard( const shardReaderCluster5 = new ShardReader({
libp2p: mockLibp2p as any,
networkConfig: { clusterId: 5 }
});
const result = await shardReaderCluster5.isPeerOnShard(
testPeerId, testPeerId,
5,
testShardIndex testShardIndex
); );
@ -211,7 +214,6 @@ describe("ShardReader", function () {
const result = await shardReader.isPeerOnShard( const result = await shardReader.isPeerOnShard(
testPeerId, testPeerId,
testClusterId,
testShardIndex + 100 testShardIndex + 100
); );
@ -223,7 +225,6 @@ describe("ShardReader", function () {
const result = await shardReader.isPeerOnShard( const result = await shardReader.isPeerOnShard(
testPeerId, testPeerId,
testClusterId,
testShardIndex testShardIndex
); );

View File

@ -20,7 +20,7 @@ type ShardReaderConstructorOptions = {
networkConfig: NetworkConfig; networkConfig: NetworkConfig;
}; };
interface IShardReader { export interface IShardReader {
hasShardInfo(id: PeerId): Promise<boolean>; hasShardInfo(id: PeerId): Promise<boolean>;
isPeerOnCluster(id: PeerId): Promise<boolean>; isPeerOnCluster(id: PeerId): Promise<boolean>;
isPeerOnShard( isPeerOnShard(
@ -66,7 +66,8 @@ export class ShardReader implements IShardReader {
): Promise<boolean> { ): Promise<boolean> {
try { try {
const { clusterId, shard } = pubsubTopicToSingleShardInfo(pubsubTopic); const { clusterId, shard } = pubsubTopicToSingleShardInfo(pubsubTopic);
return await this.isPeerOnShard(id, clusterId, shard); if (clusterId !== this.clusterId) return false;
return await this.isPeerOnShard(id, shard);
} catch (error) { } catch (error) {
log.error( log.error(
`Error comparing pubsub topic ${pubsubTopic} with shard info for ${id}`, `Error comparing pubsub topic ${pubsubTopic} with shard info for ${id}`,
@ -76,14 +77,10 @@ export class ShardReader implements IShardReader {
} }
} }
public async isPeerOnShard( public async isPeerOnShard(id: PeerId, shard: ShardId): Promise<boolean> {
id: PeerId,
clusterId: ClusterId,
shard: ShardId
): Promise<boolean> {
const peerShardInfo = await this.getRelayShards(id); const peerShardInfo = await this.getRelayShards(id);
log.info( log.info(
`Checking if peer on same shard: this { clusterId: ${clusterId}, shardId: ${shard} },` + `Checking if peer on same shard: this { clusterId: ${this.clusterId}, shardId: ${shard} },` +
`${id} { clusterId: ${peerShardInfo?.clusterId}, shards: ${peerShardInfo?.shards} }` `${id} { clusterId: ${peerShardInfo?.clusterId}, shards: ${peerShardInfo?.shards} }`
); );
if (!peerShardInfo) { if (!peerShardInfo) {
@ -91,7 +88,7 @@ export class ShardReader implements IShardReader {
} }
return ( return (
peerShardInfo.clusterId === clusterId && peerShardInfo.clusterId === this.clusterId &&
peerShardInfo.shards.includes(shard) peerShardInfo.shards.includes(shard)
); );
} }

View File

@ -1,6 +1,8 @@
import type { Peer, PeerId, Stream } from "@libp2p/interface"; import type { Peer, PeerId, Stream } from "@libp2p/interface";
import type { MultiaddrInput } from "@multiformats/multiaddr"; import type { MultiaddrInput } from "@multiformats/multiaddr";
import { ShardId } from "./sharding.js";
// Peer tags // Peer tags
export enum Tags { export enum Tags {
BOOTSTRAP = "bootstrap", BOOTSTRAP = "bootstrap",
@ -161,4 +163,14 @@ export interface IConnectionManager {
* @returns Promise resolving to true if the peer has shard info, false otherwise * @returns Promise resolving to true if the peer has shard info, false otherwise
*/ */
hasShardInfo(peerId: PeerId): Promise<boolean>; hasShardInfo(peerId: PeerId): Promise<boolean>;
/**
* Returns true if the passed peer is on the passed pubsub topic
*/
isPeerOnTopic(peerId: PeerId, pubsubTopic: string): Promise<boolean>;
/**
* Returns true if the passed peer is on the passed shard
*/
isPeerOnShard(peerId: PeerId, shardId: ShardId): Promise<boolean>;
} }

View File

@ -4,14 +4,10 @@ import {
PeerId, PeerId,
TypedEventEmitter TypedEventEmitter
} from "@libp2p/interface"; } from "@libp2p/interface";
import { import { FilterCodecs, LightPushCodec, StoreCodec } from "@waku/core";
ConnectionManager,
FilterCodecs,
LightPushCodec,
StoreCodec
} from "@waku/core";
import { import {
CONNECTION_LOCKED_TAG, CONNECTION_LOCKED_TAG,
type IConnectionManager,
Libp2p, Libp2p,
Libp2pEventHandler, Libp2pEventHandler,
Protocols Protocols
@ -29,7 +25,7 @@ type PeerManagerConfig = {
type PeerManagerParams = { type PeerManagerParams = {
libp2p: Libp2p; libp2p: Libp2p;
config?: PeerManagerConfig; config?: PeerManagerConfig;
connectionManager: ConnectionManager; connectionManager: IConnectionManager;
}; };
type GetPeersParams = { type GetPeersParams = {
@ -67,7 +63,7 @@ export class PeerManager {
private readonly numPeersToUse: number; private readonly numPeersToUse: number;
private readonly libp2p: Libp2p; private readonly libp2p: Libp2p;
private readonly connectionManager: ConnectionManager; private readonly connectionManager: IConnectionManager;
private readonly lockedPeers = new Set<string>(); private readonly lockedPeers = new Set<string>();
private readonly unlockedPeers = new Map<string, number>(); private readonly unlockedPeers = new Map<string, number>();