Skip to content

Commit fcead6b

Browse files
committed
test: rm integration tests + unit tests for state
Signed-off-by: Carson Farmer <carson.farmer@gmail.com>
1 parent 2c0cdf3 commit fcead6b

4 files changed

Lines changed: 189 additions & 173 deletions

File tree

fendermint/actors/objectstore/src/actor.rs

Lines changed: 19 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR;
88
use fil_actors_runtime::runtime::{ActorCode, Runtime};
99
use fil_actors_runtime::ActorDowncast;
1010
use fil_actors_runtime::ActorError;
11-
use fil_actors_runtime::Map;
1211
use fvm_ipld_hamt::BytesKey;
1312
use fvm_shared::error::ExitCode;
1413

1514
use crate::DeleteObjectParams;
16-
use crate::{Method, PutObjectParams, State, BIT_WIDTH, OBJECTSTORE_ACTOR_NAME};
15+
use crate::{Method, PutObjectParams, State, OBJECTSTORE_ACTOR_NAME};
1716

1817
fil_actors_runtime::wasm_trampoline!(Actor);
1918

@@ -26,7 +25,10 @@ impl Actor {
2625
rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?;
2726

2827
let state = State::new(rt.store()).map_err(|e| {
29-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to create empty Hamt")
28+
e.downcast_default(
29+
ExitCode::USR_ILLEGAL_STATE,
30+
"failed to construct empty store",
31+
)
3032
})?;
3133

3234
rt.create(&state)
@@ -37,37 +39,10 @@ impl Actor {
3739
rt.validate_immediate_caller_accept_any()?;
3840

3941
rt.transaction(|st: &mut State, rt| {
40-
// Load the root Hamt
41-
let mut hamt = Map::<_, Vec<u8>>::load_with_bit_width(&st.root, rt.store(), BIT_WIDTH)
42+
st.append(rt.store(), BytesKey(params.key), params.content)
4243
.map_err(|e| {
43-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load Hamt root")
44-
})?;
45-
46-
let key = BytesKey(params.key);
47-
48-
let new_content = match hamt.get(&key).map_err(|e| {
49-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get object")
50-
})? {
51-
Some(existing) => {
52-
// Append the object to the existing object
53-
let mut new_content = existing.clone();
54-
new_content.extend(params.content);
55-
new_content
56-
}
57-
None => params.content,
58-
};
59-
60-
// Put the new content into the Hamt
61-
hamt.set(key, new_content).map_err(|e| {
62-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to update key")
63-
})?;
64-
65-
// Save the new Hamt cid root
66-
st.root = hamt.flush().map_err(|e| {
67-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save root")
68-
})?;
69-
70-
Ok(())
44+
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to append to object")
45+
})
7146
})?;
7247

7348
Ok(())
@@ -78,25 +53,10 @@ impl Actor {
7853
rt.validate_immediate_caller_accept_any()?;
7954

8055
rt.transaction(|st: &mut State, rt| {
81-
// Load the root Hamt
82-
let mut hamt =
83-
Map::load_with_bit_width(&st.root, rt.store(), BIT_WIDTH).map_err(|e| {
84-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load Hamt root")
85-
})?;
86-
87-
// Put the object into the Hamt
88-
// TODO: We could use set_if_absent here to avoid overwriting existing objects.
89-
hamt.set(BytesKey(params.key), params.content)
56+
st.put(rt.store(), BytesKey(params.key), params.content)
9057
.map_err(|e| {
91-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to update key")
92-
})?;
93-
94-
// Save the new Hamt cid root
95-
st.root = hamt.flush().map_err(|e| {
96-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save root")
97-
})?;
98-
99-
Ok(())
58+
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to put object")
59+
})
10060
})?;
10161

10262
Ok(())
@@ -107,23 +67,9 @@ impl Actor {
10767
rt.validate_immediate_caller_accept_any()?;
10868

10969
rt.transaction(|st: &mut State, rt| {
110-
// Load the root Hamt
111-
let mut hamt = Map::<_, Vec<u8>>::load_with_bit_width(&st.root, rt.store(), BIT_WIDTH)
112-
.map_err(|e| {
113-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load Hamt root")
114-
})?;
115-
116-
// Delete the object from the Hamt
117-
hamt.delete(&BytesKey(params.key)).map_err(|e| {
118-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to delete object")
119-
})?;
120-
121-
// Save the new Hamt cid root
122-
st.root = hamt.flush().map_err(|e| {
123-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save root")
124-
})?;
125-
126-
Ok(())
70+
st.delete(rt.store(), &BytesKey(params.key)).map_err(|e| {
71+
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to delete to object")
72+
})
12773
})?;
12874

12975
Ok(())
@@ -132,37 +78,17 @@ impl Actor {
13278
fn get_object(rt: &impl Runtime, key: Vec<u8>) -> Result<Option<Vec<u8>>, ActorError> {
13379
let st: State = rt.state()?;
13480

135-
// Load the root Hamt
136-
let hamt = Map::<_, Vec<u8>>::load_with_bit_width(&st.root, rt.store(), BIT_WIDTH)
137-
.map_err(|e| {
138-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load Hamt root")
139-
})?;
140-
141-
// Get the object from the Hamt
142-
hamt.get(&BytesKey(key))
81+
st.get(rt.store(), &BytesKey(key))
14382
.map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get object"))
144-
.map(|v| v.map(|inner| inner.to_owned()))
14583
}
14684

14785
fn list_objects(rt: &impl Runtime) -> Result<Option<Vec<Vec<u8>>>, ActorError> {
14886
let st: State = rt.state()?;
14987

150-
// Load the root Hamt
151-
let hamt = Map::<_, Vec<u8>>::load_with_bit_width(&st.root, rt.store(), BIT_WIDTH)
152-
.map_err(|e| {
153-
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load Hamt root")
154-
})?;
155-
156-
let mut keys = Vec::new();
157-
158-
// List the keys from each item in the Hamt
159-
hamt.for_each(|k, _| {
160-
keys.push(k.0.to_owned());
161-
Ok(())
162-
})
163-
.map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to list objects"))?;
164-
165-
Ok(Some(keys))
88+
let objects = st.list(rt.store()).map_err(|e| {
89+
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to list objects")
90+
})?;
91+
Ok(Some(objects))
16692
}
16793
}
16894

fendermint/actors/objectstore/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SPDX-License-Identifier: Apache-2.0, MIT
44

55
#[cfg(feature = "fil-actor")]
6-
pub mod actor;
6+
mod actor;
77
mod shared;
88

99
pub use shared::*;

fendermint/actors/objectstore/src/shared.rs

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use cid::Cid;
66
use fvm_ipld_blockstore::Blockstore;
77
use fvm_ipld_encoding::tuple::{Deserialize_tuple, Serialize_tuple};
8-
use fvm_ipld_hamt::Hamt;
8+
use fvm_ipld_hamt::{BytesKey, Hamt};
99
use fvm_shared::METHOD_CONSTRUCTOR;
1010
use num_derive::FromPrimitive;
1111

@@ -34,6 +34,74 @@ impl State {
3434

3535
Ok(Self { root })
3636
}
37+
38+
pub fn append<BS: Blockstore>(
39+
&mut self,
40+
store: &BS,
41+
key: BytesKey,
42+
content: Vec<u8>,
43+
) -> anyhow::Result<()> {
44+
let mut hamt = Hamt::<_, Vec<u8>>::load_with_bit_width(&self.root, store, BIT_WIDTH)?;
45+
46+
let new_content = match hamt.get(&key)? {
47+
Some(existing) => {
48+
// Append the object to the existing object
49+
let mut new_content = existing.clone();
50+
new_content.extend(content);
51+
new_content
52+
}
53+
None => content,
54+
};
55+
56+
hamt.set(key, new_content)?;
57+
self.root = hamt.flush()?;
58+
Ok(())
59+
}
60+
61+
pub fn put<BS: Blockstore>(
62+
&mut self,
63+
store: &BS,
64+
key: BytesKey,
65+
content: Vec<u8>,
66+
) -> anyhow::Result<()> {
67+
let mut hamt = Hamt::<_, Vec<u8>>::load_with_bit_width(&self.root, store, BIT_WIDTH)?;
68+
69+
// TODO: We could use set_if_absent here to avoid overwriting existing objects.
70+
hamt.set(key, content)?;
71+
self.root = hamt.flush()?;
72+
Ok(())
73+
}
74+
75+
pub fn delete<BS: Blockstore>(&mut self, store: &BS, key: &BytesKey) -> anyhow::Result<()> {
76+
let mut hamt = Hamt::<_, Vec<u8>>::load_with_bit_width(&self.root, store, BIT_WIDTH)?;
77+
78+
hamt.delete(key)?;
79+
self.root = hamt.flush()?;
80+
Ok(())
81+
}
82+
83+
pub fn get<BS: Blockstore>(
84+
&self,
85+
store: &BS,
86+
key: &BytesKey,
87+
) -> anyhow::Result<Option<Vec<u8>>> {
88+
let hamt = Hamt::<_, Vec<u8>>::load_with_bit_width(&self.root, store, BIT_WIDTH)?;
89+
90+
let value = hamt.get(key).map(|v| v.map(|inner| inner.to_owned()))?;
91+
Ok(value)
92+
}
93+
94+
pub fn list<BS: Blockstore>(&self, store: &BS) -> anyhow::Result<Vec<Vec<u8>>> {
95+
let hamt = Hamt::<_, Vec<u8>>::load_with_bit_width(&self.root, store, BIT_WIDTH)?;
96+
97+
let mut keys = Vec::new();
98+
hamt.for_each(|k, _| {
99+
keys.push(k.0.to_owned());
100+
Ok(())
101+
})?;
102+
103+
Ok(keys)
104+
}
37105
}
38106

39107
pub const OBJECTSTORE_ACTOR_NAME: &str = "objectstore";
@@ -59,3 +127,103 @@ pub enum Method {
59127
DeleteObject = frc42_dispatch::method_hash!("DeleteObject"),
60128
ListObjects = frc42_dispatch::method_hash!("ListObjects"),
61129
}
130+
131+
#[cfg(test)]
132+
mod tests {
133+
use std::str::FromStr;
134+
135+
use super::*;
136+
137+
#[test]
138+
fn test_constructor() {
139+
let store = fvm_ipld_blockstore::MemoryBlockstore::default();
140+
let state = State::new(&store);
141+
assert!(state.is_ok());
142+
assert_eq!(
143+
state.unwrap().root,
144+
Cid::from_str("bafy2bzaceamp42wmmgr2g2ymg46euououzfyck7szknvfacqscohrvaikwfay")
145+
.unwrap()
146+
);
147+
}
148+
149+
#[test]
150+
fn test_put_object() {
151+
let store = fvm_ipld_blockstore::MemoryBlockstore::default();
152+
let mut state = State::new(&store).unwrap();
153+
let params = PutObjectParams {
154+
key: vec![1, 2, 3],
155+
content: vec![4, 5, 6],
156+
};
157+
assert!(state
158+
.put(&store, BytesKey(params.key), params.content)
159+
.is_ok());
160+
161+
assert_eq!(
162+
state.root,
163+
Cid::from_str("bafy2bzacedojzjpwtx565wt43ard7e3kordcw4hf7bbpywpnkctasuwfh7c5m")
164+
.unwrap()
165+
);
166+
}
167+
168+
#[test]
169+
fn test_append_object() {
170+
let store = fvm_ipld_blockstore::MemoryBlockstore::default();
171+
let mut state = State::new(&store).unwrap();
172+
let params = PutObjectParams {
173+
key: vec![1, 2, 3],
174+
content: vec![4, 5, 6],
175+
};
176+
assert!(state
177+
.append(&store, BytesKey(params.key), params.content)
178+
.is_ok());
179+
}
180+
181+
#[test]
182+
fn test_get_object() {
183+
let store = fvm_ipld_blockstore::MemoryBlockstore::default();
184+
let mut state = State::new(&store).unwrap();
185+
let params = PutObjectParams {
186+
key: vec![1, 2, 3],
187+
content: vec![4, 5, 6],
188+
};
189+
190+
let key = BytesKey(params.key);
191+
state.put(&store, key.clone(), params.content).unwrap();
192+
let result = state.get(&store, &key);
193+
assert!(result.is_ok());
194+
assert_eq!(result.unwrap(), Some(vec![4, 5, 6]));
195+
}
196+
197+
#[test]
198+
fn test_delete_object() {
199+
let store = fvm_ipld_blockstore::MemoryBlockstore::default();
200+
let mut state = State::new(&store).unwrap();
201+
let params = PutObjectParams {
202+
key: vec![1, 2, 3],
203+
content: vec![4, 5, 6],
204+
};
205+
let key = BytesKey(params.key);
206+
state.put(&store, key.clone(), params.content).unwrap();
207+
assert!(state.delete(&store, &key).is_ok());
208+
209+
let result = state.get(&store, &key);
210+
assert!(result.is_ok());
211+
assert_eq!(result.unwrap(), None);
212+
}
213+
214+
#[test]
215+
fn test_list_objects() {
216+
let store = fvm_ipld_blockstore::MemoryBlockstore::default();
217+
let mut state = State::new(&store).unwrap();
218+
let params = PutObjectParams {
219+
key: vec![1, 2, 3],
220+
content: vec![4, 5, 6],
221+
};
222+
state
223+
.put(&store, BytesKey(params.key), params.content)
224+
.unwrap();
225+
let result = state.list(&store);
226+
assert!(result.is_ok());
227+
assert_eq!(result.unwrap(), vec![vec![1, 2, 3]]);
228+
}
229+
}

0 commit comments

Comments
 (0)