radicle-cob: test parse_refstr
Add quickcheck tests for parse_refstr to ensure the correct behaviour. This required adding Arbitrary instances for TypeName and ObjectId, as well as adding a mechanism for generating Invalid ObjectIds. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
a985e067a5
commit
c620f873e5
|
|
@ -1596,6 +1596,7 @@ name = "radicle-cob"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519-compact",
|
||||
"fastrand",
|
||||
"git-commit",
|
||||
"git-ref-format",
|
||||
"git-trailers",
|
||||
|
|
@ -1603,6 +1604,7 @@ dependencies = [
|
|||
"log",
|
||||
"petgraph",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"radicle-crypto",
|
||||
"radicle-git-ext",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -38,8 +38,11 @@ features = ["derive"]
|
|||
|
||||
[dev-dependencies]
|
||||
ed25519-compact = { version = "1.0.12", features = ["pem"] }
|
||||
fastrand = { version = "1.8.0", default-features = false }
|
||||
git-ref-format = { version = "0.1", features = ["macro"] }
|
||||
tempfile = { version = "3" }
|
||||
quickcheck = { version = "1", default-features = false }
|
||||
quickcheck_macros = { version = "1", default-features = false }
|
||||
|
||||
[dev-dependencies.radicle-crypto]
|
||||
path = "../radicle-crypto"
|
||||
|
|
|
|||
|
|
@ -71,6 +71,12 @@
|
|||
//! [automerge]: https://automerge.org
|
||||
//! [RFC-0662]: https://github.com/radicle-dev/radicle-link/blob/master/docs/rfc/0662-collaborative-objects.adoc
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate quickcheck;
|
||||
#[cfg(test)]
|
||||
#[macro_use(quickcheck)]
|
||||
extern crate quickcheck_macros;
|
||||
|
||||
extern crate radicle_crypto as crypto;
|
||||
extern crate radicle_git_ext as git_ext;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@ pub use identity::{Name, Person, Project, RemoteProject};
|
|||
|
||||
pub mod storage;
|
||||
pub use storage::Storage;
|
||||
|
||||
pub mod arbitrary;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
use std::{iter, marker::PhantomData};
|
||||
|
||||
use quickcheck::Arbitrary;
|
||||
|
||||
use crate::{ObjectId, TypeName};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Invalid<T> {
|
||||
pub value: String,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl Arbitrary for TypeName {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let rng = fastrand::Rng::with_seed(u64::arbitrary(g));
|
||||
let mut name: Vec<String> = Vec::new();
|
||||
for _ in 0..rng.usize(1..5) {
|
||||
name.push(
|
||||
iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
name.join(".")
|
||||
.parse::<TypeName>()
|
||||
.expect("TypeName is valid")
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for ObjectId {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let rng = fastrand::Rng::with_seed(u64::arbitrary(g));
|
||||
let bytes = iter::repeat_with(|| rng.u8(..))
|
||||
.take(20)
|
||||
.collect::<Vec<_>>();
|
||||
Self::from(git_ext::Oid::try_from(bytes.as_slice()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Invalid<ObjectId> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let rng = fastrand::Rng::with_seed(u64::arbitrary(g));
|
||||
let value = iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(21..50))
|
||||
.collect();
|
||||
Invalid {
|
||||
value,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use crypto::test::signer::MockSigner;
|
||||
use git_ref_format::{refname, Component, RefString};
|
||||
use quickcheck::Arbitrary;
|
||||
use radicle_crypto::Signer;
|
||||
|
||||
use crate::{create, get, history, list, update, Create, ObjectId, TypeName, Update};
|
||||
use crate::{
|
||||
create, get, history, list, object, test::arbitrary::Invalid, update, Create, ObjectId,
|
||||
TypeName, Update,
|
||||
};
|
||||
|
||||
use super::test;
|
||||
|
||||
|
|
@ -223,6 +227,69 @@ fn traverse_cobs() {
|
|||
assert_eq!(contents, vec![b"issue 1".to_vec(), b"issue 2".to_vec()]);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn parse_refstr(oid: ObjectId, typename: TypeName) {
|
||||
let suffix = refname!("refs/cobs")
|
||||
.and(Component::from(&typename))
|
||||
.and(Component::from(&oid));
|
||||
|
||||
// refs/cobs/<typename>/<object_id> gives back the <typename> and <object_id>
|
||||
assert_eq!(object::parse_refstr(&suffix), Some((typename.clone(), oid)));
|
||||
|
||||
// strips a single namespace
|
||||
assert_eq!(
|
||||
object::parse_refstr(&refname!("refs/namespaces/a").join(&suffix)),
|
||||
Some((typename.clone(), oid))
|
||||
);
|
||||
|
||||
// strips multiple namespaces
|
||||
assert_eq!(
|
||||
object::parse_refstr(&refname!("refs/namespaces/a/refs/namespaces/b").join(&suffix)),
|
||||
Some((typename.clone(), oid))
|
||||
);
|
||||
|
||||
// ignores the extra path
|
||||
assert_eq!(
|
||||
object::parse_refstr(
|
||||
&refname!("refs/namespaces/a/refs/namespaces/b")
|
||||
.join(suffix)
|
||||
.and(refname!("more/paths"))
|
||||
),
|
||||
Some((typename, oid))
|
||||
);
|
||||
}
|
||||
|
||||
/// Note: an invalid type name is also an invalid reference string, it
|
||||
/// cannot start or end with a '.', and cannot have '..'.
|
||||
#[quickcheck]
|
||||
fn invalid_parse_refstr(oid: Invalid<ObjectId>, typename: TypeName) {
|
||||
let oid = RefString::try_from(oid.value).unwrap();
|
||||
let typename = Component::from(&typename);
|
||||
let suffix = refname!("refs/cobs").and(typename).and(oid);
|
||||
|
||||
// All parsing will fail because `oid` is not a valid ObjectId
|
||||
assert_eq!(object::parse_refstr(&suffix), None);
|
||||
|
||||
assert_eq!(
|
||||
object::parse_refstr(&refname!("refs/namespaces/a").join(&suffix)),
|
||||
None
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
object::parse_refstr(&refname!("refs/namespaces/a/refs/namespaces/b").join(&suffix)),
|
||||
None
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
object::parse_refstr(
|
||||
&refname!("refs/namespaces/a/refs/namespaces/b")
|
||||
.join(suffix)
|
||||
.and(refname!("more/paths"))
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
fn gen<T: Arbitrary>(size: usize) -> T {
|
||||
let mut gen = quickcheck::Gen::new(size);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue