use DIR_TAG;
use client::{Client, MDataInfo};
use crypto::shared_secretbox;
use errors::CoreError;
use futures::Future;
use futures::future::{self, Loop};
use nfs::{File, Mode, NfsError, NfsFuture, create_dir, file_helper};
use nfs::reader::Reader;
use nfs::writer::Writer;
use rand::{self, Rng};
use rust_sodium::crypto::secretbox;
use std;
use utils::FutureExt;
use utils::test_utils::random_client;
const APPEND_SIZE: usize = 10;
const ORIG_SIZE: usize = 5555;
const NEW_SIZE: usize = 50;
fn create_test_file(client: &Client<()>) -> Box<NfsFuture<(MDataInfo, File)>> {
let c2 = client.clone();
let c3 = client.clone();
let root = unwrap!(MDataInfo::random_private(DIR_TAG));
let root2 = root.clone();
create_dir(client, &root, btree_map![], btree_map![])
.then(move |res| {
assert!(res.is_ok());
file_helper::write(
c2.clone(),
File::new(Vec::new()),
Mode::Overwrite,
root.enc_key().cloned(),
)
})
.then(move |res| {
let writer = unwrap!(res);
writer.write(&[0u8; ORIG_SIZE]).and_then(
move |_| writer.close(),
)
})
.then(move |res| {
let file = unwrap!(res);
file_helper::insert(c3, root2.clone(), "hello.txt", &file).map(move |_| (root2, file))
})
.into_box()
}
#[test]
fn file_fetch_public_md() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let c5 = client.clone();
let c6 = client.clone();
let c7 = client.clone();
let mut root = unwrap!(MDataInfo::random_public(DIR_TAG));
root.enc_info = Some((shared_secretbox::gen_key(), secretbox::gen_nonce()));
root.new_enc_info = Some((shared_secretbox::gen_key(), secretbox::gen_nonce()));
let root2 = root.clone();
create_dir(client, &root, btree_map![], btree_map![])
.then(move |res| {
assert!(res.is_ok());
file_helper::write(
c2.clone(),
File::new(Vec::new()),
Mode::Overwrite,
root.enc_key().cloned(),
)
})
.then(move |res| {
let writer = unwrap!(res);
writer.write(&[0u8; ORIG_SIZE]).and_then(
move |_| writer.close(),
)
})
.then(move |res| {
let file = unwrap!(res);
file_helper::insert(c3, root2.clone(), "", &file).map(move |_| root2)
})
.then(move |res| {
let dir = unwrap!(res);
file_helper::fetch(c4, dir.clone(), "").map(move |(_version, file)| (dir, file))
})
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::read(c5, &file, dir.enc_key().cloned()).map(
move |reader| (reader, dir),
)
})
.then(move |res| {
let (reader, dir) = unwrap!(res);
let size = reader.size();
println!("reading {} bytes", size);
reader.read(0, size).map(move |data| {
assert_eq!(data, vec![0u8; ORIG_SIZE]);
dir
})
})
.then(move |res| {
let dir = unwrap!(res);
std::thread::sleep(std::time::Duration::new(3, 0));
file_helper::fetch(c6, dir.clone(), "").map(move |(_version, file)| (dir, file))
})
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::read(c7, &file, dir.enc_key().cloned()).map(
move |reader| (reader, dir),
)
})
.then(move |res| {
let (reader, _dir) = unwrap!(res);
let size = reader.size();
println!("reading {} bytes", size);
reader.read(0, size).map(move |data| {
assert_eq!(data, vec![0u8; ORIG_SIZE]);
})
})
});
}
#[test]
fn file_read() {
random_client(|client| {
let c2 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, file) = unwrap!(res);
let creation_time = *file.created_time();
file_helper::read(c2, &file, dir.enc_key().cloned()).map(
move |reader| (reader, file, creation_time),
)
})
.then(|res| {
let (reader, file, creation_time) = unwrap!(res);
let size = reader.size();
println!("reading {} bytes", size);
let result = reader.read(0, size);
assert_eq!(creation_time, *file.created_time());
assert!(creation_time <= *file.modified_time());
result
})
.map(move |data| {
assert_eq!(data, vec![0u8; ORIG_SIZE]);
})
});
}
#[test]
fn file_read_chunks() {
const CHUNK_SIZE: u64 = 1000;
random_client(|client| {
let c2 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::read(c2, &file, dir.enc_key().cloned())
})
.then(|res| {
let reader = unwrap!(res);
let size = reader.size();
assert_eq!(size, ORIG_SIZE as u64);
let size_read = 0;
let result = Vec::new();
future::loop_fn((reader, size_read, result), move |(reader,
mut size_read,
mut result)| {
let to_read = if size_read + CHUNK_SIZE >= size {
size - size_read
} else {
CHUNK_SIZE
};
println!("reading {} bytes", to_read);
reader.read(size_read, to_read).then(move |res| {
let mut data = unwrap!(res);
size_read += data.len() as u64;
result.append(&mut data);
if size_read < size {
Ok(Loop::Continue((reader, size_read, result)))
} else {
Ok(Loop::Break((reader, size_read, result)))
}
})
}).then(move |res: Result<(Reader<()>, u64, Vec<u8>), NfsError>| {
let (reader, size_read, result) = unwrap!(res);
assert_eq!(size, size_read);
assert_eq!(result, vec![0u8; ORIG_SIZE]);
println!("reading 0 bytes");
reader.read(size, 0).map(move |data| (reader, size, data))
})
.then(|res| {
let (reader, size, data) = unwrap!(res);
assert_eq!(data, Vec::<u8>::new());
reader.read(size, 1)
})
.then(|res| -> Result<_, CoreError> {
match res {
Ok(_) => {
panic!("Read past end of file successfully")
}
Err(_) => Ok(()),
}
})
})
});
}
#[test]
fn file_write_chunks() {
const CHUNK_SIZE: usize = 1000;
const GOAL_SIZE: usize = 5555;
let content = [0u8; GOAL_SIZE];
random_client(move |client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::write(c2, file, Mode::Overwrite, dir.enc_key().cloned())
.map(move |writer| (writer, dir))
})
.then(move |res| {
let (writer, dir) = unwrap!(res);
let size_written = 0;
future::loop_fn((writer, size_written), move |(writer, mut size_written)| {
let to_write = if size_written + CHUNK_SIZE >= GOAL_SIZE {
GOAL_SIZE - size_written
} else {
CHUNK_SIZE
};
println!("writing {} bytes", to_write);
writer
.write(&content[size_written..size_written + to_write])
.then(move |res| {
unwrap!(res);
size_written += to_write;
if size_written < GOAL_SIZE {
Ok(Loop::Continue((writer, size_written)))
} else {
Ok(Loop::Break(writer))
}
})
}).map(move |writer| (writer, dir))
})
.then(move |res: Result<(Writer<()>, MDataInfo), NfsError>| {
let (writer, dir) = unwrap!(res);
writer.write(&content[GOAL_SIZE..GOAL_SIZE]).map(move |_| {
(writer, dir)
})
})
.then(move |res| {
let (writer, dir) = unwrap!(res);
writer.close().map(move |file| (file, dir))
})
.then(move |res| {
let (file, dir) = unwrap!(res);
file_helper::write(c3, file, Mode::Append, dir.enc_key().cloned())
.map(move |writer| (writer, dir))
})
.then(move |res| {
let (writer, dir) = unwrap!(res);
let size_written = 0;
future::loop_fn((writer, size_written), move |(writer, mut size_written)| {
let to_write = if size_written + CHUNK_SIZE >= GOAL_SIZE {
GOAL_SIZE - size_written
} else {
CHUNK_SIZE
};
println!("writing {} bytes", to_write);
writer
.write(&content[size_written..size_written + to_write])
.then(move |res| {
unwrap!(res);
size_written += to_write;
if size_written < GOAL_SIZE {
Ok(Loop::Continue((writer, size_written)))
} else {
Ok(Loop::Break(writer))
}
})
}).map(move |writer| (writer, dir))
})
.then(move |res: Result<(Writer<()>, MDataInfo), NfsError>| {
let (writer, dir) = unwrap!(res);
writer.write(&content[GOAL_SIZE..GOAL_SIZE]).map(move |_| {
(writer, dir)
})
})
.then(move |res| {
let (writer, dir) = unwrap!(res);
writer.close().map(move |file| (file, dir))
})
.then(move |res| {
let (file, dir) = unwrap!(res);
file_helper::read(c4, &file, dir.enc_key().cloned())
})
.then(move |res| {
let reader = unwrap!(res);
let size = reader.size();
assert_eq!(size, 2 * GOAL_SIZE as u64);
reader.read(0, size)
})
.map(move |data| {
assert_eq!(data, vec![0u8; 2 * GOAL_SIZE]);
})
})
}
#[test]
fn file_update_overwrite() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let c5 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, file) = unwrap!(res);
let creation_time = *file.created_time();
file_helper::write(c2, file, Mode::Overwrite, dir.enc_key().cloned())
.map(move |writer| (writer, dir, creation_time))
})
.then(move |res| {
let (writer, dir, creation_time) = unwrap!(res);
writer
.write(&[1u8; NEW_SIZE])
.and_then(move |_| writer.close())
.map(move |file| (file, dir, creation_time))
})
.then(move |res| {
let (file, dir, creation_time) = unwrap!(res);
file_helper::update(c3, dir.clone(), "hello.txt", &file, 1)
.map(move |_| (dir, creation_time))
})
.then(move |res| {
let (dir, creation_time) = unwrap!(res);
file_helper::fetch(c4, dir.clone(), "hello.txt").map(
move |(_version, file)| (dir, file, creation_time),
)
})
.then(move |res| {
let (dir, file, creation_time) = unwrap!(res);
assert_eq!(creation_time, *file.created_time());
assert!(creation_time <= *file.modified_time());
file_helper::read(c5, &file, dir.enc_key().cloned())
})
.then(move |res| {
let reader = unwrap!(res);
let size = reader.size();
println!("reading {} bytes", size);
reader.read(0, size)
})
.map(move |data| {
assert_eq!(data, vec![1u8; NEW_SIZE]);
})
});
}
#[test]
fn file_update_append() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::write(c2, file, Mode::Append, dir.enc_key().cloned())
.map(move |writer| (dir, writer))
})
.then(move |res| {
let (dir, writer) = unwrap!(res);
writer
.write(&[2u8; APPEND_SIZE])
.and_then(move |_| writer.close())
.map(move |file| (dir, file))
})
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::read(c3, &file, dir.enc_key().cloned())
})
.then(move |res| {
let reader = unwrap!(res);
let size = reader.size();
println!("reading {} bytes", size);
reader.read(0, size)
})
.map(move |data| {
assert_eq!(data.len(), ORIG_SIZE + APPEND_SIZE);
assert_eq!(data[0..ORIG_SIZE].to_owned(), vec![0u8; ORIG_SIZE]);
assert_eq!(&data[ORIG_SIZE..], [2u8; APPEND_SIZE]);
})
});
}
#[test]
fn file_update_metadata() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, mut file) = unwrap!(res);
file.set_user_metadata(vec![12u8; 10]);
file_helper::update(c2, dir.clone(), "hello.txt", &file, 1)
.map(move |()| dir)
})
.then(move |res| {
let dir = unwrap!(res);
file_helper::fetch(c3.clone(), dir, "hello.txt")
})
.map(move |(_version, file)| {
assert_eq!(*file.user_metadata(), [12u8; 10][..]);
})
});
}
#[test]
fn file_delete() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, _file) = unwrap!(res);
file_helper::delete(&c2, &dir, "hello.txt", 1)
.map(move |()| dir)
})
.then(move |res| {
let dir = unwrap!(res);
file_helper::fetch(c3.clone(), dir, "hello.txt")
})
.then(move |res| -> Result<_, CoreError> {
match res {
Ok(_) => {
panic!("Fetched non-existing file succesfully")
}
Err(_) => Ok(()),
}
})
});
}
#[test]
fn file_delete_then_add() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let c5 = client.clone();
let c6 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::delete(&c2, &dir, "hello.txt", 1).map(move |_| (dir, file))
})
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::write(c3, file, Mode::Overwrite, dir.enc_key().cloned())
.map(move |writer| (writer, dir))
})
.then(move |res| {
let (writer, dir) = unwrap!(res);
writer
.write(&[1u8; NEW_SIZE])
.and_then(move |_| writer.close())
.map(move |file| (file, dir))
})
.then(move |res| {
let (file, dir) = unwrap!(res);
file_helper::update(c4, dir.clone(), "hello.txt", &file, 2).map(move |_| dir)
})
.then(move |res| {
let dir = unwrap!(res);
file_helper::fetch(c5, dir.clone(), "hello.txt").map(
move |(version, file)| (version, file, dir),
)
})
.then(move |res| {
let (version, file, dir) = unwrap!(res);
assert_eq!(version, 2);
file_helper::read(c6, &file, dir.enc_key().cloned())
})
.then(move |res| {
let reader = unwrap!(res);
let size = reader.size();
println!("reading {} bytes", size);
reader.read(0, size)
})
.map(move |data| {
assert_eq!(data, vec![1u8; NEW_SIZE]);
})
});
}
#[test]
fn file_open_close() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let c5 = client.clone();
create_test_file(client)
.then(move |res| {
let (dir, file) = unwrap!(res);
file_helper::read(c2, &file, dir.enc_key().cloned()).map(
move |reader| (reader, file, dir),
)
})
.then(move |res| {
let (_reader, file, dir) = unwrap!(res);
file_helper::write(c3, file.clone(), Mode::Overwrite, dir.enc_key().cloned())
.map(move |writer| (writer, file, dir))
})
.then(move |res| {
let (writer, file, dir) = unwrap!(res);
let _ = writer.close();
file_helper::write(c4, file.clone(), Mode::Append, dir.enc_key().cloned())
.map(move |writer| (writer, file, dir))
})
.then(move |res| {
let (writer, file, dir) = unwrap!(res);
let _ = writer.close();
file_helper::read(c5, &file, dir.enc_key().cloned())
})
.then(move |res| {
let reader = unwrap!(res);
let size = reader.size();
reader.read(0, size)
})
.map(move |data| {
assert_eq!(data, vec![0u8; ORIG_SIZE]);
})
});
}
#[test]
fn encryption() {
random_client(|client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let mut rng = rand::thread_rng();
let content: Vec<u8> = rng.gen_iter().take(ORIG_SIZE).collect();
let content2 = content.clone();
let key = shared_secretbox::gen_key();
let wrong_key = shared_secretbox::gen_key();
file_helper::write(
client.clone(),
File::new(Vec::new()),
Mode::Overwrite,
Some(key.clone()),
).then(move |res| {
let writer = unwrap!(res);
writer.write(&content).and_then(move |_| writer.close())
})
.then(move |res| {
let file = unwrap!(res);
file_helper::read(c2, &file, None)
.and_then(|_| Err(NfsError::from("Unexpected success")))
.or_else(move |_error| -> Result<_, NfsError> {
Ok(file)
})
})
.then(move |res| {
let file = unwrap!(res);
file_helper::read(c3, &file, Some(wrong_key))
.and_then(|_| Err(NfsError::from("Unexpected success")))
.or_else(move |error| match error {
NfsError::CoreError(CoreError::SymmetricDecipherFailure) => Ok(file),
error => Err(error),
})
})
.then(move |res| {
let file = unwrap!(res);
file_helper::read(c4, &file, Some(key))
})
.then(move |res| {
let reader = unwrap!(res);
let size = reader.size();
reader.read(0, size)
})
.then(move |res| -> Result<_, NfsError> {
let retrieved_content = unwrap!(res);
assert_eq!(retrieved_content, content2);
Ok(())
})
})
}