Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ module-items-ordered-within-groupings = [
"macros",
"modules",
"global_asm",
"UPPER_SNAKE_CASE",
"lower_snake_case",
]
17 changes: 17 additions & 0 deletions src/directory.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::*;

#[serde_as]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields, rename_all = "kebab-case", transparent)]
pub struct Directory {
#[serde_as(as = "MapPreventDuplicates<_, _>")]
pub(crate) entries: BTreeMap<Component, Entry>,
}

Expand Down Expand Up @@ -67,3 +69,18 @@ impl Directory {
Self::default()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn duplicate_entries_are_rejected() {
assert_eq!(
serde_json::from_str::<Directory>(r#"{"a":{},"a":{}}"#)
.unwrap_err()
.to_string(),
"invalid entry: found duplicate key at line 1 column 15",
);
}
}
11 changes: 7 additions & 4 deletions src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ mod tests {
#[test]
fn unknown_fields_are_rejected() {
assert!(
serde_json::from_str::<File>(r#"{"hash": "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262", "size": 0, "foo": null}"#)
.unwrap_err()
.to_string()
.starts_with("unknown field `foo`")
serde_json::from_str::<File>(&format!(
r#"{{"hash": "{}", "size": 0, "foo": null}}"#,
test::HASH,
))
.unwrap_err()
.to_string()
.starts_with("unknown field `foo`")
);
}
}
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ use {
owo_colors::Styled,
regex::Regex,
serde::{Deserialize, Deserializer, Serialize, Serializer},
serde_with::{DeserializeFromStr, SerializeDisplay},
serde_with::{DeserializeFromStr, MapPreventDuplicates, SerializeDisplay, serde_as},
snafu::{ErrorCompat, OptionExt, ResultExt, Snafu, ensure},
std::{
array::TryFromSliceError,
Expand Down Expand Up @@ -141,6 +141,9 @@ mod subcommand;
mod template;
mod utf8_path_ext;

#[cfg(test)]
mod test;

const SEPARATORS: [char; 2] = ['/', '\\'];

type Result<T = (), E = Error> = std::result::Result<T, E>;
Expand Down
8 changes: 1 addition & 7 deletions src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,7 @@ mod tests {
let re = Regex::new(r"(?s)```json(.*?)```").unwrap();

for capture in re.captures_iter(&readme) {
let manifest = capture[1].replace(
"…",
concat!(
"3f814a19e6db6431959f0393d362920846224af1d44ceee851e0caded9412d93",
"9a221a15f6ba9a5d118a570a6b1cc48c95c7fb73581eeec1e33afdb4d0163907"
),
);
let manifest = capture[1].replace("…", test::SIGNATURE);
serde_json::from_str::<Manifest>(&manifest).unwrap();
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/note.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::*;

#[serde_as]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct Note {
#[serde_as(as = "MapPreventDuplicates<_, _>")]
pub signatures: BTreeMap<PublicKey, Signature>,
}

Expand Down Expand Up @@ -34,3 +36,23 @@ impl Note {
Ok(self.signatures.len().into_u64())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn duplicate_signatures_are_rejected() {
let json = format!(
r#"{{"signatures":{{"{}":"{}","{}":"{}"}}}}"#,
test::PUBLIC_KEY,
test::SIGNATURE,
test::PUBLIC_KEY,
test::SIGNATURE,
);
assert_eq!(
serde_json::from_str::<Note>(&json).unwrap_err().to_string(),
"invalid entry: found duplicate key at line 1 column 411",
);
}
}
40 changes: 16 additions & 24 deletions src/private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ mod tests {

#[test]
fn must_have_leading_zeros() {
"0e56ae8b43aa93fd4c179ceaff96f729522622d26b4b5357bc959e476e59e107"
.parse::<PrivateKey>()
.unwrap();

"e56ae8b43aa93fd4c179ceaff96f729522622d26b4b5357bc959e476e59e107"
.parse::<PrivateKey>()
.unwrap_err();
assert_eq!(
"0"
.repeat(63)
.parse::<PrivateKey>()
.unwrap_err()
.to_string(),
"invalid private key hex",
);
}

#[test]
Expand Down Expand Up @@ -132,17 +133,13 @@ mod tests {

#[test]
fn serialized_private_key_is_not_valid_public_key() {
let key = "0e56ae8b43aa93fd4c179ceaff96f729522622d26b4b5357bc959e476e59e107";
key.parse::<PrivateKey>().unwrap();
key.parse::<PublicKey>().unwrap_err();
test::PRIVATE_KEY.parse::<PublicKey>().unwrap_err();
}

#[test]
fn uppercase_is_forbidden() {
let key = "0e56ae8b43aa93fd4c179ceaff96f729522622d26b4b5357bc959e476e59e107";
key.parse::<PrivateKey>().unwrap();
assert_eq!(
key
test::PRIVATE_KEY
.to_uppercase()
.parse::<PrivateKey>()
.unwrap_err()
Expand All @@ -153,11 +150,7 @@ mod tests {

#[test]
fn whitespace_is_not_trimmed_when_parsing_from_string() {
"0e56ae8b43aa93fd4c179ceaff96f729522622d26b4b5357bc959e476e59e107"
.parse::<PrivateKey>()
.unwrap();

" 0e56ae8b43aa93fd4c179ceaff96f729522622d26b4b5357bc959e476e59e107"
format!(" {}", test::PRIVATE_KEY)
.parse::<PrivateKey>()
.unwrap_err();
}
Expand All @@ -170,14 +163,13 @@ mod tests {

let path = Utf8PathBuf::from_path_buf(dir.path().join("key")).unwrap();

let key = "0e56ae8b43aa93fd4c179ceaff96f729522622d26b4b5357bc959e476e59e107"
.parse::<PrivateKey>()
.unwrap();

filesystem::write(&path, format!(" \t{}\n", key.display_secret())).unwrap();
filesystem::write(&path, format!(" \t{}\n", test::PRIVATE_KEY)).unwrap();

filesystem::chmod(&path, 0o600).unwrap();

assert_eq!(PrivateKey::load(&path).unwrap(), key);
assert_eq!(
PrivateKey::load(&path).unwrap(),
test::PRIVATE_KEY.parse().unwrap(),
);
}
}
49 changes: 17 additions & 32 deletions src/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,11 @@ mod tests {

#[test]
fn must_have_leading_zeros() {
"0f6d444f09eb336d3cc94d66cc541fea0b70b36be291eb3ecf5b49113f34c8d3"
.parse::<PublicKey>()
.unwrap();

"f6d444f09eb336d3cc94d66cc541fea0b70b36be291eb3ecf5b49113f34c8d3"
.parse::<PublicKey>()
.unwrap_err();
let s = "0".repeat(63);
assert_eq!(
s.parse::<PublicKey>().unwrap_err().to_string(),
format!("invalid public key hex: `{s}`"),
);
}

#[test]
Expand All @@ -126,37 +124,25 @@ mod tests {

#[test]
fn uppercase_is_forbidden() {
let key = "0f6d444f09eb336d3cc94d66cc541fea0b70b36be291eb3ecf5b49113f34c8d3";
key.parse::<PublicKey>().unwrap();
let uppercase = test::PUBLIC_KEY.to_uppercase();
assert_eq!(
key
.to_uppercase()
.parse::<PublicKey>()
.unwrap_err()
.to_string(),
"public keys must be lowercase hex: \
`0F6D444F09EB336D3CC94D66CC541FEA0B70B36BE291EB3ECF5B49113F34C8D3`"
uppercase.parse::<PublicKey>().unwrap_err().to_string(),
format!("public keys must be lowercase hex: `{uppercase}`"),
);
}

#[test]
fn weak_public_keys_are_forbidden() {
let key = "0".repeat(64);
assert!(matches!(
"0000000000000000000000000000000000000000000000000000000000000000"
.parse::<PublicKey>()
.unwrap_err(),
PublicKeyError::Weak { key }
if key == "0000000000000000000000000000000000000000000000000000000000000000",
key.parse::<PublicKey>().unwrap_err(),
PublicKeyError::Weak { key } if key == key,
));
}

#[test]
fn whitespace_is_not_trimmed_when_parsing_from_string() {
"0f6d444f09eb336d3cc94d66cc541fea0b70b36be291eb3ecf5b49113f34c8d3"
.parse::<PublicKey>()
.unwrap();

" 0f6d444f09eb336d3cc94d66cc541fea0b70b36be291eb3ecf5b49113f34c8d3"
format!(" {}", test::PUBLIC_KEY)
.parse::<PublicKey>()
.unwrap_err();
}
Expand All @@ -167,12 +153,11 @@ mod tests {

let path = Utf8PathBuf::from_path_buf(dir.path().join("key")).unwrap();

let key = "0f6d444f09eb336d3cc94d66cc541fea0b70b36be291eb3ecf5b49113f34c8d3"
.parse::<PublicKey>()
.unwrap();

filesystem::write(&path, format!(" \t{key}\n")).unwrap();
filesystem::write(&path, format!(" \t{}\n", test::PUBLIC_KEY)).unwrap();

assert_eq!(PublicKey::load(&path).unwrap(), key);
assert_eq!(
PublicKey::load(&path).unwrap().to_string(),
test::PUBLIC_KEY,
);
}
}
34 changes: 34 additions & 0 deletions src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use super::*;

pub(crate) const HASH: &str = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262";

pub(crate) const PRIVATE_KEY: &str =
"8eb33440cce2a651c6d8867c331392f642ebfd9b96e485cd2124643461fb41a2";

pub(crate) const PUBLIC_KEY: &str =
"26892a0ef203b97c2702052336f2b8711efaf1442430ff0d9fb4d03df794a0ac";

pub(crate) const SIGNATURE: &str = concat!(
"3f814a19e6db6431959f0393d362920846224af1d44ceee851e0caded9412d93",
"9a221a15f6ba9a5d118a570a6b1cc48c95c7fb73581eeec1e33afdb4d0163907",
);

#[test]
fn hash_is_valid() {
HASH.parse::<Hash>().unwrap();
}

#[test]
fn private_key_is_valid() {
PRIVATE_KEY.parse::<PrivateKey>().unwrap();
}

#[test]
fn public_key_is_valid() {
PUBLIC_KEY.parse::<PublicKey>().unwrap();
}

#[test]
fn signature_is_valid() {
SIGNATURE.parse::<Signature>().unwrap();
}
26 changes: 10 additions & 16 deletions tests/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,22 @@ use super::*;
#[test]
fn assert_failure() {
Test::new()
.write("foo", "foo")
.args([
"hash",
"foo",
"--assert",
"0000000000000000000000000000000000000000000000000000000000000000",
])
.stderr("error: file hash 04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9 not equal to expected 0000000000000000000000000000000000000000000000000000000000000000\n")
.touch("foo")
.args(["hash", "foo", "--assert", &"0".repeat(64)])
.stderr(&format!(
"error: file hash {} not equal to expected {}\n",
EMPTY_HASH,
"0".repeat(64)
))
.failure();
}

#[test]
fn assert_success() {
Test::new()
.write("foo", "foo")
.args([
"hash",
"foo",
"--assert",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9",
])
.stdout("04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9\n")
.touch("foo")
.args(["hash", "foo", "--assert", EMPTY_HASH])
.stdout(format!("{EMPTY_HASH}\n"))
.success();
}

Expand Down