use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
use std::path::PathBuf;
use std::sync::Arc;
use bindle::client::{tokens::NoToken, Client};
use bindle::signature::{
KeyRing, KeyRingLoader, KeyRingSaver, SecretKeyEntry, SecretKeyFile, SignatureRole,
};
#[allow(dead_code)]
pub const ENV_BINDLE_URL: &str = "BINDLE_URL";
#[allow(dead_code)]
#[cfg(not(target_family = "windows"))]
pub const BINARY_NAME: &str = "bindle-server";
#[allow(dead_code)]
#[cfg(target_family = "windows")]
pub const BINARY_NAME: &str = "bindle-server.exe";
const SECRET_KEY_FILE: &str = "secret_keys.toml";
const KEYRING_FILE: &str = "keyring.toml";
pub struct TestController {
pub client: Client<NoToken>,
pub base_url: String,
pub keyring: KeyRing,
pub keyring_path: PathBuf,
server_handle: std::process::Child,
_tempdir: tempfile::TempDir,
}
impl TestController {
pub async fn new(server_binary_name: &str) -> TestController {
let build_result = tokio::task::spawn_blocking(|| {
std::process::Command::new("cargo")
.args(["build", "--features", "cli"])
.output()
})
.await
.unwrap()
.expect("unable to run build command");
assert!(
build_result.status.success(),
"Error trying to build server {}",
String::from_utf8(build_result.stderr).unwrap()
);
let tempdir = tempfile::tempdir().expect("unable to create tempdir");
let address = format!("127.0.0.1:{}", get_random_port());
let base_url = format!("http://{}/v1/", address);
let test_data = PathBuf::from(
std::env::var("CARGO_MANIFEST_DIR").expect("Unable to get project directory"),
)
.join("test/data");
let mut secret_file = SecretKeyFile::load_file(test_data.join(SECRET_KEY_FILE))
.await
.expect("Unable to load secret file");
let mut keyring = test_data
.join(KEYRING_FILE)
.load()
.await
.expect("Unable to load keyring file");
let secret_file_path = tempdir.path().join("secret_keys.toml");
let key = SecretKeyEntry::new("test <test@example.com>", vec![SignatureRole::Host]);
secret_file.key.push(key.clone());
secret_file
.save_file(&secret_file_path)
.await
.expect("Unable to save host key");
keyring.add_entry(key.try_into().unwrap());
let keyring_path = tempdir.path().join("keyring.toml");
keyring_path
.save(&keyring)
.await
.expect("Unable to save keyring to disk");
let server_handle = std::process::Command::new(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("target/debug")
.join(server_binary_name),
)
.args([
"--unauthenticated",
"-d",
tempdir.path().to_string_lossy().to_string().as_str(),
"-i",
address.as_str(),
"--keyring",
keyring_path.to_string_lossy().to_string().as_str(),
])
.env("BINDLE_SIGNING_KEYS", secret_file_path)
.spawn()
.expect("unable to start bindle server");
let mut wait_count = 1;
loop {
if wait_count >= 11 {
panic!("Ran out of retries waiting for server to start");
}
match tokio::net::TcpStream::connect(&address).await {
Ok(_) => break,
Err(e) => {
eprintln!("Waiting for server to come up, attempt {}. Will retry in 1 second. Got error {:?}", wait_count, e);
wait_count += 1;
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
}
}
let client = Client::new(&base_url, NoToken, Arc::new(keyring.clone()))
.expect("unable to setup bindle client");
TestController {
client,
base_url,
keyring,
keyring_path,
server_handle,
_tempdir: tempdir,
}
}
}
impl Drop for TestController {
fn drop(&mut self) {
let _ = self.server_handle.kill();
}
}
fn get_random_port() -> u16 {
TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))
.expect("Unable to bind to check for port")
.local_addr()
.unwrap()
.port()
}