use crate::Plugin; use indexmap::IndexMap; use nu_errors::ShellError; use nu_protocol::{ CallInfo, EvaluatedArgs, Primitive, ReturnSuccess, ReturnValue, UntaggedValue, Value, }; use nu_source::Tag; use nu_test_support::value::column_path; use nu_value_ext::ValueExt; pub struct PluginTest<'a, T: Plugin> { plugin: &'a mut T, call_info: CallInfo, input: Value, } impl<'a, T: Plugin> PluginTest<'a, T> { pub fn for_plugin(plugin: &'a mut T) -> Self { PluginTest { plugin, call_info: CallStub::new().create(), input: UntaggedValue::nothing().into_value(Tag::unknown()), } } pub fn args(&mut self, call_info: CallInfo) -> &mut PluginTest<'a, T> { self.call_info = call_info; self } pub fn configure(&mut self, callback: impl FnOnce(Vec)) -> &mut PluginTest<'a, T> { let signature = self .plugin .config() .expect("There was a problem configuring the plugin."); callback(signature.named.keys().map(String::from).collect()); self } pub fn input(&mut self, value: Value) -> &mut PluginTest<'a, T> { self.input = value; self } pub fn test(&mut self) -> Result, ShellError> { let return_values = self.plugin.filter(self.input.clone()); let mut return_values = return_values?; let end = self.plugin.end_filter(); return_values.extend(end?); self.plugin.quit(); Ok(return_values) } pub fn setup( &mut self, callback: impl FnOnce(&mut T, Result, ShellError>), ) -> &mut PluginTest<'a, T> { let call_stub = self.call_info.clone(); self.configure(|flags_configured| { let flags_registered = &call_stub.args.named; let flag_passed = match flags_registered { Some(names) => Some(names.keys().map(String::from).collect::>()), None => None, }; if let Some(flags) = flag_passed { for flag in flags { assert!( flags_configured.iter().any(|f| *f == flag), format!( "The flag you passed ({}) is not configured in the plugin.", flag ) ); } } }); let return_values = self.plugin.begin_filter(call_stub); callback(self.plugin, return_values); self } } pub fn plugin(plugin: &mut T) -> PluginTest { PluginTest::for_plugin(plugin) } #[derive(Default)] pub struct CallStub { positionals: Vec, flags: IndexMap, } impl CallStub { pub fn new() -> Self { Default::default() } pub fn with_named_parameter(&mut self, name: &str, value: Value) -> &mut Self { self.flags.insert(name.to_string(), value); self } pub fn with_long_flag(&mut self, name: &str) -> &mut Self { self.flags.insert( name.to_string(), UntaggedValue::boolean(true).into_value(Tag::unknown()), ); self } pub fn with_parameter(&mut self, name: &str) -> Result<&mut Self, ShellError> { let cp = column_path(&name) .as_column_path() .expect("Failed! Expected valid column path."); let cp = UntaggedValue::Primitive(Primitive::ColumnPath(cp.item)).into_value(cp.tag); self.positionals.push(cp); Ok(self) } pub fn create(&self) -> CallInfo { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), name_tag: Tag::unknown(), } } } pub fn expect_return_value_at( for_results: Result>, ShellError>, at: usize, ) -> Value { let return_values = for_results .expect("Failed! This seems to be an error getting back the results from the plugin."); for (idx, item) in return_values.iter().enumerate() { let item = match item { Ok(return_value) => return_value, Err(reason) => panic!(format!("{}", reason)), }; if idx == at { if let Some(value) = item.raw_value() { return value; } else { panic!("Internal error: could not get raw value in expect_return_value_at") } } } panic!(format!( "Couldn't get return value from stream at {}. (There are {} items)", at, return_values.len() - 1 )) }