[wip] unit testing
- mock impl for `sharry::Client` and associated IDs
This commit is contained in:
parent
908e0031e2
commit
3257a97351
5 changed files with 123 additions and 65 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
|
@ -79,6 +79,15 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
|
|
@ -108,6 +117,12 @@ version = "3.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.23.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
|
|
@ -874,6 +889,12 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
|
|
@ -944,6 +965,7 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"ureq",
|
"ureq",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1166,6 +1188,18 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||||
|
dependencies = [
|
||||||
|
"atomic",
|
||||||
|
"getrandom 0.3.3",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
|
|
@ -1195,6 +1229,7 @@ checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ ureq = { version = "3.0.11", features = ["json"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.20.0"
|
||||||
|
uuid = { version = "1.17.0", features = ["rng", "std", "v1"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
# Optimize for speed even more aggressively
|
# Optimize for speed even more aggressively
|
||||||
|
|
|
||||||
10
src/error.rs
10
src/error.rs
|
|
@ -154,3 +154,13 @@ impl Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! error_response {
|
||||||
|
// Match a format string plus optional arguments
|
||||||
|
($fmt:expr $(, $arg:expr )* $(,)?) => {
|
||||||
|
// Expand to constructing the Error::Response variant,
|
||||||
|
// wrapping a `format!(...)` call
|
||||||
|
Error::Response(format!($fmt $(, $arg )*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,19 @@ use std::{
|
||||||
collections::{HashMap, hash_map::Entry},
|
collections::{HashMap, hash_map::Entry},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Error, Result,
|
Error, Result, error_response,
|
||||||
file::{self, FileTrait},
|
file::{self, FileTrait},
|
||||||
sharry::{AliasID, Client, FileID, ShareID, Uri, json},
|
sharry::{AliasID, Client, FileID, ShareID, Uri, json},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::mock_ids::CheckID;
|
use super::mock_ids::CheckID;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MockClient {
|
pub struct MockClient {
|
||||||
|
use_uuid: bool,
|
||||||
shares: RefCell<HashMap<String, MockShare>>,
|
shares: RefCell<HashMap<String, MockShare>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,19 +26,25 @@ struct MockShare {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MockFile {
|
struct MockFile {
|
||||||
name: String,
|
|
||||||
size: u64,
|
size: u64,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<bool> for MockClient {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
use_uuid: value,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MockClient {
|
impl MockClient {
|
||||||
fn insert_share(&self, share_id: &ShareID) -> Result<()> {
|
fn insert_share(&self, share_id: &ShareID) -> Result<()> {
|
||||||
let mut shares = self.shares.borrow_mut();
|
let mut shares = self.shares.borrow_mut();
|
||||||
|
|
||||||
let Entry::Vacant(entry) = shares.entry(share_id.to_string()) else {
|
let Entry::Vacant(entry) = shares.entry(share_id.to_string()) else {
|
||||||
return Err(Error::response(format_args!(
|
return Err(error_response!("can't insert share {share_id:?}!"));
|
||||||
"Can't create share {share_id:?}!"
|
|
||||||
)));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
entry.insert(MockShare::default());
|
entry.insert(MockShare::default());
|
||||||
|
|
@ -43,58 +52,46 @@ impl MockClient {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_file(
|
fn insert_file(&self, share_id: &ShareID, file_id: &FileID, size: u64) -> Result<()> {
|
||||||
&self,
|
let mut share = self.get_share(share_id)?;
|
||||||
share_id: &ShareID,
|
|
||||||
file_id: &FileID,
|
|
||||||
name: String,
|
|
||||||
size: u64,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut share = self.get_share_mut(share_id)?;
|
|
||||||
|
|
||||||
let Entry::Vacant(entry) = share.files.entry(file_id.to_string()) else {
|
let Entry::Vacant(entry) = share.files.entry(file_id.to_string()) else {
|
||||||
return Err(Error::response(format_args!(
|
return Err(error_response!("can't insert file {file_id:?}!"));
|
||||||
"Can't create file {file_id:?}!"
|
|
||||||
)));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
entry.insert(MockFile {
|
entry.insert(MockFile { size, offset: 0 });
|
||||||
name,
|
|
||||||
size,
|
|
||||||
offset: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_share_mut<'t>(&'t self, share_id: &ShareID) -> Result<RefMut<'t, MockShare>> {
|
fn get_share<'t>(&'t self, share_id: &ShareID) -> Result<RefMut<'t, MockShare>> {
|
||||||
let share_id = &share_id.to_string();
|
let share_id = &share_id.to_string();
|
||||||
let shares = self.shares.borrow_mut();
|
let shares = self.shares.borrow_mut();
|
||||||
|
|
||||||
|
// check share exists
|
||||||
shares
|
shares
|
||||||
.get(share_id)
|
.get(share_id)
|
||||||
.ok_or_else(|| Error::response(format_args!("Can't find share {share_id:?}!")))?;
|
.ok_or_else(|| error_response!("can't find share {share_id:?}!"))?;
|
||||||
|
|
||||||
// share exists
|
|
||||||
Ok(RefMut::map(shares, |shares| {
|
Ok(RefMut::map(shares, |shares| {
|
||||||
shares.get_mut(share_id).expect("checked but None!")
|
shares.get_mut(share_id).expect("checked but None!")
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_file_mut<'t>(
|
fn get_file<'t>(
|
||||||
&'t self,
|
&'t self,
|
||||||
share_id: &ShareID,
|
share_id: &ShareID,
|
||||||
file_id: &FileID,
|
file_id: &FileID,
|
||||||
) -> Result<RefMut<'t, MockFile>> {
|
) -> Result<RefMut<'t, MockFile>> {
|
||||||
let file_id = &file_id.to_string();
|
let file_id = &file_id.to_string();
|
||||||
let share = self.get_share_mut(share_id)?;
|
let share = self.get_share(share_id)?;
|
||||||
|
|
||||||
|
// check file exists
|
||||||
share
|
share
|
||||||
.files
|
.files
|
||||||
.get(file_id)
|
.get(file_id)
|
||||||
.ok_or_else(|| Error::response(format_args!("Can't find file {file_id:?}!")))?;
|
.ok_or_else(|| error_response!("can't find file {file_id:?}!"))?;
|
||||||
|
|
||||||
// file exists
|
|
||||||
Ok(RefMut::map(share, move |share| {
|
Ok(RefMut::map(share, move |share| {
|
||||||
share.files.get_mut(file_id).expect("checked but None!")
|
share.files.get_mut(file_id).expect("checked but None!")
|
||||||
}))
|
}))
|
||||||
|
|
@ -110,7 +107,11 @@ impl Client for MockClient {
|
||||||
) -> Result<ShareID> {
|
) -> Result<ShareID> {
|
||||||
(uri, alias_id).check()?;
|
(uri, alias_id).check()?;
|
||||||
|
|
||||||
let share_id = true.into();
|
let share_id = if self.use_uuid {
|
||||||
|
Uuid::now_v1(&[4, 8, 15, 16, 23, 42]).to_string().into()
|
||||||
|
} else {
|
||||||
|
true.into()
|
||||||
|
};
|
||||||
self.insert_share(&share_id)?;
|
self.insert_share(&share_id)?;
|
||||||
|
|
||||||
Ok(share_id)
|
Ok(share_id)
|
||||||
|
|
@ -120,6 +121,8 @@ impl Client for MockClient {
|
||||||
(uri, alias_id).check()?;
|
(uri, alias_id).check()?;
|
||||||
share_id.check()?;
|
share_id.check()?;
|
||||||
|
|
||||||
|
let _share = self.get_share(share_id)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,13 +136,13 @@ impl Client for MockClient {
|
||||||
(uri, alias_id).check()?;
|
(uri, alias_id).check()?;
|
||||||
share_id.check()?;
|
share_id.check()?;
|
||||||
|
|
||||||
let file_id = true.into();
|
let file_id = if self.use_uuid {
|
||||||
self.insert_file(
|
let id = Uuid::now_v1(&[4, 8, 15, 16, 23, 42]);
|
||||||
share_id,
|
FileID::new_test(id)
|
||||||
&file_id,
|
} else {
|
||||||
file.get_name().to_string(),
|
true.into()
|
||||||
file.get_size(),
|
};
|
||||||
)?;
|
self.insert_file(share_id, &file_id, file.get_size())?;
|
||||||
|
|
||||||
Ok(file_id)
|
Ok(file_id)
|
||||||
}
|
}
|
||||||
|
|
@ -155,10 +158,21 @@ impl Client for MockClient {
|
||||||
(share_id, chunk.get_file_id()).check()?;
|
(share_id, chunk.get_file_id()).check()?;
|
||||||
|
|
||||||
// TODO: `chunk` must align to a full MiB
|
// TODO: `chunk` must align to a full MiB
|
||||||
let file = self.get_file_mut(share_id, chunk.get_file_id())?;
|
let file = self.get_file(share_id, chunk.get_file_id())?;
|
||||||
|
|
||||||
todo!()
|
if chunk.get_length() == 0 {
|
||||||
|
return Err(error_response!("chunk {chunk:?} empty!"));
|
||||||
|
} else if chunk.get_offset() % (1024 * 1024) != 0 {
|
||||||
|
return Err(error_response!("chunk {chunk:?} not aligned to a MiB!"));
|
||||||
|
} else if chunk.get_offset() != file.offset {
|
||||||
|
return Error::mismatch(file.offset, chunk.get_offset());
|
||||||
|
} else if file.offset + chunk.get_length() > file.size {
|
||||||
|
return Err(error_response!("chunk {chunk:?} too long!"));
|
||||||
|
}
|
||||||
|
|
||||||
// Ok(())
|
let mut file = file;
|
||||||
|
file.offset += chunk.get_length();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,43 +124,41 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn false_makes_invalids() {
|
fn false_makes_invalids() {
|
||||||
|
fn test_check(value: impl CheckID, callback: impl FnOnce(&Parameter) -> bool) {
|
||||||
|
let check = value.check().expect_err("should be invalid");
|
||||||
|
let p = check.get_invalid_param().expect("should be InvalidParam");
|
||||||
|
assert!(callback(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid ids
|
||||||
let uri = Uri::from(true);
|
let uri = Uri::from(true);
|
||||||
let alias_id = AliasID::from(true);
|
let alias_id = AliasID::from(true);
|
||||||
let share_id = ShareID::from(true);
|
let share_id = ShareID::from(true);
|
||||||
let file_id = FileID::from(true);
|
let file_id = FileID::from(true);
|
||||||
|
|
||||||
|
// invalid ids
|
||||||
let uri_i = Uri::from(false);
|
let uri_i = Uri::from(false);
|
||||||
let alias_id_i = AliasID::from(false);
|
let alias_id_i = AliasID::from(false);
|
||||||
let share_id_i = ShareID::from(false);
|
let share_id_i = ShareID::from(false);
|
||||||
let file_id_i = FileID::from(false);
|
let file_id_i = FileID::from(false);
|
||||||
|
|
||||||
// invalid Uri, valid alias
|
// param checks
|
||||||
let check = (&uri_i, &alias_id).check().expect_err("should be invalid");
|
let is_uri_i = |p: &Parameter| matches!(p, Parameter::Uri(_));
|
||||||
let p = check.get_invalid_param().expect("should be InvalidParam");
|
let is_alias_id_i = |p: &Parameter| matches!(p, Parameter::AliasID(_));
|
||||||
assert!(matches!(p, Parameter::Uri(_)));
|
let is_share_id_i = |p: &Parameter| matches!(p, Parameter::ShareID(_));
|
||||||
|
let is_file_id_i = |p: &Parameter| matches!(p, Parameter::FileID(_));
|
||||||
|
|
||||||
// valid Uri, invalid alias
|
// uri + alias
|
||||||
let check = (&uri, &alias_id_i).check().expect_err("should be invalid");
|
test_check((&uri_i, &alias_id_i), is_uri_i);
|
||||||
let p = check.get_invalid_param().expect("should be InvalidParam");
|
test_check((&uri_i, &alias_id), is_uri_i);
|
||||||
assert!(matches!(p, Parameter::AliasID(_)));
|
test_check((&uri, &alias_id_i), is_alias_id_i);
|
||||||
|
|
||||||
// invalid share
|
// share
|
||||||
let check = share_id_i.check().expect_err("should be invalid");
|
test_check(&share_id_i, is_share_id_i);
|
||||||
let p = check.get_invalid_param().expect("should be InvalidParam");
|
|
||||||
assert!(matches!(p, Parameter::ShareID(_)));
|
|
||||||
|
|
||||||
// invalid share, valid file
|
// share + file
|
||||||
let check = (&share_id_i, &file_id)
|
test_check((&share_id_i, &file_id_i), is_share_id_i);
|
||||||
.check()
|
test_check((&share_id_i, &file_id), is_share_id_i);
|
||||||
.expect_err("should be invalid");
|
test_check((&share_id, &file_id_i), is_file_id_i);
|
||||||
let p = check.get_invalid_param().expect("should be InvalidParam");
|
|
||||||
assert!(matches!(p, Parameter::ShareID(_)));
|
|
||||||
|
|
||||||
// valid share, invalid file
|
|
||||||
let check = (&share_id, &file_id_i)
|
|
||||||
.check()
|
|
||||||
.expect_err("should be invalid");
|
|
||||||
let p = check.get_invalid_param().expect("should be InvalidParam");
|
|
||||||
assert!(matches!(p, Parameter::FileID(_)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue