Skip to content
Closed
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
85 changes: 85 additions & 0 deletions pkg/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,17 @@ func (e *executor) ExecuteTests(ctx context.Context, opts *ExecuteOptions) (*Exe
}
}

// After setup step, wait for chain head to advance before running the test step.
// This is critical for multi-block EEST fixtures where the setup block deploys
// contracts and the test block calls them. Without this wait, the test block's
// engine_newPayload returns SYNCING because the node hasn't committed the setup
// block as the canonical head yet.
if test.Setup != nil && test.Test != nil && testPassed && opts.RPCEndpoint != "" {
if err := e.waitForChainHeadAdvance(ctx, opts.RPCEndpoint, rollbackInfo, log); err != nil {
log.WithError(err).Warn("Failed to wait for chain head advance after setup")
}
}

// Drop caches between setup and test.
if dropBetweenSteps && test.Setup != nil && test.Test != nil {
if err := e.dropMemoryCaches(dropCachesPath); err != nil {
Expand Down Expand Up @@ -1025,6 +1036,80 @@ func (e *executor) getBlockInfo(ctx context.Context, rpcEndpoint string) (*block
}, nil
}

// waitForChainHeadAdvance polls eth_blockNumber until the chain head has advanced
// beyond the block number captured before the setup step. This ensures the node has
// committed setup blocks (e.g., contract deployments) before the test step begins.
// Without this, multi-block EEST fixtures fail because the test block's
// engine_newPayload returns SYNCING — the node hasn't adopted the setup block yet.
func (e *executor) waitForChainHeadAdvance(
ctx context.Context,
rpcEndpoint string,
preSetupBlock *blockInfo,
log *logrus.Entry,
) error {
if preSetupBlock == nil {
// No baseline block info — just do a short sleep as fallback.
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(500 * time.Millisecond):
}

return nil
}

maxWait := 30 * time.Second
pollInterval := 200 * time.Millisecond
deadline := time.Now().Add(maxWait)

log.WithFields(logrus.Fields{
"pre_setup_block": preSetupBlock.HexNumber,
}).Debug("Waiting for chain head to advance after setup step")

for time.Now().Before(deadline) {
select {
case <-ctx.Done():
return ctx.Err()
default:
}

current, err := e.getBlockInfo(ctx, rpcEndpoint)
if err != nil {
log.WithError(err).Debug("Failed to get block info while waiting")

select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(pollInterval):
}

continue
}

if current.HexNumber != preSetupBlock.HexNumber {
log.WithFields(logrus.Fields{
"pre_setup_block": preSetupBlock.HexNumber,
"current_block": current.HexNumber,
}).Debug("Chain head advanced after setup step")

return nil
}

select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(pollInterval):
}
}

log.WithFields(logrus.Fields{
"pre_setup_block": preSetupBlock.HexNumber,
"timeout": maxWait,
}).Warn("Timed out waiting for chain head to advance after setup step")

return nil // Don't fail the test, just warn — the test step's SYNCING retry may still succeed.
}

// rollback calls the client-specific rollback RPC method to revert chain state.
func (e *executor) rollback(
ctx context.Context,
Expand Down
Loading