Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ await indexer.EntityName.getOrThrow("id"); // throws if not found
await indexer.EntityName.getAll(); // returns all entities of this type
```

## Test isolation

Each `createTestIndexer()` has its own entity store; within one indexer, entity
state and block progress persist across `process()` calls (each continues where
the last stopped).

Tests run in-process, so **module-level** state in your handler code (top-level
`let`/`const`, memoized clients, effect caches) is shared across every indexer
and test in the file and persists between them. Reset it yourself (e.g. in a
`beforeEach`) if a test depends on it.

## result.changes

`result.changes` is an array of per-block change objects. Each entry has `block`, `chainId`, `eventsProcessed`, plus entity names as keys with `sets` arrays of created/updated entities. Dynamic contract registrations appear under `addresses.sets`.
Expand Down
3 changes: 2 additions & 1 deletion packages/envio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"tsx": "4.21.0"
},
"devDependencies": {
"rescript": "12.2.0"
"rescript": "12.2.0",
"vitest": "4.1.0"
}
}
9 changes: 1 addition & 8 deletions packages/envio/src/Api.res
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,5 @@
let indexer: unknown = Main.getGlobalIndexer()

let createTestIndexer: unit => unknown = () => {
let workerPath =
NodeJs.Path.join(
NodeJs.Path.getDirname(NodeJs.ImportMeta.importMeta),
"TestIndexerWorker.res.mjs",
)->NodeJs.Path.toString
TestIndexer.makeCreateTestIndexer(~config=Config.load(), ~workerPath)()->(
Utils.magic: TestIndexer.t<'a> => unknown
)
TestIndexer.createTestIndexer()->(Utils.magic: TestIndexer.t<'a> => unknown)
}
8 changes: 6 additions & 2 deletions packages/envio/src/ExitOnCaughtUp.res
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ let run = async (state: IndexerState.t) => {
->IndexerState.simulateDeadInputTracker
->Option.flatMap(SimulateDeadInputTracker.failureMessage) {
| None =>
Logging.info("Exiting with success")
NodeJs.process->NodeJs.exitWithCode(Success)
switch state->IndexerState.onExit {
| Some(onExit) => onExit()
| None =>
Logging.info("Exiting with success")
NodeJs.process->NodeJs.exitWithCode(Success)
}
| Some(message) => state->IndexerState.errorExit(ErrorHandling.make(Utils.Error.make(message)))
}
}
Expand Down
10 changes: 10 additions & 0 deletions packages/envio/src/IndexerState.res
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ type t = {
exitAfterFirstEventBlock: bool,
// The single fatal-error handler.
onError: ErrorHandling.t => unit,
// Invoked once when the indexer catches up and would otherwise exit the
// process. `None` keeps the production behavior (exit the process); the
// in-process test runner injects a callback that resolves its run promise
// instead, so a caught-up run doesn't kill the test process.
onExit: option<unit => unit>,
// Set once on any fatal error. Every loop checks it to stop iterating and
// every launch skips when it's set, so a single failure quiesces the indexer.
mutable isStopped: bool,
Expand Down Expand Up @@ -149,6 +154,7 @@ let make = (
~shouldUseTui=false,
~exitAfterFirstEventBlock=false,
~onError: ErrorHandling.t => unit,
~onExit=?,
) => {
let chainMetaThrottler = {
let intervalMillis = Env.ThrottleWrites.chainMetadataIntervalMillis
Expand Down Expand Up @@ -194,6 +200,7 @@ let make = (
keepProcessAlive: isDevelopmentMode || shouldUseTui,
exitAfterFirstEventBlock,
onError,
onExit,
isStopped: false,
epoch: 0,
simulateDeadInputTracker: SimulateDeadInputTracker.makeFromConfig(config),
Expand Down Expand Up @@ -229,6 +236,7 @@ let makeFromDbState = (
~reducedPollingInterval=?,
~targetBufferSize=CrossChainState.calculateTargetBufferSize(),
~onError,
~onExit=?,
) => {
let isInReorgThreshold = if initialState.cleanRun {
false
Expand Down Expand Up @@ -280,6 +288,7 @@ let makeFromDbState = (
~shouldUseTui,
~exitAfterFirstEventBlock,
~onError,
~onExit?,
)
initialState.cache->Utils.Dict.forEach(({effectName, count, scope}) => {
state.effectState->EffectState.setUnregisteredCacheCount(~effectName, ~scope, ~count)
Expand Down Expand Up @@ -415,6 +424,7 @@ let indexerStartTime = (state: t) => state.indexerStartTime
let loadManager = (state: t) => state.loadManager
let keepProcessAlive = (state: t) => state.keepProcessAlive
let exitAfterFirstEventBlock = (state: t) => state.exitAfterFirstEventBlock
let onExit = (state: t) => state.onExit
let isStopped = (state: t) => state.isStopped
let epoch = (state: t) => state.epoch
let lastPrunedAtMillis = (state: t) => state.lastPrunedAtMillis
Expand Down
3 changes: 3 additions & 0 deletions packages/envio/src/IndexerState.resi
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ let make: (
~shouldUseTui: bool=?,
~exitAfterFirstEventBlock: bool=?,
~onError: ErrorHandling.t => unit,
~onExit: unit => unit=?,
) => t

let makeFromDbState: (
Expand All @@ -43,6 +44,7 @@ let makeFromDbState: (
~reducedPollingInterval: int=?,
~targetBufferSize: int=?,
~onError: ErrorHandling.t => unit,
~onExit: unit => unit=?,
) => t

let unexpectedErrorMsg: string
Expand Down Expand Up @@ -96,6 +98,7 @@ let indexerStartTime: t => Date.t
let loadManager: t => LoadManager.t
let keepProcessAlive: t => bool
let exitAfterFirstEventBlock: t => bool
let onExit: t => option<unit => unit>
let isStopped: t => bool
let epoch: t => int
let lastPrunedAtMillis: t => dict<float>
Expand Down
Loading
Loading