use super::*;
use crate::http::{RequestBody, RequestRecord};
use indexmap::IndexMap;
use itertools::Itertools;
use rstest::{fixture, rstest};
use slumber_util::{Factory, paths::get_repo_root};
use std::collections::HashMap;
impl CollectionDatabase {
fn count_requests(&self) -> usize {
self.database
.connection()
.query_row(
"SELECT COUNT(*) FROM requests_v2
WHERE collection_id = :collection_id",
named_params! {
":collection_id": self.collection_id(),
},
|row| row.get::<_, u32>(0),
)
.unwrap() as usize
}
}
#[fixture]
fn collection_file() -> CollectionFile {
CollectionFile::new(Some(get_repo_root().join("slumber.yml"))).unwrap()
}
#[fixture]
fn other_collection_file() -> CollectionFile {
CollectionFile::new(Some(get_repo_root().join("README.md"))).unwrap()
}
#[fixture]
fn request_db(
collection_file: CollectionFile,
other_collection_file: CollectionFile,
) -> RequestDb {
let database = Database::factory(());
let collection1 =
database.clone().into_collection(&collection_file).unwrap();
let collection2 = database
.clone()
.into_collection(&other_collection_file)
.unwrap();
let collections = [collection1, collection2];
let mut request_ids: IndexMap<
(CollectionId, Option<ProfileId>, RecipeId),
RequestId,
> = Default::default();
for collection in &collections {
for profile_id in [None, Some("profile1"), Some("profile2")] {
for recipe_id in ["recipe1", "recipe2"] {
let recipe_id: RecipeId = recipe_id.into();
let profile_id = profile_id.map(ProfileId::from);
let exchange =
Exchange::factory((profile_id.clone(), recipe_id.clone()));
collection.insert_exchange(&exchange).unwrap();
request_ids.insert(
(collection.collection_id(), profile_id, recipe_id),
exchange.id,
);
}
}
}
RequestDb {
database,
collections,
request_ids,
}
}
struct RequestDb {
database: Database,
collections: [CollectionDatabase; 2],
request_ids:
IndexMap<(CollectionId, Option<ProfileId>, RecipeId), RequestId>,
}
#[rstest]
fn test_collection_delete(collection_file: CollectionFile) {
let database = Database::factory(());
let collection =
database.clone().into_collection(&collection_file).unwrap();
let exchange = Exchange::factory(RecipeId::from("recipe1"));
let key_type = "MyKey";
let ui_key = "key1";
collection.insert_exchange(&exchange).unwrap();
collection
.set_ui(&[UiSetting {
key_type,
key: ui_key.to_owned(),
value: "value1".to_owned(),
}])
.unwrap();
collection.insert_command("jq .").unwrap();
assert_eq!(collection.get_all_requests().unwrap().len(), 1);
assert_eq!(
collection.get_ui(key_type, ui_key).unwrap(),
Some("value1".into())
);
assert_eq!(collection.get_commands("jq").unwrap().len(), 1);
database
.delete_collection(collection.collection_id)
.unwrap();
assert_eq!(database.get_collections().unwrap(), []);
assert_eq!(database.get_all_requests().unwrap(), []);
assert_eq!(collection.get_ui(key_type, ui_key).unwrap(), None);
assert_eq!(collection.get_commands("jq").unwrap(), [""; 0]);
}
#[rstest]
fn test_collection_merge(
collection_file: CollectionFile,
other_collection_file: CollectionFile,
) {
let database = Database::factory(());
let target = database.clone().into_collection(&collection_file).unwrap();
let source = database
.clone()
.into_collection(&other_collection_file)
.unwrap();
let exchange1 =
Exchange::factory((Some("profile1".into()), "recipe1".into()));
let exchange2 =
Exchange::factory((Some("profile1".into()), "recipe1".into()));
let profile_id = exchange1.request.profile_id.as_ref();
let recipe_id = &exchange1.request.recipe_id;
let key_type = "MyKey";
let ui_key = "key1";
target.insert_exchange(&exchange1).unwrap();
target
.set_ui(&[UiSetting {
key_type,
key: ui_key.to_owned(),
value: "value1".to_owned(),
}])
.unwrap();
target.insert_command("jq .").unwrap();
source.insert_exchange(&exchange2).unwrap();
source
.set_ui(&[UiSetting {
key_type,
key: ui_key.to_owned(),
value: "value2".to_owned(),
}])
.unwrap();
source.insert_command("jq .").unwrap(); source.insert_command("jq .data").unwrap();
assert_eq!(
target
.get_latest_request(profile_id.into(), recipe_id)
.unwrap()
.unwrap()
.id,
exchange1.id
);
assert_eq!(
target.get_ui(key_type, ui_key).unwrap(),
Some("value1".into())
);
assert_eq!(target.get_commands("jq").unwrap(), ["jq ."]);
assert_eq!(
source
.get_latest_request(profile_id.into(), recipe_id)
.unwrap()
.unwrap()
.id,
exchange2.id
);
assert_eq!(
source.get_ui(key_type, ui_key).unwrap(),
Some("value2".into())
);
assert_eq!(source.get_commands("jq").unwrap(), ["jq .data", "jq ."]);
database
.merge_collections(source.collection_id, target.collection_id)
.unwrap();
assert_eq!(
target
.get_latest_request(profile_id.into(), recipe_id)
.unwrap()
.unwrap()
.id,
exchange2.id
);
assert_eq!(
target.get_ui(key_type, ui_key).unwrap(),
Some("value2".into())
);
assert_eq!(target.get_commands("jq").unwrap(), ["jq .data", "jq ."]);
assert_eq!(
database
.get_collections()
.unwrap()
.into_iter()
.map(|collection| collection.path)
.collect::<Vec<_>>(),
vec![collection_file.path().canonicalize().unwrap()]
);
}
#[rstest]
fn test_database_get_all_requests(request_db: RequestDb) {
assert_eq!(request_db.database.get_all_requests().unwrap().len(), 12);
}
#[rstest]
fn test_get_latest_request(request_db: RequestDb) {
for collection in &request_db.collections {
for profile_id in [None, Some("profile1"), Some("extra_profile")] {
for recipe_id in ["recipe1", "extra_recipe"] {
let collection_id = collection.collection_id();
let profile_id = profile_id.map(ProfileId::from);
let recipe_id = recipe_id.into();
let exchange_id = collection
.get_latest_request(profile_id.as_ref().into(), &recipe_id)
.unwrap()
.map(|exchange| exchange.id);
let expected_id = request_db.request_ids.get(&(
collection_id,
profile_id.clone(),
recipe_id.clone(),
));
assert_eq!(
exchange_id.as_ref(),
expected_id,
"Request mismatch for collection = {collection_id}, \
profile = {profile_id:?}, recipe = {recipe_id}"
);
}
}
}
}
#[rstest]
#[case::none(RequestBody::None)]
#[case::stream(RequestBody::Stream)]
#[case::too_large(RequestBody::TooLarge)]
#[case::empty(RequestBody::Some(b"".as_slice().into()))]
#[case::some(RequestBody::Some(b"data".as_slice().into()))]
fn test_request_body(#[case] body: RequestBody) {
let database = CollectionDatabase::factory(());
let exchange = Exchange::factory(RequestRecord {
body: body.clone(),
..RequestRecord::factory(())
});
database.insert_exchange(&exchange).unwrap();
let actual = database.get_request(exchange.id).unwrap().unwrap();
assert_eq!(actual.request.body, body);
}
#[rstest]
fn test_collection_get_all_requests(request_db: RequestDb) {
let [collection1, collection2] = request_db.collections;
assert_eq!(collection1.get_all_requests().unwrap().len(), 6);
assert_eq!(collection2.get_all_requests().unwrap().len(), 6);
}
#[test]
fn test_get_recipe_requests() {
let database = CollectionDatabase::factory(());
let mut request_ids: HashMap<
(Option<ProfileId>, RecipeId),
Vec<RequestId>,
> = Default::default();
for profile_id in [None, Some("profile1"), Some("profile2")] {
for recipe_id in ["recipe1", "recipe2"] {
let recipe_id: RecipeId = recipe_id.into();
let profile_id = profile_id.map(ProfileId::from);
let mut ids = (0..3)
.map(|_| {
let exchange = Exchange::factory((
profile_id.clone(),
recipe_id.clone(),
));
database.insert_exchange(&exchange).unwrap();
exchange.id
})
.collect_vec();
ids.reverse();
request_ids.insert((profile_id, recipe_id), ids);
}
}
for profile_id in [None, Some("profile1"), Some("extra_profile")] {
for recipe_id in ["recipe1", "extra_recipe"] {
let profile_id = profile_id.map(ProfileId::from);
let recipe_id = recipe_id.into();
let ids = database
.get_recipe_requests(profile_id.as_ref().into(), &recipe_id)
.unwrap()
.into_iter()
.map(|exchange| exchange.id)
.collect_vec();
let expected_id = request_ids
.get(&(profile_id.clone(), recipe_id.clone()))
.cloned()
.unwrap_or_default();
assert_eq!(
ids, expected_id,
"Requests mismatch for \
profile = {profile_id:?}, recipe = {recipe_id}"
);
}
}
let recipe_id = "recipe1".into();
let ids = database
.get_recipe_requests(ProfileFilter::All, &recipe_id)
.unwrap()
.into_iter()
.map(|exchange| exchange.id)
.sorted()
.collect_vec();
let expected_ids = request_ids
.iter()
.filter(|((_, r), _)| r == &recipe_id)
.flat_map(|(_, request_ids)| request_ids)
.sorted()
.copied()
.collect_vec();
assert_eq!(ids, expected_ids);
}
#[rstest]
#[case(ProfileFilter::All, "recipe1", 3)]
#[case(ProfileFilter::Some(Cow::Owned("profile1".into())), "recipe1", 1)]
#[case(ProfileFilter::None, "recipe1", 1)]
fn test_delete_recipe_requests(
request_db: RequestDb,
#[case] profile_filter: ProfileFilter<'static>,
#[case] recipe_id: RecipeId,
#[case] expected_deleted: usize,
) {
let [collection1, collection2] = request_db.collections;
assert_eq!(
collection1
.delete_recipe_requests(profile_filter, &recipe_id)
.unwrap()
.len(),
expected_deleted
);
assert_eq!(collection1.count_requests(), 6 - expected_deleted);
assert_eq!(collection2.count_requests(), 6);
}
#[rstest]
fn test_delete_request(request_db: RequestDb) {
let [collection1, collection2] = request_db.collections;
let request_id = *request_db.request_ids.first().unwrap().1;
assert_eq!(request_db.database.delete_request(request_id).unwrap(), 1);
assert_eq!(collection1.count_requests(), 5);
assert_eq!(collection2.count_requests(), 6);
}
#[rstest]
fn test_ui_state(
collection_file: CollectionFile,
other_collection_file: CollectionFile,
) {
let database = Database::factory(());
let collection1 =
database.clone().into_collection(&collection_file).unwrap();
let collection2 = database
.clone()
.into_collection(&other_collection_file)
.unwrap();
let key_type = "MyKey";
let ui_key = "key1";
collection1
.set_ui(&[UiSetting {
key_type,
key: ui_key.to_owned(),
value: "value1".to_owned(),
}])
.unwrap();
collection2
.set_ui(&[UiSetting {
key_type,
key: ui_key.to_owned(),
value: "value2".to_owned(),
}])
.unwrap();
assert_eq!(
collection1.get_ui(key_type, ui_key).unwrap(),
Some("value1".into())
);
assert_eq!(
collection2.get_ui(key_type, ui_key).unwrap(),
Some("value2".into())
);
}
#[test]
fn test_commands() {
let database = CollectionDatabase::factory(());
database.insert_command("tail").unwrap();
database.insert_command("jq .").unwrap();
database.insert_command("jq .data").unwrap();
database.insert_command("jq .").unwrap();
assert_eq!(database.get_commands("jq").unwrap(), ["jq .", "jq .data"]);
assert_eq!(database.get_commands("q").unwrap(), [""; 0]);
assert_eq!(
database.get_command(0, "").unwrap().as_deref(),
Some("jq .")
);
assert_eq!(
database.get_command(0, "jq .").unwrap().as_deref(),
Some("jq .data")
);
}
#[rstest]
fn test_commands_max_history_size(request_db: RequestDb) {
let [collection1, collection2] = request_db.collections;
for i in 0..MAX_COMMAND_HISTORY_SIZE {
let command = format!("command {i}");
collection1.insert_command(&command).unwrap();
collection2.insert_command(&command).unwrap();
}
assert_eq!(
collection1.get_commands("").unwrap().len(),
MAX_COMMAND_HISTORY_SIZE as usize
);
collection1.insert_command("new command").unwrap();
assert_eq!(
collection1.get_commands("").unwrap().len(),
MAX_COMMAND_HISTORY_SIZE as usize
);
assert_eq!(
collection1.get_command(0, "").unwrap().as_deref(),
Some("new command")
);
assert_eq!(
collection1
.get_command(MAX_COMMAND_HISTORY_SIZE - 1, "")
.unwrap()
.as_deref(),
Some("command 1")
);
}