Skip to content

Commit c8fd4d0

Browse files
authored
test: allo & registry integration tests (#67)
# 🤖 Linear Closes GIT-263 GIT-264 ## Description We write integration tests for Allo and Registry events on Orchestrator level. We aim at verifying that Processors and DataLoader communication is working correctly. Although these are integration tests, we mock some components (mainly external ones and Repositories) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced the system’s event processing capabilities to improve responsiveness and accuracy in handling various operations. - **Bug Fixes** - Standardized event data handling to ensure consistent and reliable updates, leading to improved stability in profile and role management. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 088679d commit c8fd4d0

File tree

9 files changed

+1505
-6
lines changed

9 files changed

+1505
-6
lines changed

packages/data-flow/test/integration/allo.integration.spec.ts

Lines changed: 559 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { Address, Hex } from "viem";
2+
import { vi } from "vitest";
3+
4+
import type { IIndexerClient } from "@grants-stack-indexer/indexer-client";
5+
import type {
6+
IEventRegistryRepository,
7+
TransactionConnection,
8+
} from "@grants-stack-indexer/repository";
9+
import { EvmProvider } from "@grants-stack-indexer/chain-providers";
10+
import { DummyPricingProvider } from "@grants-stack-indexer/pricing";
11+
import { ChainId } from "@grants-stack-indexer/shared";
12+
13+
import { CoreDependencies, IStrategyRegistry } from "../../../src/internal.js";
14+
15+
// Mock repositories with Vi spies
16+
export const createMockRepositories = (): Pick<
17+
CoreDependencies,
18+
| "projectRepository"
19+
| "roundRepository"
20+
| "applicationRepository"
21+
| "donationRepository"
22+
| "applicationPayoutRepository"
23+
| "transactionManager"
24+
> => ({
25+
projectRepository: {
26+
getProjects: vi.fn(),
27+
insertProject: vi.fn(),
28+
updateProject: vi.fn(),
29+
insertProjectRole: vi.fn(),
30+
deleteManyProjectRoles: vi.fn(),
31+
insertPendingProjectRole: vi.fn(),
32+
deleteManyPendingProjectRoles: vi.fn(),
33+
getProjectById: vi.fn(),
34+
getProjectByIdOrThrow: vi.fn(),
35+
getPendingProjectRoles: vi.fn(),
36+
getPendingProjectRolesByRole: vi.fn(),
37+
getProjectByAnchor: vi.fn(),
38+
getProjectByAnchorOrThrow: vi.fn(),
39+
},
40+
roundRepository: {
41+
getRounds: vi.fn(),
42+
getRoundById: vi.fn(),
43+
insertRound: vi.fn(),
44+
updateRound: vi.fn(),
45+
incrementRoundFunds: vi.fn(),
46+
incrementRoundTotalDistributed: vi.fn(),
47+
insertRoundRole: vi.fn(),
48+
deleteManyRoundRolesByRoleAndAddress: vi.fn(),
49+
insertPendingRoundRole: vi.fn(),
50+
deleteManyPendingRoundRoles: vi.fn(),
51+
getRoundByIdOrThrow: vi.fn(),
52+
getRoundByStrategyAddress: vi.fn(),
53+
getRoundByStrategyAddressOrThrow: vi.fn(),
54+
getRoundByRole: vi.fn(),
55+
getRoundMatchTokenAddressById: vi.fn(),
56+
getRoundRoles: vi.fn(),
57+
getPendingRoundRoles: vi.fn(),
58+
},
59+
applicationRepository: {
60+
insertApplication: vi.fn(),
61+
updateApplication: vi.fn(),
62+
getApplicationById: vi.fn(),
63+
getApplicationByProjectId: vi.fn(),
64+
getApplicationByAnchorAddress: vi.fn(),
65+
getApplicationByAnchorAddressOrThrow: vi.fn(),
66+
getApplicationsByRoundId: vi.fn(),
67+
},
68+
donationRepository: {
69+
insertDonation: vi.fn(),
70+
insertManyDonations: vi.fn(),
71+
},
72+
applicationPayoutRepository: {
73+
insertApplicationPayout: vi.fn(),
74+
},
75+
transactionManager: {
76+
runInTransaction: vi
77+
.fn()
78+
.mockImplementation((fn: (tx: TransactionConnection) => Promise<unknown>) => {
79+
return fn({} as TransactionConnection);
80+
}),
81+
},
82+
});
83+
84+
// Mock providers
85+
export const createMockProviders = (): Pick<
86+
CoreDependencies,
87+
"pricingProvider" | "metadataProvider" | "evmProvider"
88+
> => ({
89+
pricingProvider: new DummyPricingProvider(),
90+
metadataProvider: {
91+
getMetadata: vi.fn(),
92+
},
93+
evmProvider: {
94+
getMulticall3Address: vi.fn().mockReturnValue(undefined),
95+
getTransaction: vi.fn(),
96+
getBalance: vi.fn(),
97+
readContract: vi.fn(),
98+
multicall: vi.fn(),
99+
} as unknown as EvmProvider,
100+
});
101+
102+
export const createMockIndexerClient = (): IIndexerClient => ({
103+
getEventsAfterBlockNumberAndLogIndex: vi.fn(),
104+
getEvents: vi.fn(),
105+
getBlockRangeTimestampByChainId: vi.fn(),
106+
});
107+
108+
export const DEFAULT_STRATEGY_MAP = new Map<ChainId, Map<Address, Hex>>([
109+
[
110+
1 as ChainId,
111+
new Map([
112+
[
113+
"0xF5F6Ca46a9DA3C1089d0F2F029cF14F3F714D483",
114+
"0x103732a8e473467a510d4128ee11065262bdd978f0d9dad89ba68f2c56127e27",
115+
],
116+
]),
117+
],
118+
]);
119+
120+
export const createMockRegistries = (
121+
strategiesMap: Map<ChainId, Map<Address, Hex>>,
122+
): {
123+
eventsRegistry: IEventRegistryRepository;
124+
strategyRegistry: IStrategyRegistry;
125+
} => ({
126+
eventsRegistry: {
127+
saveLastProcessedEvent: vi.fn(),
128+
getLastProcessedEvent: vi.fn(),
129+
},
130+
strategyRegistry: {
131+
getStrategyId: vi.fn().mockImplementation((chainId: ChainId, strategyAddress: Address) => {
132+
const chainIdMap = strategiesMap.get(chainId);
133+
if (!chainIdMap) {
134+
return undefined;
135+
}
136+
const strategyId = chainIdMap.get(strategyAddress);
137+
if (!strategyId) {
138+
return undefined;
139+
}
140+
return {
141+
id: strategyId,
142+
address: strategyAddress,
143+
chainId: chainId,
144+
handled: true,
145+
};
146+
}),
147+
saveStrategyId: vi.fn(),
148+
getStrategies: vi.fn(),
149+
},
150+
});
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import type {
2+
Address,
3+
AlloEvent,
4+
EventParams,
5+
IndexerFetchedEvent,
6+
RegistryEvent,
7+
StrategyEvent,
8+
TimestampMs,
9+
} from "@grants-stack-indexer/shared";
10+
11+
export const DEFAULT_SRC_ADDRESS = "0x3075bE2EdD29E1b1886c332193239ae321f434Bf" as const;
12+
export const DEFAULT_FROM_ADDRESS = "0x9511f02B36e6712971c77A91943D4ca7604C6e10" as const;
13+
export const DEFAULT_TX_HASH =
14+
"0xc13d7905be5c989378a945487cd2a1193627ae606009e28e296d48ddaec66162" as const;
15+
export const DEFAULT_TIMESTAMP_MS = 1701472394000 as TimestampMs;
16+
export const DEFAULT_BLOCK_NUMBER = 170147 as const;
17+
18+
export const createTestAlloEvent = <E extends AlloEvent>({
19+
contractName,
20+
eventName,
21+
params,
22+
blockNumber = DEFAULT_BLOCK_NUMBER,
23+
blockTimestamp = DEFAULT_TIMESTAMP_MS,
24+
srcAddress = DEFAULT_SRC_ADDRESS,
25+
logIndex = 0,
26+
chainId = 1,
27+
from = DEFAULT_FROM_ADDRESS,
28+
txIndex = 0,
29+
}: {
30+
contractName: "Allo";
31+
eventName: E;
32+
params: EventParams<"Allo", E>;
33+
blockNumber?: number;
34+
blockTimestamp?: TimestampMs;
35+
srcAddress?: Address;
36+
logIndex?: number;
37+
chainId?: number;
38+
from?: Address;
39+
txIndex?: number;
40+
}): IndexerFetchedEvent<"Allo", E> => {
41+
return {
42+
contractName,
43+
eventName,
44+
params,
45+
blockNumber,
46+
blockTimestamp,
47+
logIndex,
48+
chainId,
49+
srcAddress,
50+
transactionFields: {
51+
hash: DEFAULT_TX_HASH,
52+
transactionIndex: txIndex,
53+
from,
54+
},
55+
};
56+
};
57+
58+
export const createTestRegistryEvent = <E extends RegistryEvent>({
59+
eventName,
60+
params,
61+
blockNumber = DEFAULT_BLOCK_NUMBER,
62+
blockTimestamp = DEFAULT_TIMESTAMP_MS,
63+
logIndex = 0,
64+
chainId = 1,
65+
srcAddress = DEFAULT_SRC_ADDRESS,
66+
from = DEFAULT_FROM_ADDRESS,
67+
txIndex = 0,
68+
}: {
69+
eventName: E;
70+
params: EventParams<"Registry", E>;
71+
blockNumber?: number;
72+
blockTimestamp?: TimestampMs;
73+
logIndex?: number;
74+
chainId?: number;
75+
srcAddress?: Address;
76+
from?: Address;
77+
txIndex?: number;
78+
}): IndexerFetchedEvent<"Registry", E> => {
79+
return {
80+
contractName: "Registry",
81+
eventName,
82+
params,
83+
blockNumber,
84+
blockTimestamp,
85+
logIndex,
86+
chainId,
87+
srcAddress,
88+
transactionFields: {
89+
hash: DEFAULT_TX_HASH,
90+
transactionIndex: txIndex,
91+
from,
92+
},
93+
};
94+
};
95+
96+
export const createTestStrategyEvent = <E extends StrategyEvent>({
97+
eventName,
98+
params,
99+
blockNumber,
100+
blockTimestamp = DEFAULT_TIMESTAMP_MS,
101+
logIndex = 0,
102+
chainId = 1,
103+
srcAddress = DEFAULT_SRC_ADDRESS,
104+
from = DEFAULT_FROM_ADDRESS,
105+
txIndex = 0,
106+
}: {
107+
eventName: E;
108+
params: EventParams<"Strategy", E>;
109+
blockNumber: number;
110+
blockTimestamp?: TimestampMs;
111+
logIndex?: number;
112+
chainId?: number;
113+
srcAddress?: Address;
114+
from?: Address;
115+
txIndex?: number;
116+
}): IndexerFetchedEvent<"Strategy", E> => {
117+
return {
118+
contractName: "Strategy",
119+
eventName,
120+
params,
121+
blockNumber,
122+
blockTimestamp,
123+
logIndex,
124+
chainId,
125+
srcAddress,
126+
transactionFields: {
127+
hash: DEFAULT_TX_HASH,
128+
transactionIndex: txIndex,
129+
from,
130+
},
131+
};
132+
};
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { vi } from "vitest";
2+
3+
import type { Address, ChainId, Hex, ILogger } from "@grants-stack-indexer/shared";
4+
import { type IIndexerClient } from "@grants-stack-indexer/indexer-client";
5+
import { IEventRegistryRepository } from "@grants-stack-indexer/repository";
6+
import { ExponentialBackoff } from "@grants-stack-indexer/shared";
7+
8+
import { CoreDependencies, IStrategyRegistry } from "../../../src/internal.js";
9+
import { Orchestrator } from "../../../src/orchestrator.js";
10+
import {
11+
createMockIndexerClient,
12+
createMockProviders,
13+
createMockRegistries,
14+
createMockRepositories,
15+
DEFAULT_STRATEGY_MAP,
16+
} from "./dependencies.js";
17+
18+
export const createTestOrchestrator = (
19+
config: {
20+
chainId: ChainId;
21+
strategiesMap: Map<ChainId, Map<Address, Hex>>;
22+
} = { chainId: 1 as ChainId, strategiesMap: DEFAULT_STRATEGY_MAP },
23+
): {
24+
orchestrator: Orchestrator;
25+
mocks: {
26+
dependencies: CoreDependencies;
27+
indexerClient: IIndexerClient;
28+
registries: {
29+
eventsRegistry: IEventRegistryRepository;
30+
strategyRegistry: IStrategyRegistry;
31+
};
32+
logger: ILogger;
33+
};
34+
} => {
35+
const repositories = createMockRepositories();
36+
const providers = createMockProviders();
37+
const mockNotifier = {
38+
send: vi.fn(),
39+
};
40+
const mockLogger = {
41+
info: vi.fn(),
42+
error: vi.fn(),
43+
warn: vi.fn(),
44+
debug: vi.fn(),
45+
trace: vi.fn(),
46+
};
47+
const mockIndexerClient = createMockIndexerClient();
48+
const { eventsRegistry, strategyRegistry } = createMockRegistries(config.strategiesMap);
49+
50+
const dependencies: CoreDependencies = {
51+
...repositories,
52+
...providers,
53+
};
54+
55+
const orchestrator = new Orchestrator(
56+
config.chainId,
57+
dependencies,
58+
mockIndexerClient,
59+
{
60+
eventsRegistry,
61+
strategyRegistry,
62+
},
63+
1000,
64+
0, // No delay in tests
65+
new ExponentialBackoff({ baseDelay: 10, factor: 2, maxAttempts: 1 }),
66+
mockLogger,
67+
mockNotifier,
68+
);
69+
70+
return {
71+
orchestrator,
72+
mocks: {
73+
dependencies,
74+
logger: mockLogger,
75+
indexerClient: mockIndexerClient,
76+
registries: {
77+
eventsRegistry,
78+
strategyRegistry,
79+
},
80+
},
81+
};
82+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { MockInstance, vi } from "vitest";
2+
3+
export const waitForProcessing = async (
4+
eventsFetcherSpy: MockInstance,
5+
dataLoaderSpy: MockInstance,
6+
): Promise<void> => {
7+
await vi.waitFor(
8+
() => {
9+
if (eventsFetcherSpy.mock.calls.length < 2) throw new Error("Not yet called");
10+
if (dataLoaderSpy.mock.calls.length < 1) throw new Error("Not yet called");
11+
},
12+
{
13+
timeout: 500,
14+
interval: 50,
15+
},
16+
);
17+
};

0 commit comments

Comments
 (0)