Skip to content
Open
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
269 changes: 269 additions & 0 deletions lib/cometbft-rpc/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ fn ensure_json<T: DeserializeOwned + PartialEq + Debug>(path: impl AsRef<Path>,
}
}

/// Deserialize a captured response, re-serialize the typed value, and deserialize
/// again, asserting the two typed values are equal. This verifies our serde impls
/// losslessly round-trip real-world responses without hand-transcribing every field
/// (used for the large structural responses where full-equality vectors are impractical).
#[track_caller]
fn ensure_roundtrip<T: Serialize + DeserializeOwned + PartialEq + Debug>(path: impl AsRef<Path>) {
let json = std::fs::read_to_string(path).unwrap();

let jd = &mut serde_json::Deserializer::from_str(&json);
let response: JsonRpcResponse<T> = match serde_path_to_error::deserialize(jd) {
Ok(response) => response,
Err(err) => panic!("deserialize: {err}"),
};

let reencoded = serde_json::to_string(&response.result).unwrap();
let jd = &mut serde_json::Deserializer::from_str(&reencoded);
let roundtripped: T = match serde_path_to_error::deserialize(jd) {
Ok(result) => result,
Err(err) => panic!("re-deserialize: {err}"),
};

assert_eq!(response.result, roundtripped);
}

#[derive(Debug, Serialize, Deserialize)]
struct JsonRpcResponse<T> {
jsonrpc: String,
Expand Down Expand Up @@ -425,3 +449,248 @@ mod status {
);
}
}

// Vectors below are captured from public RPCs of union (union-1), babylon (bbn-1),
// and osmosis (osmosis-1). Small responses assert full typed equality; the larger
// structural responses use `ensure_roundtrip` (see the helper at the top of this file).

mod abci_info {
use cometbft_types::abci::info_response::InfoResponse;
use unionlabs::bounded::BoundedI64;

use super::*;
use crate::rpc_types::AbciInfoResponse;

#[test]
fn union() {
ensure_json(
"testdata/abci_info/union.json",
AbciInfoResponse {
response: InfoResponse {
data: "union".to_owned(),
version: "v1.2.3".to_owned(),
app_version: 0,
last_block_height: BoundedI64::new_const(7277988).unwrap(),
last_block_app_hash: "Y8mcAu1ZdKgl03/Jx/1I85N4k6k1CEdMAhPCJ39q/ac="
.parse()
.unwrap(),
},
},
);
}

#[test]
fn bbn() {
ensure_roundtrip::<AbciInfoResponse>("testdata/abci_info/bbn.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<AbciInfoResponse>("testdata/abci_info/osmosis.json");
}
}

mod abci_query {
use super::*;
use crate::rpc_types::AbciQueryResponse;

#[test]
fn union() {
ensure_roundtrip::<AbciQueryResponse>("testdata/abci_query/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<AbciQueryResponse>("testdata/abci_query/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<AbciQueryResponse>("testdata/abci_query/osmosis-65368004.json");
}
}

mod block {
use super::*;
use crate::rpc_types::BlockResponse;

#[test]
fn union() {
ensure_roundtrip::<BlockResponse>("testdata/block/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<BlockResponse>("testdata/block/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<BlockResponse>("testdata/block/osmosis-65368004.json");
}
}

mod block_results {
use super::*;
use crate::rpc_types::BlockResultsResponse;

#[test]
fn union() {
ensure_roundtrip::<BlockResultsResponse>("testdata/block_results/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<BlockResultsResponse>("testdata/block_results/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<BlockResultsResponse>("testdata/block_results/osmosis-65368004.json");
}
}

mod block_search {
use super::*;
use crate::rpc_types::BlockSearchResponse;

#[test]
fn union() {
ensure_roundtrip::<BlockSearchResponse>("testdata/block_search/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<BlockSearchResponse>("testdata/block_search/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<BlockSearchResponse>("testdata/block_search/osmosis-65368004.json");
}
}

mod blockchain {
use super::*;
use crate::rpc_types::BlockchainResponse;

#[test]
fn union() {
ensure_roundtrip::<BlockchainResponse>("testdata/blockchain/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<BlockchainResponse>("testdata/blockchain/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<BlockchainResponse>("testdata/blockchain/osmosis-65368004.json");
}
}

mod commit {
use super::*;
use crate::rpc_types::CommitResponse;

#[test]
fn union() {
ensure_roundtrip::<CommitResponse>("testdata/commit/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<CommitResponse>("testdata/commit/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<CommitResponse>("testdata/commit/osmosis-65368004.json");
}
}

mod header {
use super::*;
use crate::rpc_types::HeaderResponse;

#[test]
fn union() {
ensure_roundtrip::<HeaderResponse>("testdata/header/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<HeaderResponse>("testdata/header/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<HeaderResponse>("testdata/header/osmosis-65368004.json");
}
}

mod tx {
use super::*;
use crate::rpc_types::TxResponse;

#[test]
fn union() {
ensure_roundtrip::<TxResponse>("testdata/tx/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<TxResponse>("testdata/tx/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<TxResponse>("testdata/tx/osmosis-65368004.json");
}
}

mod tx_search {
use super::*;
use crate::rpc_types::TxSearchResponse;

#[test]
fn union() {
ensure_roundtrip::<TxSearchResponse>("testdata/tx_search/union-7277835.json");
}

#[test]
fn bbn() {
ensure_roundtrip::<TxSearchResponse>("testdata/tx_search/bbn-3858000.json");
}

#[test]
fn osmosis() {
ensure_roundtrip::<TxSearchResponse>("testdata/tx_search/osmosis-65368004.json");
}
}

// `broadcast_tx_sync` cannot be captured from a public RPC (it requires broadcasting a
// signed transaction), so this vector is hand-crafted for a successful (code 0) response.
mod broadcast_tx_sync {
use cometbft_types::code::Code;

use super::*;
use crate::rpc_types::BroadcastTxSyncResponse;

#[test]
fn union() {
ensure_json(
"testdata/broadcast_tx_sync/union.json",
BroadcastTxSyncResponse {
codespace: String::new(),
code: Code::Ok,
data: "".parse().unwrap(),
log: String::new(),
hash: "a1b2c3d4e5f6070809000a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
.parse()
.unwrap(),
},
);
}
}
12 changes: 12 additions & 0 deletions lib/cometbft-rpc/testdata/abci_info/bbn.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"response": {
"data": "BabylonApp",
"version": "v4.3.0",
"last_block_height": "3860883",
"last_block_app_hash": "TEqHEj20s4igqReCFz8VazEphk4EwmhQDwIT29NnmmU="
}
}
}
12 changes: 12 additions & 0 deletions lib/cometbft-rpc/testdata/abci_info/osmosis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"response": {
"data": "OsmosisApp",
"version": "31.0.2",
"last_block_height": "65371487",
"last_block_app_hash": "LvzSqfR5wce31ROXxYOF2ECvVczd9GjoUwi58g+EtZI="
}
}
}
12 changes: 12 additions & 0 deletions lib/cometbft-rpc/testdata/abci_info/union.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"response": {
"data": "union",
"version": "v1.2.3",
"last_block_height": "7277988",
"last_block_app_hash": "Y8mcAu1ZdKgl03/Jx/1I85N4k6k1CEdMAhPCJ39q/ac="
}
}
}
30 changes: 30 additions & 0 deletions lib/cometbft-rpc/testdata/abci_query/bbn-3858000.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"response": {
"code": 0,
"log": "",
"info": "",
"index": "0",
"key": "AnVvc21v",
"value": null,
"proofOps": {
"ops": [
{
"type": "ics23:iavl",
"key": "AnVvc21v",
"data": "EvMQCgYCdW9zbW8SsggKJgIg//ik1oifGoFKENRf0pTle7iSndTqsiItBD97hVRVQwB1YmJuEgUzNzc1OBoOCAEYASABKgYAAoCltAMiLAgBEigCBICltAMglm1hQnuwVyZggTTNqxBvjez61nRZpuh6lNKyUSg60RYgIi4IARIHBAaApbQDIBohIJ4YcRzRhzpxzWziglD0XoLxSkNUNAdrix5upAqYhOkFIi4IARIHBg6ApbQDIBohIOrfKxRwyLpwNkGRIXUw4tLDalBWgGjn6ZFFboIKwhafIi4IARIHCBqApbQDIBohIP1ttyweS1W2cDm/dokTOL4lQxgfBYkyrwy3WQsODSZXIi4IARIHCjaApbQDIBohIKjb4zuXCyCQDQzkQ2qnaWGYlBcjwDXY80di8oZ8QBpPIiwIARIoDFjwldYDIEiaWBBjUghYLGlAw/XSeuwUPuXj75Es26hJVsOhrJYbICIvCAESCBC4AfCV1gMgGiEg1G/p/latPX12gYm+iQy28Tn+rjuoLD7c1KyJLSzNtOYiLQgBEikSxgOU99YDIIjKiCSe12YSSAn6nLyloWqfnwuQWl+aF3nN1bE1fOlsICIvCAESCBSCB5T31gMgGiEg3m4qgSw8tjorsgie3kCz1gEoY7tYzqwFUeRZi7LxGK4iLwgBEggW1gqU99YDIBohIOWXfSxZdIiBKwwyNjx1zJqA/CJbe7FajNNPKolKN1cTIi0IARIpGKQQoPnWAyDLikl172A6FMkmpFGTnSJCZBqDkLCGUgihlhjQ/gpivCAiLwgBEggaiCCg+dYDIBohIHZSdOdz78H8K0490p3QUkmqyU13vPCUf6MYaiXz0Ey8Ii8IARIIHI4xoPnWAyAaISDxEn/5S3niDvqo8rAz5m9wEXaVW/A+LGYtCwQyDgnW2yIuCAESKiCcmQGg+dYDIDPbvRNn+3qiK7DYkWw2vmDJ1oOstHlWdibdR6W0gz2LICIuCAESKiKwrQKg+dYDIBb8TnyKUQtDFlOHeROweGXA31a0MCbj3meFhQOyt0/dICIwCAESCSSkoQOg+dYDIBohIIB1uSoIRXQBgWp+b0fM39Ze6CZhKGVS2zj3IYd5A/lfIi4IARIqJpCEBqD51gMg33peb2LrKEy9K7kSK+TmOkWZFSbyrS75ePgg+lVWuTogIi4IARIqKObODKD51gMgeSltZBSuNnChRDYMuqOTONaBhgkaYIHQrYrt7gMTBlAgIi4IARIqKvLSGqD51gMgwaCtR1Akk34IE1cOsCLCTxLUVsDQGXSugdijaEGqiMcgIi4IARIqLLSXL6D51gMgtE0tq4NrAc6LkJhS7P8T/SWlWovF56lzzVCfJ/CalZMgIjAIARIJLrrzW6D51gMgGiEg9qSIMlOGTLpJyAVLwC+Xte4NMMJ8grt34nCfgfEEaQEaswgKXgNmYWN0b3J5L2JibjEzMDBzZTB2d3VlNzdobjZzOHdwaDY0ZXk2ZDU1emFmNDhqcnZlZzl3YWZzcXVuY24zZTRzY3NzZ3ZkLwAUAB5aW+dSM1hB2QIq5b1+ne6xMqkaDggBGAEgASoGAAK22+ABIiwIARIoBAaApbQDIO17BKz4wOdgomNCG7Msgj4qD3HjPmjHKM/lE6EEzzHkICIuCAESBwYOgKW0AyAaISDq3ysUcMi6cDZBkSF1MOLSw2pQVoBo5+mRRW6CCsIWnyIuCAESBwgagKW0AyAaISD9bbcsHktVtnA5v3aJEzi+JUMYHwWJMq8Mt1kLDg0mVyIuCAESBwo2gKW0AyAaISCo2+M7lwsgkA0M5ENqp2lhmJQXI8A12PNHYvKGfEAaTyIsCAESKAxY8JXWAyBImlgQY1IIWCxpQMP10nrsFD7l4++RLNuoSVbDoayWGyAiLwgBEggQuAHwldYDIBohINRv6f5WrT19doGJvokMtvE5/q47qCw+3NSsiS0szbTmIi0IARIpEsYDlPfWAyCIyogkntdmEkgJ+py8paFqn58LkFpfmhd5zdWxNXzpbCAiLwgBEggUggeU99YDIBohIN5uKoEsPLY6K7IInt5As9YBKGO7WM6sBVHkWYuy8RiuIi8IARIIFtYKlPfWAyAaISDll30sWXSIgSsMMjY8dcyagPwiW3uxWozTTyqJSjdXEyItCAESKRikEKD51gMgy4pJde9gOhTJJqRRk50iQmQag5CwhlIIoZYY0P4KYrwgIi8IARIIGoggoPnWAyAaISB2UnTnc+/B/CtOPdKd0FJJqslNd7zwlH+jGGol89BMvCIvCAESCByOMaD51gMgGiEg8RJ/+Ut54g76qPKwM+ZvcBF2lVvwPixmLQsEMg4J1tsiLggBEiognJkBoPnWAyAz270TZ/t6oiuw2JFsNr5gydaDrLR5VnYm3UeltIM9iyAiLggBEioisK0CoPnWAyAW/E58ilELQxZTh3kTsHhlwN9WtDAm495nhYUDsrdP3SAiMAgBEgkkpKEDoPnWAyAaISCAdbkqCEV0AYFqfm9HzN/WXugmYShlUts49yGHeQP5XyIuCAESKiaQhAag+dYDIN96Xm9i6yhMvSu5Eivk5jpFmRUm8q0u+Xj4IPpVVrk6ICIuCAESKijmzgyg+dYDIHkpbWQUrjZwoUQ2DLqjkzjWgYYJGmCB0K2K7e4DEwZQICIuCAESKiry0hqg+dYDIMGgrUdQJJN+CBNXDrAiwk8S1FbA0Bl0roHYo2hBqojHICIuCAESKiy0ly+g+dYDILRNLauDawHOi5CYUuz/E/0lpVqLxeepc81QnyfwmpWTICIwCAESCS6681ug+dYDIBohIPakiDJThky6ScgFS8Avl7XuDTDCfIK7d+Jwn4HxBGkB"
},
{
"type": "ics23:simple",
"key": "YmFuaw==",
"data": "CvwBCgRiYW5rEiAL4yr3oHmho+AFMFkAZXPJNALL6e1avzEr4wzlnXpJkxoJCAEYASABKgEAIiUIARIhAal+Tavhotmnxm+3F5WveZgsoM/9XRUr2Ty6nIjw32Y+IiUIARIhAVLmbxM4SgX2mKQzGu4GwlyQ7WCvxrFiiQWZVAKcua/uIicIARIBARog15offMGtZCJBmuADE2gvY2y6NAteYlF4cmdqT8ka+4siJwgBEgEBGiCFh8+0/XyI12KgPiEEvKkhbpdFSxH0nIa/98M/EQq8hiInCAESAQEaICSS63JYQavxDEjURaGgRKCipG0K8AugfM5Y3xI1bLz1"
}
]
},
"height": "3858000",
"codespace": ""
}
}
}
30 changes: 30 additions & 0 deletions lib/cometbft-rpc/testdata/abci_query/osmosis-65368004.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"response": {
"code": 0,
"log": "",
"info": "",
"index": "0",
"key": "AnVvc21v",
"value": null,
"proofOps": {
"ops": [
{
"type": "ics23:iavl",
"key": "AnVvc21v",
"data": "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"
},
{
"type": "ics23:simple",
"key": "YmFuaw==",
"data": "CqcCCgRiYW5rEiDwz75c1ov58ojqtCpjv8eLslsfCuoPZJbCEM/LJWyE6xoJCAEYASABKgEAIicIARIBARogg8QgtJ8e0l8PQ1VGn5AIsd8NTCnnXPpmuR+JYO8H2/0iJwgBEgEBGiCquWoapMQ7eEheLzaCa2YKbSFaLOfX/i0GG+Bt5/4z5CIlCAESIQF6dDIODA9mz0+uC5ph0nCOUL8I9Vh6jddaejr9Q5Y9CyInCAESAQEaIHlCR/IRe27hShGQu9lwte3W2iwxfgD2Du8jgRXJ6YZjIicIARIBARogYG5phYy1/kmrUg5oIRNAlgZjtCYAQY/PqE8Z42cgPxsiJwgBEgEBGiBzgXpe25KXZ4TVl9D0VqxOwyWMqycOzAp87tV2kIwhQQ=="
}
]
},
"height": "65368004",
"codespace": ""
}
}
}
Loading