use anyhow::{anyhow, Result}; use once_cell::sync::Lazy; use reinfer_client::User; use std::{ env, ffi::OsStr, io::Write, path::PathBuf, process::{Command, Stdio}, }; static REINFER_CLI_TEST_PROJECT: Lazy = Lazy::new(|| { env::var("REINFER_CLI_TEST_PROJECT") .expect("REINFER_CLI_TEST_PROJECT must be set for integration tests") }); static REINFER_CLI_TEST_ENDPOINT: Lazy> = Lazy::new(|| env::var("REINFER_CLI_TEST_ENDPOINT").ok()); static REINFER_CLI_TEST_CONTEXT: Lazy> = Lazy::new(|| env::var("REINFER_CLI_TEST_CONTEXT").ok()); static REINFER_CLI_TEST_TOKEN: Lazy> = Lazy::new(|| env::var("REINFER_CLI_TEST_TOKEN").ok()); static TEST_CLI: Lazy = Lazy::new(|| { let cli_path = std::env::current_exe() .ok() .and_then(|p| Some(p.parent()?.parent()?.join("re"))) .expect("Could not resolve CLI executable from test executable"); TestCli { cli_path } }); pub struct TestCli { cli_path: PathBuf, } impl TestCli { pub fn get() -> &'static Self { &TEST_CLI } pub fn user(&self) -> User { let output = self.run(["--output=json", "get", "current-user"]); serde_json::from_str::(&output).expect("Failed to deserialize user response") } pub fn project() -> String { REINFER_CLI_TEST_PROJECT.to_owned() } pub fn command(&self) -> Command { let mut command = Command::new(&self.cli_path); match (&*REINFER_CLI_TEST_CONTEXT, &*REINFER_CLI_TEST_ENDPOINT, &*REINFER_CLI_TEST_TOKEN) { (Some(context), _, _) => { command.arg("--context").arg(context); }, (_, Some(endpoint), Some(token)) => { command .arg("--endpoint") .arg(endpoint) .arg("--token") .arg(token); }, _ => panic!("Either REINFER_CLI_TEST_CONTEXT, or REINFER_CLI_TEST_ENDPOINT and REINFER_CLI_TEST_TOKEN must be set.") } command } #[track_caller] pub fn run(&self, args: impl IntoIterator>) -> String { self.output(self.command().args(args)) } #[track_caller] pub fn run_and_error(&self, args: impl IntoIterator>) -> String { self.output_error(self.command().args(args)) } #[track_caller] pub fn run_and_result( &self, args: impl IntoIterator>, ) -> Result { self.output_result(self.command().args(args)) } #[track_caller] pub fn run_with_stdin( &self, args: impl IntoIterator>, stdin: &[u8], ) -> String { let mut process = self .command() .args(args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .unwrap(); process.stdin.as_mut().unwrap().write_all(stdin).unwrap(); let output = process.wait_with_output().unwrap(); if !output.status.success() { panic!( "failed to run command:\n{}", String::from_utf8_lossy(&output.stderr) ); } String::from_utf8(output.stdout).unwrap() } #[track_caller] pub fn output(&self, command: &mut Command) -> String { let output = command.output().unwrap(); if !output.status.success() { panic!( "failed to run command:\n{}", String::from_utf8_lossy(&output.stderr) ); } String::from_utf8(output.stdout).unwrap() } #[track_caller] pub fn output_result(&self, command: &mut Command) -> Result { let output = command.output().unwrap(); if output.status.success() { Ok(String::from_utf8(output.stdout)?) } else { Err(anyhow!( "failed to run command:\n{}", String::from_utf8_lossy(&output.stderr) )) } } #[track_caller] pub fn output_error(&self, command: &mut Command) -> String { let output = command.output().unwrap(); if output.status.success() { panic!( "succeeded running command (expected failure):\n{}", String::from_utf8_lossy(&output.stdout) ); } String::from_utf8(output.stderr).unwrap() } }