Skip to content

Commit b80d992

Browse files
authored
feat: update portal-spec-tests and refactor reading test files (#1797)
1 parent 5bc7e54 commit b80d992

File tree

8 files changed

+242
-266
lines changed

8 files changed

+242
-266
lines changed
+40-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,52 @@
11
use std::{
2-
fs, io,
2+
fs::{self, File},
3+
io::{self, BufReader},
34
path::{Path, PathBuf},
45
};
56

7+
use anyhow::anyhow;
8+
use serde::de::DeserializeOwned;
9+
use ssz::Decode;
10+
611
use self::constants::PORTAL_SPEC_TESTS_SUBMODULE_PATH;
712

813
pub mod constants;
14+
pub mod types;
915

10-
/// Reads a file from a "portal-spec-tests" submodule.
11-
pub fn read_file_from_tests_submodule<P: AsRef<Path>>(path: P) -> io::Result<String> {
12-
fs::read_to_string(PathBuf::from(PORTAL_SPEC_TESTS_SUBMODULE_PATH).join(path))
16+
fn portal_spec_tests_path(path: impl AsRef<Path>) -> PathBuf {
17+
PathBuf::from(PORTAL_SPEC_TESTS_SUBMODULE_PATH).join(path)
1318
}
1419

1520
/// Reads bytes from a "portal-spec-tests" submodule.
16-
pub fn read_bytes_from_tests_submodule<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
17-
fs::read(PathBuf::from(PORTAL_SPEC_TESTS_SUBMODULE_PATH).join(path))
21+
pub fn read_binary_portal_spec_tests_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
22+
fs::read(portal_spec_tests_path(path))
23+
}
24+
25+
/// Reads json file from a "portal-spec-tests" submodule
26+
pub fn read_json_portal_spec_tests_file<T>(path: impl AsRef<Path>) -> anyhow::Result<T>
27+
where
28+
T: DeserializeOwned,
29+
{
30+
let reader = BufReader::new(File::open(portal_spec_tests_path(path))?);
31+
Ok(serde_json::from_reader(reader)?)
32+
}
33+
34+
/// Reads yaml file from a "portal-spec-tests" submodule
35+
pub fn read_yaml_portal_spec_tests_file<T>(path: impl AsRef<Path>) -> anyhow::Result<T>
36+
where
37+
T: DeserializeOwned,
38+
{
39+
let reader = BufReader::new(File::open(portal_spec_tests_path(path))?);
40+
Ok(serde_yaml::from_reader(reader)?)
41+
}
42+
43+
/// Reads ssz file from a "portal-spec-tests" submodule
44+
pub fn read_ssz_portal_spec_tests_file<T: Decode>(path: impl AsRef<Path>) -> anyhow::Result<T> {
45+
let bytes = read_binary_portal_spec_tests_file(&path)?;
46+
T::from_ssz_bytes(&bytes).map_err(|err| {
47+
anyhow!(
48+
"Error decoding ssz file: {}. Error: {err:?}",
49+
path.as_ref().display()
50+
)
51+
})
1852
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
use crate::{
4+
types::execution::header_with_proof::HeaderWithProof, ContentValue, ContentValueError,
5+
HistoryContentKey, HistoryContentValue, OverlayContentKey, RawContentValue,
6+
};
7+
8+
/// A common type used in test files.
9+
#[derive(Debug, Clone, Serialize, Deserialize)]
10+
pub struct ContentItem<K: OverlayContentKey> {
11+
pub content_key: K,
12+
#[serde(rename = "content_value")]
13+
pub raw_content_value: RawContentValue,
14+
}
15+
16+
impl<K: OverlayContentKey> ContentItem<K> {
17+
pub fn content_value<V: ContentValue<TContentKey = K>>(&self) -> Result<V, ContentValueError> {
18+
V::decode(&self.content_key, &self.raw_content_value)
19+
}
20+
}
21+
22+
impl ContentItem<HistoryContentKey> {
23+
/// Decodes content value as HeaderWithProof.
24+
///
25+
/// Panics if content value is not HeaderWithProof.
26+
pub fn content_value_as_header_with_proof(&self) -> HeaderWithProof {
27+
let HistoryContentValue::BlockHeaderWithProof(header_with_proof) =
28+
self.content_value().unwrap()
29+
else {
30+
panic!(
31+
"Expected BlockHeaderWithProof content value. Actual {}",
32+
self.raw_content_value
33+
);
34+
};
35+
header_with_proof
36+
}
37+
}

crates/ethportal-api/src/types/content_key/state.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ mod test {
177177
use serde_yaml::Value;
178178

179179
use super::*;
180-
use crate::{test_utils::read_file_from_tests_submodule, utils::bytes::hex_decode};
180+
use crate::{test_utils::read_yaml_portal_spec_tests_file, utils::bytes::hex_decode};
181181

182182
const TEST_DATA_DIRECTORY: &str = "tests/mainnet/state/serialization";
183183

@@ -293,9 +293,7 @@ mod test {
293293
}
294294

295295
fn read_yaml_file(filename: &str) -> anyhow::Result<Value> {
296-
let path = PathBuf::from(TEST_DATA_DIRECTORY).join(filename);
297-
let file = read_file_from_tests_submodule(path)?;
298-
Ok(serde_yaml::from_str(&file)?)
296+
read_yaml_portal_spec_tests_file(PathBuf::from(TEST_DATA_DIRECTORY).join(filename))
299297
}
300298

301299
fn yaml_as_address(value: &Value) -> Address {

crates/ethportal-api/src/types/content_value/beacon.rs

+15-44
Original file line numberDiff line numberDiff line change
@@ -548,32 +548,22 @@ impl ContentValue for BeaconContentValue {
548548

549549
#[cfg(test)]
550550
mod test {
551-
use std::str::FromStr;
552-
553-
use alloy::primitives::Bytes;
554551
use serde::Deserialize;
555-
use serde_yaml::Value;
556552

557553
use super::*;
558-
use crate::test_utils::read_file_from_tests_submodule;
554+
use crate::test_utils::{read_yaml_portal_spec_tests_file, types::ContentItem};
559555

560556
#[rstest::rstest]
561557
#[case("capella", 6718368)]
562558
#[case("deneb", 10248000)]
563559
fn light_client_bootstrap_encode_decode(#[case] fork_name: &str, #[case] expected_slot: u64) {
564-
let file = read_file_from_tests_submodule(format!(
560+
let test_data: ContentItem<BeaconContentKey> = read_yaml_portal_spec_tests_file(format!(
565561
"tests/mainnet/beacon_chain/light_client/{fork_name}/bootstrap.yaml",
566562
))
567563
.unwrap();
564+
let content_value: BeaconContentValue = test_data.content_value().unwrap();
568565

569-
let value: Value = serde_yaml::from_str(&file).unwrap();
570-
let content_key: BeaconContentKey =
571-
serde_yaml::from_value(value["content_key"].clone()).unwrap();
572-
let raw_content_value = Bytes::from_str(value["content_value"].as_str().unwrap()).unwrap();
573-
let content_value = BeaconContentValue::decode(&content_key, raw_content_value.as_ref())
574-
.expect("unable to decode content value");
575-
576-
assert_str_roundtrip(content_key, content_value.clone());
566+
assert_str_roundtrip(test_data.content_key, content_value.clone());
577567

578568
match content_value {
579569
BeaconContentValue::LightClientBootstrap(value) => {
@@ -590,19 +580,13 @@ mod test {
590580
#[case] fork_name: &str,
591581
#[case] expected_slot: u64,
592582
) {
593-
let file = read_file_from_tests_submodule(format!(
583+
let test_data: ContentItem<BeaconContentKey> = read_yaml_portal_spec_tests_file(format!(
594584
"tests/mainnet/beacon_chain/light_client/{fork_name}/updates.yaml",
595585
))
596586
.unwrap();
587+
let content_value: BeaconContentValue = test_data.content_value().unwrap();
597588

598-
let value: Value = serde_yaml::from_str(&file).unwrap();
599-
let content_key: BeaconContentKey =
600-
serde_yaml::from_value(value["content_key"].clone()).unwrap();
601-
let raw_content_value = Bytes::from_str(value["content_value"].as_str().unwrap()).unwrap();
602-
let content_value = BeaconContentValue::decode(&content_key, raw_content_value.as_ref())
603-
.expect("unable to decode content value");
604-
605-
assert_str_roundtrip(content_key, content_value.clone());
589+
assert_str_roundtrip(test_data.content_key, content_value.clone());
606590

607591
let update = match content_value {
608592
BeaconContentValue::LightClientUpdatesByRange(value) => value[0].update.clone(),
@@ -623,19 +607,13 @@ mod test {
623607
#[case] fork_name: &str,
624608
#[case] expected_slot: u64,
625609
) {
626-
let file = read_file_from_tests_submodule(format!(
610+
let test_data: ContentItem<BeaconContentKey> = read_yaml_portal_spec_tests_file(format!(
627611
"tests/mainnet/beacon_chain/light_client/{fork_name}/optimistic_update.yaml",
628612
))
629613
.unwrap();
614+
let content_value: BeaconContentValue = test_data.content_value().unwrap();
630615

631-
let value: Value = serde_yaml::from_str(&file).unwrap();
632-
let content_key: BeaconContentKey =
633-
serde_yaml::from_value(value["content_key"].clone()).unwrap();
634-
let raw_content_value = Bytes::from_str(value["content_value"].as_str().unwrap()).unwrap();
635-
let content_value = BeaconContentValue::decode(&content_key, raw_content_value.as_ref())
636-
.expect("unable to decode content value");
637-
638-
assert_str_roundtrip(content_key, content_value.clone());
616+
assert_str_roundtrip(test_data.content_key, content_value.clone());
639617

640618
let update = match content_value {
641619
BeaconContentValue::LightClientOptimisticUpdate(value) => value.update,
@@ -656,19 +634,13 @@ mod test {
656634
#[case] fork_name: &str,
657635
#[case] expected_slot: u64,
658636
) {
659-
let file = read_file_from_tests_submodule(format!(
660-
"tests/mainnet/beacon_chain/light_client/{fork_name}/finality_update.yaml"
637+
let test_data: ContentItem<BeaconContentKey> = read_yaml_portal_spec_tests_file(format!(
638+
"tests/mainnet/beacon_chain/light_client/{fork_name}/finality_update.yaml",
661639
))
662640
.unwrap();
641+
let content_value: BeaconContentValue = test_data.content_value().unwrap();
663642

664-
let value: Value = serde_yaml::from_str(&file).unwrap();
665-
let content_key: BeaconContentKey =
666-
serde_yaml::from_value(value["content_key"].clone()).unwrap();
667-
let raw_content_value = Bytes::from_str(value["content_value"].as_str().unwrap()).unwrap();
668-
let content_value = BeaconContentValue::decode(&content_key, raw_content_value.as_ref())
669-
.expect("unable to decode content value");
670-
671-
assert_str_roundtrip(content_key, content_value.clone());
643+
assert_str_roundtrip(test_data.content_key, content_value.clone());
672644

673645
let update = match content_value {
674646
BeaconContentValue::LightClientFinalityUpdate(value) => value.update,
@@ -684,10 +656,9 @@ mod test {
684656

685657
#[test]
686658
fn deneb_historical_summaries_with_proof_encode_decode() {
687-
let file = read_file_from_tests_submodule(
659+
let value: serde_yaml::Value = read_yaml_portal_spec_tests_file(
688660
"tests/mainnet/beacon_chain/historical_summaries_with_proof/deneb/historical_summaries_with_proof.yaml",
689661
).unwrap();
690-
let value: serde_yaml::Value = serde_yaml::from_str(&file).unwrap();
691662
let content_key = BeaconContentKey::deserialize(&value["content_key"]).unwrap();
692663
let content_bytes = RawContentValue::deserialize(&value["content_value"]).unwrap();
693664
let beacon_content = BeaconContentValue::decode(&content_key, &content_bytes).unwrap();

crates/ethportal-api/src/types/content_value/state.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ mod test {
126126
use serde_yaml::Value;
127127

128128
use super::*;
129-
use crate::test_utils::read_file_from_tests_submodule;
129+
use crate::test_utils::read_yaml_portal_spec_tests_file;
130130

131131
const TEST_DATA_DIRECTORY: &str = "tests/mainnet/state/serialization";
132132

@@ -273,9 +273,7 @@ mod test {
273273
}
274274

275275
fn read_yaml_file(filename: &str) -> Result<Value> {
276-
let path = PathBuf::from(TEST_DATA_DIRECTORY).join(filename);
277-
let file = read_file_from_tests_submodule(path)?;
278-
Ok(serde_yaml::from_str(&file)?)
276+
read_yaml_portal_spec_tests_file(PathBuf::from(TEST_DATA_DIRECTORY).join(filename))
279277
}
280278

281279
fn yaml_to_bytes(value: &Value) -> Vec<u8> {

0 commit comments

Comments
 (0)