diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-09-23 08:33:06 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-09-23 08:34:45 +0200 |
| commit | 2dc74d621399be454abbbff892fb46204ddc6e7b (patch) | |
| tree | f9525527fc09c465d4e2e4a4f665bfd444b889f8 /crates/rocie-server/tests/_testenv/init.rs | |
| parent | feat: Provide basic barcode handling support (diff) | |
| download | server-2dc74d621399be454abbbff892fb46204ddc6e7b.zip | |
feat(treewide): Add tests and barcode buying/consuming
Diffstat (limited to 'crates/rocie-server/tests/_testenv/init.rs')
| -rw-r--r-- | crates/rocie-server/tests/_testenv/init.rs | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/crates/rocie-server/tests/_testenv/init.rs b/crates/rocie-server/tests/_testenv/init.rs new file mode 100644 index 0000000..5309fea --- /dev/null +++ b/crates/rocie-server/tests/_testenv/init.rs @@ -0,0 +1,210 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use std::{ + env, + ffi::OsStr, + fmt::Write, + fs, io, mem, + path::{Path, PathBuf}, + process::{self, Stdio}, + thread::sleep, + time::Duration, +}; + +use rocie_client::apis::configuration::Configuration; + +use crate::{_testenv::Paths, testenv::TestEnv}; + +fn target_dir() -> PathBuf { + // Tests exe is in target/debug/deps, the *rocie-server* exe is in target/debug + env::current_exe() + .expect("./target/debug/deps/rocie-server-*") + .parent() + .expect("./target/debug/deps") + .parent() + .expect("./target/debug") + .parent() + .expect("./target") + .to_path_buf() +} + +fn test_dir(name: &'static str, port: u32) -> PathBuf { + target_dir().join("tests").join(name).join(port.to_string()) +} + +fn prepare_files_and_dirs(test_dir: &Path) -> io::Result<Paths> { + fs::create_dir_all(test_dir)?; + + let db_path = test_dir.join("database.sqlite"); + + { + // Remove all files, so that the test run stays pure + for entry in fs::read_dir(test_dir).unwrap() { + let entry = entry.unwrap(); + let entry_ft = entry.file_type().unwrap(); + + if entry_ft.is_dir() { + fs::remove_dir_all(entry.path())?; + } else if entry_ft.is_file() { + fs::remove_file(entry.path())?; + } else { + panic!("Unknown file: {} ({entry_ft:#?})", entry.path().display()); + } + } + } + + Ok(Paths { + db: db_path, + test_dir: test_dir.to_owned(), + }) +} + +fn find_server_exe() -> PathBuf { + let target = target_dir().join("debug"); + + let exe_name = if cfg!(windows) { + "rocie-server.exe" + } else { + "rocie-server" + }; + + target.join(exe_name) +} + +fn rocie_base_path(port: &str) -> String { + format!("http://127.0.0.1:{port}") +} + +fn rocie_server_args<'a>(paths: &'a Paths, port: &'a str) -> [&'a OsStr; 5] { + [ + OsStr::new("serve"), + OsStr::new("--db-path"), + paths.db.as_os_str(), + OsStr::new("--port"), + OsStr::new(port), + ] +} + +impl TestEnv { + pub(crate) fn new(name: &'static str, port: u32) -> TestEnv { + let test_dir = test_dir(name, port); + + let paths = prepare_files_and_dirs(&test_dir) + .inspect_err(|err| panic!("Error during test dir preparation: {err}")) + .unwrap(); + + let server_process = { + let server_exe = find_server_exe(); + let mut cmd = process::Command::new(&server_exe); + + cmd.current_dir(&paths.test_dir); + + cmd.stdout(Stdio::piped()); + cmd.stderr(Stdio::piped()); + + cmd.args(rocie_server_args(&paths, port.to_string().as_str())); + + let child = cmd.spawn().expect("server spawn"); + + // Give the server time to start. + // TODO(@bpeetz): Use a better synchronization primitive <2025-09-11> + sleep(Duration::from_millis(240)); + + child + }; + + let config = { + let mut inner = Configuration::new(); + inner.base_path = rocie_base_path(port.to_string().as_str()); + inner.user_agent = Some(String::from("Rocie test driver")); + inner + }; + + let me = TestEnv { + name, + test_dir, + paths, + server_process: Some(server_process), + config, + port: port.to_string(), + }; + + me.log(format!("Starting test `{name}` on port `{port}`")); + + me + } +} + +impl Drop for TestEnv { + fn drop(&mut self) { + /// Format an error message for when the server did not exit successfully. + fn format_exit(args: &[&OsStr], output: &process::Output) -> String { + let mut base = String::new(); + + { + let args = args + .iter() + .map(|s| s.to_str().unwrap()) + .collect::<Vec<_>>() + .join(" "); + + if output.status.success() { + writeln!(base, "`rocie-server {args}` did exit successfully.") + .expect("In-memory"); + } else { + writeln!(base, "`rocie-server {args}` did not exit successfully.") + .expect("In-memory"); + } + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + if !stdout.is_empty() { + writeln!(base, "Stdout:\n---\n{stdout}\n---").expect("In-memory"); + } + if !stderr.is_empty() { + writeln!(base, "Stderr:\n---\n{stderr}\n---").expect("In-memory"); + } + + base + } + + { + // Stop the server process via SIGTERM. + let mut kill = process::Command::new("kill") + .args([ + "-s", + "TERM", + &self.server_process.as_ref().unwrap().id().to_string(), + ]) + .spawn() + .unwrap(); + + eprintln!("Killing the server process"); + + kill.wait().unwrap(); + } + + let output = mem::take(&mut self.server_process) + .expect("Is some at this point") + .wait_with_output() + .expect("server exit output"); + + eprintln!( + "{}", + format_exit( + rocie_server_args(&self.paths, &self.port).as_slice(), + &output + ) + ); + } +} |
