Skip to content

Commit

Permalink
Merge pull request #12 from fission-codes/test-deleg-store
Browse files Browse the repository at this point in the history
Test delegation store
  • Loading branch information
matheus23 authored Mar 26, 2024
2 parents 643b466 + 6affc01 commit 13510c5
Show file tree
Hide file tree
Showing 12 changed files with 983 additions and 308 deletions.
7 changes: 7 additions & 0 deletions proptest-regressions/delegation/policy/selector/select.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 6496f8ae07f0fa0d57c9bc4d581551bc9940c50fe830880006156471a72e806b # shrinks to data = Newtype(null), more = [ArrayIndex(0)]
4 changes: 2 additions & 2 deletions src/crypto/signature/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,11 @@ pub trait Envelope: Sized {

fn cid(&self) -> Result<Cid, libipld_core::error::Error>
where
Self: Encode<Self::Encoder>,
Ipld: Encode<Self::Encoder>,
{
let codec = varsig::header::Header::codec(self.varsig_header()).clone();
let mut ipld_buffer = vec![];
self.encode(codec, &mut ipld_buffer)?;
self.to_ipld_envelope().encode(codec, &mut ipld_buffer)?;

let multihash = Code::Sha2_256.digest(&ipld_buffer);
Ok(Cid::new_v1(
Expand Down
32 changes: 18 additions & 14 deletions src/delegation/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,33 @@ use web_time::SystemTime;
/// This is helpful for sessions where more than one delegation will be made.
#[derive(Debug)]
pub struct Agent<
S: Store<DID, V, Enc>,
DID: Did = did::preset::Verifier,
V: varsig::Header<Enc> + Clone = varsig::header::Preset,
Enc: Codec + Into<u64> + TryFrom<u64> = varsig::encoding::Preset,
> {
S: Store<DID, V, C>,
DID: Did + Clone = did::preset::Verifier,
V: varsig::Header<C> + Clone = varsig::header::Preset,
C: Codec + Into<u64> + TryFrom<u64> = varsig::encoding::Preset,
> where
Ipld: Encode<C>,
Payload<DID>: TryFrom<Named<Ipld>>,
Named<Ipld>: From<Payload<DID>>,
{
/// The [`Did`][Did] of the agent.
pub did: DID,

/// The attached [`deleagtion::Store`][super::store::Store].
pub store: S,

signer: <DID as Did>::Signer,
_marker: PhantomData<(V, Enc)>,
_marker: PhantomData<(V, C)>,
}

impl<
S: Store<DID, V, Enc> + Clone,
S: Store<DID, V, C> + Clone,
DID: Did + Clone,
V: varsig::Header<Enc> + Clone,
Enc: Codec + TryFrom<u64> + Into<u64>,
> Agent<S, DID, V, Enc>
V: varsig::Header<C> + Clone,
C: Codec + TryFrom<u64> + Into<u64>,
> Agent<S, DID, V, C>
where
Ipld: Encode<Enc>,
Ipld: Encode<C>,
Payload<DID>: TryFrom<Named<Ipld>>,
Named<Ipld>: From<Payload<DID>>,
{
Expand All @@ -67,7 +71,7 @@ where
not_before: Option<Timestamp>,
now: SystemTime,
varsig_header: V,
) -> Result<Delegation<DID, V, Enc>, DelegateError<S::DelegationStoreError>> {
) -> Result<Delegation<DID, V, C>, DelegateError<S::DelegationStoreError>> {
let mut salt = self.did.clone().to_string().into_bytes();
let nonce = Nonce::generate_12(&mut salt);

Expand Down Expand Up @@ -121,7 +125,7 @@ where
pub fn receive(
&self,
cid: Cid, // FIXME remove and generate from the capsule header?
delegation: Delegation<DID, V, Enc>,
delegation: Delegation<DID, V, C>,
) -> Result<(), ReceiveError<S::DelegationStoreError, DID>> {
if self.store.get(&cid).is_ok() {
return Ok(());
Expand All @@ -135,7 +139,7 @@ where
.validate_signature()
.map_err(|_| ReceiveError::InvalidSignature(cid))?;

self.store.insert(cid, delegation).map_err(Into::into)
self.store.insert_keyed(cid, delegation).map_err(Into::into)
}
}

Expand Down
33 changes: 14 additions & 19 deletions src/delegation/policy/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,20 @@ impl Predicate {
Predicate::Not(inner) => !inner.run(data)?,
Predicate::And(lhs, rhs) => lhs.run(data)? && rhs.run(data)?,
Predicate::Or(lhs, rhs) => lhs.run(data)? || rhs.run(data)?,
Predicate::Every(xs, p) => {
xs.get(data)?
.to_vec()
.iter()
.try_fold(true, |acc, each_datum| {
dbg!("every", &p, acc, each_datum);
Ok(acc && p.clone().run(&each_datum.0)?)
})?
}
Predicate::Some(xs, p) => {
xs.get(data)?
.to_vec()
.iter()
.try_fold(false, |acc, each_datum| {
dbg!("some", &p, acc, each_datum);
Ok(acc || p.clone().run(&each_datum.0)?)
})?
}
Predicate::Every(xs, p) => xs
.get(data)?
.to_vec()
.iter()
.try_fold(true, |acc, each_datum| {
Ok(acc && p.clone().run(&each_datum.0)?)
})?,
Predicate::Some(xs, p) => xs
.get(data)?
.to_vec()
.iter()
.try_fold(false, |acc, each_datum| {
Ok(acc || p.clone().run(&each_datum.0)?)
})?,
})
}

Expand Down Expand Up @@ -1136,7 +1132,6 @@ mod tests {

#[test_log::test]
fn test_eq_dot_field_inner_try_null() -> TestResult {
// FIXME double check against jq
let p = Predicate::Equal(Select::from_str(".nope?.not").unwrap(), Ipld::Null.into());

assert!(p.run(&email())?);
Expand Down
12 changes: 12 additions & 0 deletions src/delegation/policy/selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,18 @@ mod tests {
Ok(())
}

#[test_log::test]
fn test_inner_try_is_null() -> TestResult {
pretty::assert_eq!(
Selector::from_str(".nope?.not"),
Ok(Selector(vec![
Filter::Try(Box::new(Filter::Field("nope".into()))),
Filter::Field("not".into())
]))
);
Ok(())
}

#[test_log::test]
fn test_dot_many_tries_and_dot_field() -> TestResult {
pretty::assert_eq!(
Expand Down
152 changes: 122 additions & 30 deletions src/delegation/policy/selector/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ impl<T> Select<T> {

impl<T: Selectable> Select<T> {
pub fn get(self, ctx: &Ipld) -> Result<T, SelectorError> {
self.filters
.iter()
.try_fold((ctx.clone(), vec![]), |(ipld, mut seen_ops), op| {
let got = self.filters.iter().try_fold(
(ctx.clone(), vec![], false),
|(ipld, mut seen_ops, is_try), op| {
seen_ops.push(op);

match op {
Expand All @@ -57,70 +57,95 @@ impl<T: Selectable> Select<T> {
let ipld: Ipld =
Select::<Ipld>::new(vec![op]).get(ctx).unwrap_or(Ipld::Null);

Ok((ipld, seen_ops))
Ok((ipld, seen_ops.clone(), true))
}
Filter::ArrayIndex(i) => {
let result = {
match ipld {
Ipld::List(xs) => {
if i.abs() as usize > xs.len() {
return Err(SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::IndexOutOfBounds,
return Err((
is_try,
SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::IndexOutOfBounds,
),
));
};

xs.get((xs.len() as i32 + *i) as usize)
.ok_or(SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::IndexOutOfBounds,
.ok_or((
is_try,
SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::IndexOutOfBounds,
),
))
.cloned()
}
// FIXME behaviour on maps? type error
_ => Err(SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::NotAList,
_ => Err((
is_try,
SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::NotAList,
),
)),
}
};

Ok((result?, seen_ops))
Ok((result?, seen_ops.clone(), is_try))
}
Filter::Field(k) => {
let result = match ipld {
Ipld::Map(xs) => xs
.get(k)
.ok_or(SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::KeyNotFound,
.ok_or((
is_try,
SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::KeyNotFound,
),
))
.cloned(),
_ => Err(SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::NotAMap,
_ => Err((
is_try,
SelectorError::from_refs(&seen_ops, SelectorErrorReason::NotAMap),
)),
};

Ok((result?, seen_ops))
Ok((result?, seen_ops.clone(), is_try))
}
Filter::Values => {
let result = match ipld {
Ipld::List(xs) => Ok(Ipld::List(xs)),
Ipld::Map(xs) => Ok(Ipld::List(xs.values().cloned().collect())),
_ => Err(SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::NotACollection,
_ => Err((
is_try,
SelectorError::from_refs(
&seen_ops,
SelectorErrorReason::NotACollection,
),
)),
};

Ok((result?, seen_ops))
Ok((result?, seen_ops.clone(), is_try))
}
}
})
.and_then(|(ipld, ref path)| {
T::try_select(ipld).map_err(|e| SelectorError::from_refs(path, e))
})
},
);

let (ipld, path) = match got {
Ok((ipld, seen_ops, _)) => Ok((ipld, seen_ops)),
Err((is_try, ref e @ SelectorError { ref selector, .. })) => {
if is_try {
Ok((Ipld::Null, selector.0.iter().map(|x| x).collect::<Vec<_>>()))
} else {
Err(e.clone())
}
}
}?;

T::try_select(ipld).map_err(|e| SelectorError::from_refs(&path, e))
}
}

Expand Down Expand Up @@ -166,3 +191,70 @@ impl<T: 'static> Arbitrary for Select<T> {
.boxed()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::ipld;
use pretty_assertions as pretty;
use proptest::prelude::*;
use testresult::TestResult;

mod get {
use super::*;

fn nested_data() -> Ipld {
Ipld::Map(
vec![
("name".to_string(), Ipld::String("Alice".to_string())),
("age".to_string(), Ipld::Integer(42)),
(
"friends".to_string(),
Ipld::List(vec![
Ipld::String("Bob".to_string()),
Ipld::String("Charlie".to_string()),
]),
),
]
.into_iter()
.collect(),
)
}

proptest! {
#[test_log::test]
fn test_identity(data: ipld::Newtype) {
let selector = Select::<ipld::Newtype>::from_str(".")?;
prop_assert_eq!(selector.get(&data.0)?, data);
}

#[test_log::test]
fn test_try_missing_is_null(data: ipld::Newtype) {
let selector = Select::<Ipld>::from_str(".foo?")?;
let cleaned_data = match data.0.clone() {
Ipld::Map(mut m) => {
m.remove("foo").map_or(Ipld::Null, |v| v)
}
ipld => ipld
};
prop_assert_eq!(selector.get(&cleaned_data)?, Ipld::Null);
}

#[test_log::test]
fn test_try_missing_plus_trailing_is_null(data: ipld::Newtype, more: Vec<Filter>) {
let mut filters = vec![Filter::Try(Box::new(Filter::Field("foo".into())))];
filters.append(&mut more.clone());

let selector: Select<Ipld> = Select::new(filters);

let cleaned_data = match data.0.clone() {
Ipld::Map(mut m) => {
m.remove("foo").map_or(Ipld::Null, |v| v)
}
ipld => ipld
};
prop_assert_eq!(selector.get(&cleaned_data)?, Ipld::Null);
}
}
}
}
Loading

0 comments on commit 13510c5

Please sign in to comment.