-
Notifications
You must be signed in to change notification settings - Fork 189
fix(test): JSON snapshot tests for actor states #6381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ use crate::shim::actors::evm::{State, TombstoneState}; | |
| use ::cid::Cid; | ||
| use pastey::paste; | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, JsonSchema)] | ||
| #[derive(Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| #[schemars(rename = "EVMState")] | ||
| pub struct EVMStateLotusJson { | ||
|
|
@@ -35,60 +35,67 @@ macro_rules! impl_evm_state_lotus_json { | |
| (no_transient_data: $($version:literal),+) => { | ||
| $( | ||
| paste! { | ||
| impl HasLotusJson for fil_actor_evm_state::[<v $version>]::State { | ||
| type LotusJson = EVMStateLotusJson; | ||
|
|
||
| #[cfg(test)] | ||
| fn snapshots() -> Vec<(serde_json::Value, Self)> { | ||
| vec![( | ||
| json!({ | ||
| "Bytecode": {"/":"baeaaaaa"}, | ||
| "BytecodeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
| "ContractState": {"/":"baeaaaaa"}, | ||
| "Nonce": 0, | ||
| "Tombstone": null, | ||
| "TransientData": null | ||
| }), | ||
| Self { | ||
| bytecode: Cid::default(), | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from([0; 32]), | ||
| contract_state: Cid::default(), | ||
| nonce: 0, | ||
| tombstone: None, | ||
| }, | ||
| )] | ||
| mod [<impl_evm_state_lotus_json_ $version>] { | ||
| use super::*; | ||
| type T = fil_actor_evm_state::[<v $version>]::State; | ||
| #[test] | ||
| fn snapshots() { | ||
| crate::lotus_json::assert_all_snapshots::<T>(); | ||
| } | ||
|
Comment on lines
+41
to
44
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Similar to the issue in 🔎 Proposed fix mod [<impl_evm_state_lotus_json_ $version>] {
use super::*;
type T = fil_actor_evm_state::[<v $version>]::State;
+ #[cfg(test)]
#[test]
fn snapshots() {
crate::lotus_json::assert_all_snapshots::<T>();
}Apply to both macro arms (no_transient_data and with_transient_data). Also applies to: 112-115 🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CI proves the code compiles fine
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hanabi1224, you're absolutely right! I apologize for the incorrect suggestion. Functions marked with Thank you for the clarification! ✏️ Learnings added
🧠 Learnings used |
||
| impl HasLotusJson for T { | ||
| type LotusJson = EVMStateLotusJson; | ||
| #[cfg(test)] | ||
| fn snapshots() -> Vec<(serde_json::Value, Self)> { | ||
| vec![( | ||
| json!({ | ||
| "Bytecode": {"/":"baeaaaaa"}, | ||
| "BytecodeHash": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], | ||
| "ContractState": {"/":"baeaaaaa"}, | ||
| "Nonce": 0, | ||
| "Tombstone": null, | ||
| "TransientData": null | ||
| }), | ||
| Self { | ||
| bytecode: Cid::default(), | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from([0; 32]), | ||
| contract_state: Cid::default(), | ||
| nonce: 0, | ||
| tombstone: None, | ||
| }, | ||
| )] | ||
| } | ||
|
|
||
| fn into_lotus_json(self) -> Self::LotusJson { | ||
| let bytecode_hash_data: [u8; 32] = self.bytecode_hash.into(); | ||
|
|
||
| EVMStateLotusJson { | ||
| bytecode: self.bytecode, | ||
| bytecode_hash: bytecode_hash_data, | ||
| contract_state: self.contract_state, | ||
| nonce: self.nonce, | ||
| tombstone: self.tombstone.map(|t| { | ||
| let tombstone_state = TombstoneState::[<V $version>](t); | ||
| tombstone_state.into_lotus_json() | ||
| }), | ||
| transient_data: None, | ||
| fn into_lotus_json(self) -> Self::LotusJson { | ||
| let bytecode_hash_data: [u8; 32] = self.bytecode_hash.into(); | ||
|
|
||
| EVMStateLotusJson { | ||
| bytecode: self.bytecode, | ||
| bytecode_hash: bytecode_hash_data, | ||
| contract_state: self.contract_state, | ||
| nonce: self.nonce, | ||
| tombstone: self.tombstone.map(|t| { | ||
| let tombstone_state = TombstoneState::[<V $version>](t); | ||
| tombstone_state.into_lotus_json() | ||
| }), | ||
| transient_data: None, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { | ||
| let tombstone = lotus_json.tombstone.map(|tombstone_lotus| { | ||
| fil_actor_evm_state::[<v $version>]::Tombstone { | ||
| origin: tombstone_lotus.origin.into(), | ||
| nonce: tombstone_lotus.nonce, | ||
| fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { | ||
| let tombstone = lotus_json.tombstone.map(|tombstone_lotus| { | ||
| fil_actor_evm_state::[<v $version>]::Tombstone { | ||
| origin: tombstone_lotus.origin.into(), | ||
| nonce: tombstone_lotus.nonce, | ||
| } | ||
| }); | ||
|
|
||
| Self { | ||
| bytecode: lotus_json.bytecode, | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from(lotus_json.bytecode_hash), | ||
| contract_state: lotus_json.contract_state, | ||
| nonce: lotus_json.nonce, | ||
| tombstone, | ||
| } | ||
| }); | ||
|
|
||
| Self { | ||
| bytecode: lotus_json.bytecode, | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from(lotus_json.bytecode_hash), | ||
| contract_state: lotus_json.contract_state, | ||
| nonce: lotus_json.nonce, | ||
| tombstone, | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -99,70 +106,78 @@ macro_rules! impl_evm_state_lotus_json { | |
| (with_transient_data: $($version:literal),+) => { | ||
| $( | ||
| paste! { | ||
| impl HasLotusJson for fil_actor_evm_state::[<v $version>]::State { | ||
| type LotusJson = EVMStateLotusJson; | ||
|
|
||
| #[cfg(test)] | ||
| fn snapshots() -> Vec<(serde_json::Value, Self)> { | ||
| vec![( | ||
| json!({ | ||
| "Bytecode": {"/":"baeaaaaa"}, | ||
| "BytecodeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
| "ContractState": {"/":"baeaaaaa"}, | ||
| "Nonce": 0, | ||
| "Tombstone": null, | ||
| "TransientData": null | ||
| }), | ||
| Self { | ||
| bytecode: Cid::default(), | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from([0; 32]), | ||
| contract_state: Cid::default(), | ||
| nonce: 0, | ||
| tombstone: None, | ||
| transient_data: None, | ||
| }, | ||
| )] | ||
| mod [<impl_evm_state_lotus_json_ $version>] { | ||
| use super::*; | ||
| type T = fil_actor_evm_state::[<v $version>]::State; | ||
| #[test] | ||
| fn snapshots() { | ||
| crate::lotus_json::assert_all_snapshots::<T>(); | ||
| } | ||
|
|
||
| fn into_lotus_json(self) -> Self::LotusJson { | ||
| let bytecode_hash_data: [u8; 32] = self.bytecode_hash.into(); | ||
|
|
||
| EVMStateLotusJson { | ||
| bytecode: self.bytecode, | ||
| bytecode_hash: bytecode_hash_data, | ||
| contract_state: self.contract_state, | ||
| nonce: self.nonce, | ||
| tombstone: self.tombstone.map(|t| { | ||
| let tombstone_state = TombstoneState::[<V $version>](t); | ||
| tombstone_state.into_lotus_json() | ||
| }), | ||
| transient_data: Some(self.transient_data.map(|td| { | ||
| td.into_lotus_json() | ||
| })), | ||
| impl HasLotusJson for T { | ||
| type LotusJson = EVMStateLotusJson; | ||
|
|
||
| #[cfg(test)] | ||
| fn snapshots() -> Vec<(serde_json::Value, Self)> { | ||
| vec![( | ||
| json!({ | ||
| "Bytecode": {"/":"baeaaaaa"}, | ||
| "BytecodeHash": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], | ||
| "ContractState": {"/":"baeaaaaa"}, | ||
| "Nonce": 0, | ||
| "Tombstone": null, | ||
| "TransientData": null | ||
| }), | ||
| Self { | ||
| bytecode: Cid::default(), | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from([0; 32]), | ||
| contract_state: Cid::default(), | ||
| nonce: 0, | ||
| tombstone: None, | ||
| transient_data: None, | ||
| }, | ||
| )] | ||
| } | ||
| } | ||
|
|
||
| fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { | ||
| let tombstone = lotus_json.tombstone.map(|tombstone_lotus| { | ||
| fil_actor_evm_state::[<v $version>]::Tombstone { | ||
| origin: tombstone_lotus.origin.into(), | ||
| nonce: tombstone_lotus.nonce, | ||
| fn into_lotus_json(self) -> Self::LotusJson { | ||
| let bytecode_hash_data: [u8; 32] = self.bytecode_hash.into(); | ||
|
|
||
| EVMStateLotusJson { | ||
| bytecode: self.bytecode, | ||
| bytecode_hash: bytecode_hash_data, | ||
| contract_state: self.contract_state, | ||
| nonce: self.nonce, | ||
| tombstone: self.tombstone.map(|t| { | ||
| let tombstone_state = TombstoneState::[<V $version>](t); | ||
| tombstone_state.into_lotus_json() | ||
| }), | ||
| transient_data: Some(self.transient_data.map(|td| { | ||
| td.into_lotus_json() | ||
| })), | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| let transient_data = lotus_json.transient_data | ||
| .and_then(|outer_option| outer_option) | ||
| .map(|transient_data_lotus| { | ||
| fil_actor_evm_state::[<v $version>]::TransientData::from_lotus_json(transient_data_lotus) | ||
| fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { | ||
| let tombstone = lotus_json.tombstone.map(|tombstone_lotus| { | ||
| fil_actor_evm_state::[<v $version>]::Tombstone { | ||
| origin: tombstone_lotus.origin.into(), | ||
| nonce: tombstone_lotus.nonce, | ||
| } | ||
| }); | ||
|
|
||
| Self { | ||
| bytecode: lotus_json.bytecode, | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from(lotus_json.bytecode_hash), | ||
| contract_state: lotus_json.contract_state, | ||
| nonce: lotus_json.nonce, | ||
| tombstone, | ||
| transient_data, | ||
| let transient_data = lotus_json.transient_data | ||
| .and_then(|outer_option| outer_option) | ||
| .map(|transient_data_lotus| { | ||
| fil_actor_evm_state::[<v $version>]::TransientData::from_lotus_json(transient_data_lotus) | ||
| }); | ||
|
|
||
| Self { | ||
| bytecode: lotus_json.bytecode, | ||
| bytecode_hash: fil_actor_evm_state::[<v $version>]::BytecodeHash::from(lotus_json.bytecode_hash), | ||
| contract_state: lotus_json.contract_state, | ||
| nonce: lotus_json.nonce, | ||
| tombstone, | ||
| transient_data, | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -179,7 +194,7 @@ impl HasLotusJson for State { | |
| vec![( | ||
| json!({ | ||
| "Bytecode": {"/":"baeaaaaa"}, | ||
| "BytecodeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
| "BytecodeHash": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], | ||
| "ContractState": {"/":"baeaaaaa"}, | ||
| "Nonce": 0, | ||
| "Tombstone": null, | ||
|
|
@@ -216,6 +231,7 @@ impl HasLotusJson for State { | |
| ) | ||
| } | ||
| } | ||
| crate::test_snapshots!(State); | ||
|
|
||
| // Implement for versions without transient_data (v10-v15) | ||
| impl_evm_state_lotus_json!(no_transient_data: 10, 11, 12, 13, 14, 15); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ use crate::shim::{clock::ChainEpoch, econ::TokenAmount}; | |
| use ::cid::Cid; | ||
| use fvm_shared4::deal::DealID; | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, JsonSchema)] | ||
| #[derive(Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| #[schemars(rename = "MarketState")] | ||
| pub struct MarketStateLotusJson { | ||
|
|
@@ -131,7 +131,7 @@ impl HasLotusJson for State { | |
| "TotalClientLockedCollateral": "0", | ||
| "TotalProviderLockedCollateral": "0", | ||
| "TotalClientStorageFee": "0", | ||
| "PendingDealAllocationIDs": {"/":"baeaaaaa"}, | ||
| "PendingDealAllocationIds": {"/":"baeaaaaa"}, | ||
| "ProviderSectors": {"/":"baeaaaaa"} | ||
|
Comment on lines
+134
to
135
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's look at the market_state.rs file to understand the full context
fd -t f "market_state.rs" | head -5Repository: ChainSafe/forest Length of output: 104 🏁 Script executed: # Read the market_state.rs file to see the struct definition and serde attributes
cat -n src/lotus_json/actors/states/market_state.rs | head -100Repository: ChainSafe/forest Length of output: 4143 🏁 Script executed: # Search for PendingDealAllocation references in the codebase
rg -n "PendingDealAllocation" --type rust -C 2Repository: ChainSafe/forest Length of output: 534 🏁 Script executed: # Check for NextID patterns and other explicit renames in the same file
rg -n "rename = " src/lotus_json/actors/states/market_state.rsRepository: ChainSafe/forest Length of output: 161 🏁 Script executed: # Search for test files related to market_state
fd -t f "market_state" --type rust | grep -i testRepository: ChainSafe/forest Length of output: 231 🏁 Script executed: # Look for JSON examples or test data that might show the expected format
rg -l "PendingDealAllocationIds\|PendingDealAllocationIDs" --type rustRepository: ChainSafe/forest Length of output: 42 🏁 Script executed: # Check if there are any Lotus-related documentation or comments about the format
rg -n "Lotus\|lotus\|API" src/lotus_json/actors/states/market_state.rs -C 2Repository: ChainSafe/forest Length of output: 42 🏁 Script executed: # Search for references in tests to see how this field is used
rg -n "pending_deal_allocation" --type rust -C 3Repository: ChainSafe/forest Length of output: 13223 🏁 Script executed: # Look for test JSON data in the repo
find . -name "*.json" -type f | head -20Repository: ChainSafe/forest Length of output: 811 🏁 Script executed: # Search for references to Lotus documentation or API specs
rg -n "lotus\|Lotus" src/lotus_json/actors/states/market_state.rs -A 2 -B 2Repository: ChainSafe/forest Length of output: 42 🏁 Script executed: # Look for comparison with NextID pattern - check other ID fields
rg -n "rename = \".*ID\"" src/lotus_json/ --type rustRepository: ChainSafe/forest Length of output: 1552 🏁 Script executed: # Check git history or comments around this field
git log -p --follow -S "pending_deal_allocation" -- src/lotus_json/actors/states/market_state.rs 2>/dev/null | head -100Repository: ChainSafe/forest Length of output: 42 🏁 Script executed: # Look for test files that might contain market state JSON examples
find . -path ./node_modules -prune -o -name "*market*" -type f | grep -E "\.(json|txt|toml)" | head -20Repository: ChainSafe/forest Length of output: 42 🏁 Script executed: # Search for test functions that serialize/deserialize MarketStateLotusJson
rg -n "MarketStateLotusJson" --type rust -A 5 -B 2 | head -80Repository: ChainSafe/forest Length of output: 3886 🏁 Script executed: # Check if there's any documentation about the field name format
rg -n "PendingDealAllocationIDs\|AllocationIds" --type rustRepository: ChainSafe/forest Length of output: 42 🏁 Script executed: # Get the test snapshot JSON to see the actual expected format
sed -n '119,170p' src/lotus_json/actors/states/market_state.rsRepository: ChainSafe/forest Length of output: 1828 Add explicit rename to preserve uppercase "IDs" in field name. The field should use 🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @akaladarshi does this comment make sense?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hanabi1224 It would have been relevant if the lotus was consistent with their naming convention, but right now Lotus is using If the naming was consistent we could just use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! So the current field key is correct and no changes are needed.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes |
||
| }), | ||
| State::default_latest_version( | ||
|
|
@@ -195,3 +195,4 @@ impl HasLotusJson for State { | |
| ) | ||
| } | ||
| } | ||
| crate::test_snapshots!(State); | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Missing
Eqderive creates inconsistency.CronStateLotusJsonderives onlyPartialEq, notEq, which is inconsistent with other state structs in this PR (InitStateLotusJson,TombstoneLotusJson,PaymentChannelStateLotusJsonall derive both). This happens becauseEntry(fromcron/mod.rs) doesn't deriveEq. OnceEntryis updated to deriveEq, this struct should also be updated for consistency.🔎 Proposed fix (apply after fixing Entry)
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eqis not applicable for external dependenciesfil_actor_cron_state::v8::EntryThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hanabi1224, thank you for the clarification! You're absolutely right—since
Entrywraps external types fromfil_actor_cron_statethat don't implementEq, it's not possible forCronStateLotusJsonto deriveEqeither. Deriving onlyPartialEqis the correct approach here given this constraint.The inconsistency with other state structs that derive both
EqandPartialEqis justified and unavoidable in this case.✏️ Learnings added
🧠 Learnings used