feat(indexer): expose status over RPC and add integration coverage

test `RUST_LOG=info RISC0_DEV_MODE=1 cargo test -p integration_tests --test bridge --indexer_status_rpc_reports_caught_up_with_no_stall --exact --nocapture --include-ignored`
This commit is contained in:
erhant 2026-06-26 17:44:00 +03:00
parent 251919c4e7
commit 774af23521
4 changed files with 58 additions and 0 deletions

View File

@ -536,6 +536,38 @@ fn create_zone_indexer_observer(
))
}
/// Test that the indexer status RPC reports caught-up with no stall after a clean run.
///
/// TODO: Integration-level park testing (publishing a bad block to force a stall) is a follow-up
/// needing fault injection support in the test harness.
#[ignore = "requires the full local stack (bedrock + sequencer + indexer)"]
#[test]
async fn indexer_status_rpc_reports_caught_up_with_no_stall() -> anyhow::Result<()> {
use indexer_service_rpc::RpcClient as _;
let ctx = TestContext::new().await?;
let indexer_tip = wait_for_indexer_to_catch_up(&ctx).await?;
let status = ctx.indexer_client().get_status().await?;
assert_eq!(
status["state"],
serde_json::json!("caught_up"),
"indexer should be caught up, got {status}"
);
assert!(
status["stallReason"].is_null(),
"indexer should have no stall reason after a clean run, got {status}"
);
assert_eq!(
status["indexedBlockId"],
serde_json::json!(indexer_tip),
"status indexedBlockId should equal the caught-up tip"
);
Ok(())
}
async fn wait_for_finalized_withdraw_op(
observer: &ZoneIndexer<NodeHttpClient>,
expected_amount: u64,

View File

@ -69,6 +69,9 @@ pub trait Rpc {
limit: u64,
) -> Result<Vec<Transaction>, ErrorObjectOwned>;
#[method(name = "getStatus")]
async fn get_status(&self) -> Result<serde_json::Value, ErrorObjectOwned>;
// ToDo: expand healthcheck response into some kind of report
#[method(name = "checkHealth")]
async fn healthcheck(&self) -> Result<(), ErrorObjectOwned>;

View File

@ -325,6 +325,24 @@ impl indexer_service_rpc::RpcServer for MockIndexerService {
.collect())
}
async fn get_status(&self) -> Result<serde_json::Value, ErrorObjectOwned> {
let indexed_block_id = self
.state
.read()
.await
.blocks
.iter()
.rev()
.find(|block| block.bedrock_status == BedrockStatus::Finalized)
.map(|block| block.header.block_id);
Ok(serde_json::json!({
"state": "caught_up",
"lastError": null,
"indexedBlockId": indexed_block_id,
"stallReason": null,
}))
}
async fn healthcheck(&self) -> Result<(), ErrorObjectOwned> {
Ok(())
}

View File

@ -149,6 +149,11 @@ impl indexer_service_rpc::RpcServer for IndexerService {
Ok(tx_res)
}
async fn get_status(&self) -> Result<serde_json::Value, ErrorObjectOwned> {
Ok(serde_json::to_value(self.indexer.status())
.expect("IndexerStatus serialization should not fail"))
}
async fn healthcheck(&self) -> Result<(), ErrorObjectOwned> {
// Checking, that indexer can calculate last state
let _ = self