From 3bc2141b75e704fb65981af21e05ae70b104350c Mon Sep 17 00:00:00 2001 From: Serhii Mamontov Date: Mon, 28 Aug 2023 18:15:05 +0300 Subject: [PATCH] test(input): add unit tests for `PresenceInput` and `SubscribeInput` Add unit tests to verify arithmetic operations with list of channels and groups in `PresenceInput` and `SubscribeInput`. refactor(cargo): update `Cargo.toml` to include `subscribe` and `presence` Update `Cargo.toml` to include `subscribe` and `presence` features in `full` and `default` feature sets. refactor(dead_code): removed dead code attributes --- Cargo.toml | 2 +- src/core/event_engine/cancel.rs | 2 +- src/dx/presence/builders/set_state.rs | 1 - src/dx/presence/event_engine/types.rs | 458 +++++++++++++++++-- src/dx/presence/mod.rs | 1 - src/dx/subscribe/event_engine/types.rs | 604 ++++++++++++++++++++++--- 6 files changed, 982 insertions(+), 86 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 99fce0e3..2336e90d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ build = "build.rs" [features] # Enables all non-conflicting features -full = ["publish", "access", "serde", "reqwest", "aescbc", "parse_token", "blocking", "std", "tokio"] +full = ["publish", "subscribe", "presence", "access", "serde", "reqwest", "aescbc", "parse_token", "blocking", "std", "tokio"] # Enables all default features default = ["publish", "subscribe", "presence", "serde", "reqwest", "aescbc", "std", "blocking", "tokio"] diff --git a/src/core/event_engine/cancel.rs b/src/core/event_engine/cancel.rs index be87a775..2147d01c 100644 --- a/src/core/event_engine/cancel.rs +++ b/src/core/event_engine/cancel.rs @@ -10,7 +10,7 @@ use crate::{ }; #[derive(Debug)] -pub struct CancellationTask { +pub(crate) struct CancellationTask { cancel_rx: Receiver, id: String, } diff --git a/src/dx/presence/builders/set_state.rs b/src/dx/presence/builders/set_state.rs index 185c6f37..a10d0f02 100644 --- a/src/dx/presence/builders/set_state.rs +++ b/src/dx/presence/builders/set_state.rs @@ -163,7 +163,6 @@ impl SetStateRequest { } } -#[allow(dead_code)] impl SetStateRequestBuilder where T: Transport, diff --git a/src/dx/presence/event_engine/types.rs b/src/dx/presence/event_engine/types.rs index a6f47737..299a1a38 100644 --- a/src/dx/presence/event_engine/types.rs +++ b/src/dx/presence/event_engine/types.rs @@ -17,7 +17,7 @@ use crate::{ /// Object contains information about channels and groups which should be used /// with presence event engine states. #[derive(Clone, Debug, PartialEq)] -pub struct PresenceInput { +pub(crate) struct PresenceInput { /// Optional list of channels. /// /// List of channels for which `user_id` presence should be managed. @@ -66,26 +66,39 @@ impl PresenceInput { .clone() .map(|ch| ch.into_iter().collect()) } + + fn join_sets( + &self, + lhs: &Option>, + rhs: &Option>, + ) -> Option> { + match (lhs, rhs) { + (Some(lhs), Some(rhs)) => Some(lhs.iter().cloned().chain(rhs.to_owned()).collect()), + (Some(lhs), None) => Some(lhs.to_owned()), + (None, Some(rhs)) => Some(rhs.to_owned()), + _ => None, + } + } + + fn sub_sets( + &self, + lhs: &Option>, + rhs: &Option>, + ) -> Option> { + match (lhs.to_owned(), rhs.to_owned()) { + (Some(lhs), Some(rhs)) => Some(&lhs - &rhs).filter(|diff| !diff.is_empty()), + (Some(lhs), None) => Some(lhs), + _ => None, + } + } } impl Add for PresenceInput { type Output = Self; fn add(self, rhs: Self) -> Self::Output { - let channel_groups: Option> = - match (self.channel_groups, rhs.channel_groups) { - (Some(lhs), Some(rhs)) => Some(lhs.into_iter().chain(rhs).collect()), - (Some(lhs), None) => Some(lhs), - (None, Some(rhs)) => Some(rhs), - _ => None, - }; - let channels: Option> = match (self.channels, rhs.channels) { - (Some(lhs), Some(rhs)) => Some(lhs.into_iter().chain(rhs).collect()), - (Some(lhs), None) => Some(lhs), - (None, Some(rhs)) => Some(rhs), - _ => None, - }; - + let channel_groups = self.join_sets(&self.channel_groups, &rhs.channel_groups); + let channels = self.join_sets(&self.channels, &rhs.channels); let channel_groups_is_empty = channel_groups.as_ref().map_or(true, |set| set.is_empty()); let channels_is_empty = channels.as_ref().map_or(true, |set| set.is_empty()); @@ -101,18 +114,8 @@ impl Sub for PresenceInput { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - let channel_groups: Option> = - match (self.channel_groups, rhs.channel_groups) { - (Some(lhs), Some(rhs)) => Some(&lhs - &rhs), - (Some(lhs), None) => Some(lhs), - _ => None, - }; - let channels: Option> = match (self.channels, rhs.channels) { - (Some(lhs), Some(rhs)) => Some(&lhs - &rhs), - (Some(lhs), None) => Some(lhs), - _ => None, - }; - + let channel_groups = self.sub_sets(&self.channel_groups, &rhs.channel_groups); + let channels = self.sub_sets(&self.channels, &rhs.channels); let channel_groups_is_empty = channel_groups.as_ref().map_or(true, |set| set.is_empty()); let channels_is_empty = channels.as_ref().map_or(true, |set| set.is_empty()); @@ -148,3 +151,404 @@ pub(crate) struct PresenceParameters<'execution> { /// Identifier of effect which requested to create request. pub effect_id: &'execution str, } + +#[cfg(test)] +mod it_should { + use super::*; + + #[test] + fn create_empty_input() { + let input = PresenceInput::new(&None, &None); + assert!(input.is_empty); + } + + #[test] + fn create_input_with_unique_channels() { + let input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &None, + ); + + assert!(!input.is_empty); + assert_eq!(input.channels().unwrap().len(), 2); + assert_eq!( + input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-2".to_string()] + ); + } + + #[test] + fn create_input_with_unique_channel_groups() { + let input = PresenceInput::new( + &None, + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-2".into(), + ]), + ); + + assert!(!input.is_empty); + assert_eq!(input.channel_groups().unwrap().len(), 2); + assert_eq!( + input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-1".to_string(), "channel-group-2".to_string()] + ); + } + + #[test] + fn add_unique_channels_to_empty_input() { + let empty_input = PresenceInput::new(&None, &None); + let input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &None, + ); + + assert!(!input.is_empty); + + let joint_input = empty_input + input; + + assert!(!joint_input.is_empty); + assert_eq!(joint_input.channels().unwrap().len(), 2); + assert_eq!( + joint_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-2".to_string()] + ); + assert!(joint_input.channel_groups().is_none()); + } + + #[test] + fn add_unique_channel_groups_to_empty_input() { + let empty_input = PresenceInput::new(&None, &None); + let input = PresenceInput::new( + &None, + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-2".into(), + ]), + ); + + assert!(!input.is_empty); + + let joint_input = empty_input + input; + + assert!(!joint_input.is_empty); + assert!(joint_input.channels().is_none()); + assert_eq!(joint_input.channel_groups().unwrap().len(), 2); + assert_eq!( + joint_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-1".to_string(), "channel-group-2".to_string()] + ); + } + + #[test] + fn add_unique_channels_and_channel_groups_to_existing_input() { + let existing_input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-4".into(), + "channel-2".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-3".into(), + "channel-group-5".into(), + ]), + ); + let input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-2".into(), + ]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let joint_input = existing_input + input; + + assert!(!joint_input.is_empty); + assert_eq!(joint_input.channels().unwrap().len(), 3); + assert_eq!(joint_input.channel_groups().unwrap().len(), 4); + assert_eq!( + joint_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec![ + "channel-1".to_string(), + "channel-2".to_string(), + "channel-4".to_string() + ] + ); + assert_eq!( + joint_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec![ + "channel-group-1".to_string(), + "channel-group-2".to_string(), + "channel-group-3".to_string(), + "channel-group-5".to_string() + ] + ); + } + + #[test] + fn remove_channels_from_empty_input() { + let empty_input = PresenceInput::new(&None, &None); + let input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &None, + ); + + assert!(!input.is_empty); + + let diff_input = empty_input - input; + + assert!(diff_input.is_empty); + assert!(diff_input.channels().is_none()); + assert!(diff_input.channel_groups().is_none()); + } + + #[test] + fn remove_channel_groups_from_empty_input() { + let empty_input = PresenceInput::new(&None, &None); + let input = PresenceInput::new( + &None, + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-1".into(), + ]), + ); + + assert!(!input.is_empty); + + let diff_input = empty_input - input; + + assert!(diff_input.is_empty); + assert!(diff_input.channels().is_none()); + assert!(diff_input.channel_groups().is_none()); + } + + #[test] + fn remove_unique_channels_from_existing_input() { + let existing_input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-3".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-3".into(), + ]), + ); + let input = PresenceInput::new(&Some(vec!["channel-2".into(), "channel-2".into()]), &None); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(!diff_input.is_empty); + assert_eq!(diff_input.channels().unwrap().len(), 2); + assert_eq!(diff_input.channel_groups().unwrap().len(), 3); + assert_eq!( + diff_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-3".to_string()] + ); + assert_eq!( + diff_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec![ + "channel-group-1".to_string(), + "channel-group-2".to_string(), + "channel-group-3".to_string(), + ] + ); + } + + #[test] + fn remove_unique_channel_groups_from_existing_input() { + let existing_input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-3".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-3".into(), + ]), + ); + let input = PresenceInput::new(&None, &Some(vec!["channel-group-1".into()])); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(!diff_input.is_empty); + assert_eq!(diff_input.channels().unwrap().len(), 3); + assert_eq!(diff_input.channel_groups().unwrap().len(), 2); + assert_eq!( + diff_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec![ + "channel-1".to_string(), + "channel-2".to_string(), + "channel-3".to_string() + ] + ); + assert_eq!( + diff_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-2".to_string(), "channel-group-3".to_string(),] + ); + } + + #[test] + fn remove_unique_channels_and_channel_groups_from_existing_input() { + let existing_input = PresenceInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-3".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-3".into(), + ]), + ); + let input = PresenceInput::new( + &Some(vec!["channel-3".into()]), + &Some(vec!["channel-group-2".into(), "channel-group-3".into()]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(!diff_input.is_empty); + assert_eq!(diff_input.channels().unwrap().len(), 2); + assert_eq!(diff_input.channel_groups().unwrap().len(), 1); + assert_eq!( + diff_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-2".to_string(),] + ); + assert_eq!( + diff_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-1".to_string(),] + ); + } + + #[test] + fn remove_all_channels_and_channel_groups_from_existing_input() { + let existing_input = PresenceInput::new( + &Some(vec!["channel-1".into(), "channel-2".into()]), + &Some(vec!["channel-group-1".into(), "channel-group-2".into()]), + ); + let input = PresenceInput::new( + &Some(vec!["channel-1".into(), "channel-2".into()]), + &Some(vec!["channel-group-1".into(), "channel-group-2".into()]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(diff_input.is_empty); + assert!(diff_input.channels().is_none()); + assert!(diff_input.channel_groups().is_none()); + } +} diff --git a/src/dx/presence/mod.rs b/src/dx/presence/mod.rs index fcac3fcc..91a9c051 100644 --- a/src/dx/presence/mod.rs +++ b/src/dx/presence/mod.rs @@ -216,7 +216,6 @@ where /// /// Presence configuration used only with presence event engine. #[cfg(feature = "std")] - #[allow(dead_code)] pub(crate) fn configure_presence(&self) -> Arc>> { { let mut slot = self.presence.write(); diff --git a/src/dx/subscribe/event_engine/types.rs b/src/dx/subscribe/event_engine/types.rs index 788bfae5..cad3a70a 100644 --- a/src/dx/subscribe/event_engine/types.rs +++ b/src/dx/subscribe/event_engine/types.rs @@ -45,7 +45,6 @@ pub struct SubscribeInput { pub is_empty: bool, } -#[allow(dead_code)] impl SubscribeInput { pub fn new(channels: &Option>, channel_groups: &Option>) -> Self { let channels = channels.as_ref().map(|channels| { @@ -98,26 +97,39 @@ impl SubscribeInput { channel_groups.contains(channel_group) }) } + + fn join_sets( + &self, + lhs: &Option>, + rhs: &Option>, + ) -> Option> { + match (lhs, rhs) { + (Some(lhs), Some(rhs)) => Some(lhs.iter().cloned().chain(rhs.to_owned()).collect()), + (Some(lhs), None) => Some(lhs.to_owned()), + (None, Some(rhs)) => Some(rhs.to_owned()), + _ => None, + } + } + + fn sub_sets( + &self, + lhs: &Option>, + rhs: &Option>, + ) -> Option> { + match (lhs.to_owned(), rhs.to_owned()) { + (Some(lhs), Some(rhs)) => Some(&lhs - &rhs).filter(|diff| !diff.is_empty()), + (Some(lhs), None) => Some(lhs), + _ => None, + } + } } impl Add for SubscribeInput { type Output = Self; fn add(self, rhs: Self) -> Self::Output { - let channel_groups: Option> = - match (self.channel_groups, rhs.channel_groups) { - (Some(lhs), Some(rhs)) => Some(lhs.into_iter().chain(rhs).collect()), - (Some(lhs), None) => Some(lhs), - (None, Some(rhs)) => Some(rhs), - _ => None, - }; - let channels: Option> = match (self.channels, rhs.channels) { - (Some(lhs), Some(rhs)) => Some(lhs.into_iter().chain(rhs).collect()), - (Some(lhs), None) => Some(lhs), - (None, Some(rhs)) => Some(rhs), - _ => None, - }; - + let channel_groups = self.join_sets(&self.channel_groups, &rhs.channel_groups); + let channels = self.join_sets(&self.channels, &rhs.channels); let channel_groups_is_empty = channel_groups.as_ref().map_or(true, |set| set.is_empty()); let channels_is_empty = channels.as_ref().map_or(true, |set| set.is_empty()); @@ -131,21 +143,8 @@ impl Add for SubscribeInput { impl AddAssign for SubscribeInput { fn add_assign(&mut self, rhs: Self) { - let channel_groups: Option> = - match (self.channel_groups.clone(), rhs.channel_groups.clone()) { - (Some(lhs), Some(rhs)) => Some(lhs.into_iter().chain(rhs).collect()), - (Some(lhs), None) => Some(lhs), - (None, Some(rhs)) => Some(rhs), - _ => None, - }; - let channels: Option> = match (self.channels.clone(), rhs.channels.clone()) - { - (Some(lhs), Some(rhs)) => Some(lhs.into_iter().chain(rhs).collect()), - (Some(lhs), None) => Some(lhs), - (None, Some(rhs)) => Some(rhs), - _ => None, - }; - + let channel_groups = self.join_sets(&self.channel_groups, &rhs.channel_groups); + let channels = self.join_sets(&self.channels, &rhs.channels); let channel_groups_is_empty = channel_groups.as_ref().map_or(true, |set| set.is_empty()); let channels_is_empty = channels.as_ref().map_or(true, |set| set.is_empty()); @@ -159,18 +158,8 @@ impl Sub for SubscribeInput { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - let channel_groups: Option> = - match (self.channel_groups, rhs.channel_groups) { - (Some(lhs), Some(rhs)) => Some(&lhs - &rhs), - (Some(lhs), None) => Some(lhs), - _ => None, - }; - let channels: Option> = match (self.channels, rhs.channels) { - (Some(lhs), Some(rhs)) => Some(&lhs - &rhs), - (Some(lhs), None) => Some(lhs), - _ => None, - }; - + let channel_groups = self.sub_sets(&self.channel_groups, &rhs.channel_groups); + let channels = self.sub_sets(&self.channels, &rhs.channels); let channel_groups_is_empty = channel_groups.as_ref().map_or(true, |set| set.is_empty()); let channels_is_empty = channels.as_ref().map_or(true, |set| set.is_empty()); @@ -184,19 +173,8 @@ impl Sub for SubscribeInput { impl SubAssign for SubscribeInput { fn sub_assign(&mut self, rhs: Self) { - let channel_groups: Option> = - match (self.channel_groups.clone(), rhs.channel_groups.clone()) { - (Some(lhs), Some(rhs)) => Some(&lhs - &rhs), - (Some(lhs), None) => Some(lhs), - _ => None, - }; - let channels: Option> = match (self.channels.clone(), rhs.channels.clone()) - { - (Some(lhs), Some(rhs)) => Some(&lhs - &rhs), - (Some(lhs), None) => Some(lhs), - _ => None, - }; - + let channel_groups = self.sub_sets(&self.channel_groups, &rhs.channel_groups); + let channels = self.sub_sets(&self.channels, &rhs.channels); let channel_groups_is_empty = channel_groups.as_ref().map_or(true, |set| set.is_empty()); let channels_is_empty = channels.as_ref().map_or(true, |set| set.is_empty()); @@ -233,3 +211,519 @@ pub(crate) struct SubscriptionParams<'execution> { /// Identifier of effect which requested to create request. pub effect_id: &'execution str, } + +#[cfg(test)] +mod it_should { + use super::*; + + #[test] + fn create_empty_input() { + let input = SubscribeInput::new(&None, &None); + assert!(input.is_empty); + } + + #[test] + fn create_input_with_unique_channels() { + let input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &None, + ); + + assert!(!input.is_empty); + assert_eq!(input.channels().unwrap().len(), 2); + assert_eq!( + input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-2".to_string()] + ); + } + + #[test] + fn create_input_with_unique_channel_groups() { + let input = SubscribeInput::new( + &None, + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-2".into(), + ]), + ); + + assert!(!input.is_empty); + assert_eq!(input.channel_groups().unwrap().len(), 2); + assert_eq!( + input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-1".to_string(), "channel-group-2".to_string()] + ); + } + + #[test] + fn add_unique_channels_to_empty_input() { + let empty_input = SubscribeInput::new(&None, &None); + let input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &None, + ); + + assert!(!input.is_empty); + + let joint_input = empty_input + input; + + assert!(!joint_input.is_empty); + assert_eq!(joint_input.channels().unwrap().len(), 2); + assert_eq!( + joint_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-2".to_string()] + ); + assert!(joint_input.channel_groups().is_none()); + } + + #[test] + fn add_unique_channel_groups_to_empty_input() { + let empty_input = SubscribeInput::new(&None, &None); + let input = SubscribeInput::new( + &None, + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-2".into(), + ]), + ); + + assert!(!input.is_empty); + + let joint_input = empty_input + input; + + assert!(!joint_input.is_empty); + assert!(joint_input.channels().is_none()); + assert_eq!(joint_input.channel_groups().unwrap().len(), 2); + assert_eq!( + joint_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-1".to_string(), "channel-group-2".to_string()] + ); + } + + #[test] + fn add_unique_channels_and_channel_groups_to_existing_input() { + let existing_input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-4".into(), + "channel-2".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-3".into(), + "channel-group-5".into(), + ]), + ); + let input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-2".into(), + ]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let joint_input = existing_input + input; + + assert!(!joint_input.is_empty); + assert_eq!(joint_input.channels().unwrap().len(), 3); + assert_eq!(joint_input.channel_groups().unwrap().len(), 4); + assert_eq!( + joint_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec![ + "channel-1".to_string(), + "channel-2".to_string(), + "channel-4".to_string() + ] + ); + assert_eq!( + joint_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec![ + "channel-group-1".to_string(), + "channel-group-2".to_string(), + "channel-group-3".to_string(), + "channel-group-5".to_string() + ] + ); + } + + #[test] + fn add_assign_unique_channels_and_channel_groups_to_existing_input() { + let mut existing_input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-4".into(), + "channel-2".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-3".into(), + "channel-group-5".into(), + ]), + ); + let input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-2".into(), + ]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + existing_input += input; + + assert!(!existing_input.is_empty); + assert_eq!(existing_input.channels().unwrap().len(), 3); + assert_eq!(existing_input.channel_groups().unwrap().len(), 4); + assert_eq!( + existing_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec![ + "channel-1".to_string(), + "channel-2".to_string(), + "channel-4".to_string() + ] + ); + assert_eq!( + existing_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec![ + "channel-group-1".to_string(), + "channel-group-2".to_string(), + "channel-group-3".to_string(), + "channel-group-5".to_string() + ] + ); + } + + #[test] + fn remove_channels_from_empty_input() { + let empty_input = SubscribeInput::new(&None, &None); + let input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-1".into(), + ]), + &None, + ); + + assert!(!input.is_empty); + + let diff_input = empty_input - input; + + assert!(diff_input.is_empty); + assert!(diff_input.channels().is_none()); + assert!(diff_input.channel_groups().is_none()); + } + + #[test] + fn remove_channel_groups_from_empty_input() { + let empty_input = SubscribeInput::new(&None, &None); + let input = SubscribeInput::new( + &None, + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-1".into(), + ]), + ); + + assert!(!input.is_empty); + + let diff_input = empty_input - input; + + assert!(diff_input.is_empty); + assert!(diff_input.channels().is_none()); + assert!(diff_input.channel_groups().is_none()); + } + + #[test] + fn remove_unique_channels_from_existing_input() { + let existing_input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-3".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-3".into(), + ]), + ); + let input = SubscribeInput::new(&Some(vec!["channel-2".into(), "channel-2".into()]), &None); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(!diff_input.is_empty); + assert_eq!(diff_input.channels().unwrap().len(), 2); + assert_eq!(diff_input.channel_groups().unwrap().len(), 3); + assert_eq!( + diff_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-3".to_string()] + ); + assert_eq!( + diff_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec![ + "channel-group-1".to_string(), + "channel-group-2".to_string(), + "channel-group-3".to_string(), + ] + ); + } + + #[test] + fn remove_unique_channel_groups_from_existing_input() { + let existing_input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-3".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-3".into(), + ]), + ); + let input = SubscribeInput::new(&None, &Some(vec!["channel-group-1".into()])); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(!diff_input.is_empty); + assert_eq!(diff_input.channels().unwrap().len(), 3); + assert_eq!(diff_input.channel_groups().unwrap().len(), 2); + assert_eq!( + diff_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec![ + "channel-1".to_string(), + "channel-2".to_string(), + "channel-3".to_string() + ] + ); + assert_eq!( + diff_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-2".to_string(), "channel-group-3".to_string(),] + ); + } + + #[test] + fn remove_unique_channels_and_channel_groups_from_existing_input() { + let existing_input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-3".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-3".into(), + ]), + ); + let input = SubscribeInput::new( + &Some(vec!["channel-3".into()]), + &Some(vec!["channel-group-2".into(), "channel-group-3".into()]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(!diff_input.is_empty); + assert_eq!(diff_input.channels().unwrap().len(), 2); + assert_eq!(diff_input.channel_groups().unwrap().len(), 1); + assert_eq!( + diff_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-2".to_string(),] + ); + assert_eq!( + diff_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-1".to_string(),] + ); + } + + #[test] + fn remove_assign_unique_channels_and_channel_groups_from_existing_input() { + let mut existing_input = SubscribeInput::new( + &Some(vec![ + "channel-1".into(), + "channel-2".into(), + "channel-3".into(), + ]), + &Some(vec![ + "channel-group-1".into(), + "channel-group-2".into(), + "channel-group-3".into(), + ]), + ); + let input = SubscribeInput::new( + &Some(vec!["channel-3".into()]), + &Some(vec!["channel-group-2".into(), "channel-group-3".into()]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + existing_input -= input; + + assert!(!existing_input.is_empty); + assert_eq!(existing_input.channels().unwrap().len(), 2); + assert_eq!(existing_input.channel_groups().unwrap().len(), 1); + assert_eq!( + existing_input + .channels() + .map(|mut channels| { + channels.sort(); + channels + }) + .unwrap(), + vec!["channel-1".to_string(), "channel-2".to_string(),] + ); + assert_eq!( + existing_input + .channel_groups() + .map(|mut groups| { + groups.sort(); + groups + }) + .unwrap(), + vec!["channel-group-1".to_string(),] + ); + } + + #[test] + fn remove_all_channels_and_channel_groups_from_existing_input() { + let existing_input = SubscribeInput::new( + &Some(vec!["channel-1".into(), "channel-2".into()]), + &Some(vec!["channel-group-1".into(), "channel-group-2".into()]), + ); + let input = SubscribeInput::new( + &Some(vec!["channel-1".into(), "channel-2".into()]), + &Some(vec!["channel-group-1".into(), "channel-group-2".into()]), + ); + + assert!(!existing_input.is_empty); + assert!(!input.is_empty); + + let diff_input = existing_input - input; + + assert!(diff_input.is_empty); + assert!(diff_input.channels().is_none()); + assert!(diff_input.channel_groups().is_none()); + } +}