286 lines
9.3 KiB
Rust
286 lines
9.3 KiB
Rust
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use tokio::sync::mpsc::{self, Receiver};
|
|
use url::Url;
|
|
|
|
use super::results::*;
|
|
use crate::complaince::result::Result as CResult;
|
|
use crate::complaince::suite::Suite;
|
|
use crate::utils::get_random;
|
|
|
|
pub struct SuiteRunnerState {
|
|
suite: Suite,
|
|
tests: HashMap<String, TestRunnerState>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TestRunnerState {
|
|
rx: Receiver<CResult>,
|
|
container_name: String,
|
|
}
|
|
|
|
impl SuiteRunnerState {
|
|
pub async fn run(
|
|
container_host: &Url,
|
|
suite: &crate::complaince::suite::Suite,
|
|
ctx: &crate::ctx::ArcCtx,
|
|
) -> ArchivableSuiteResult {
|
|
let state = Self::launch_suite(container_host, suite, &ctx);
|
|
state.collect_results(&ctx).await
|
|
}
|
|
|
|
fn launch_suite(
|
|
container_host: &Url,
|
|
suite: &crate::complaince::suite::Suite,
|
|
ctx: &crate::ctx::ArcCtx,
|
|
) -> Self {
|
|
let mut tests = HashMap::with_capacity(suite.tests.len());
|
|
for test in suite.tests.iter() {
|
|
let auth = get_random(32);
|
|
println!("starting test {}", test.name);
|
|
let mut env = HashMap::new();
|
|
env.insert("FTEST_AUTH".into(), auth.to_owned());
|
|
env.insert(
|
|
"FTEST_HOST".into(),
|
|
format!("http://ftest:{}", ctx.settings.server.port),
|
|
);
|
|
env.insert("FTEST_TARGET_HOST".into(), container_host.to_string());
|
|
env.insert("FTEST_USER".into(), "alice".into());
|
|
if let Some(custom_vars) = test.env_vars.clone() {
|
|
env.extend(custom_vars);
|
|
}
|
|
let name = format!("{}---{}--{}", suite.name, test.name, &auth[0..5]);
|
|
|
|
ctx.docker.run_container(
|
|
&name,
|
|
&test.container,
|
|
true,
|
|
&env,
|
|
Some("ftest".to_string()),
|
|
true,
|
|
);
|
|
|
|
let (tx, rx) = mpsc::channel(1);
|
|
{
|
|
let mut w = ctx.results.write().unwrap();
|
|
w.insert(auth.to_owned(), tx);
|
|
}
|
|
tests.insert(
|
|
auth.to_owned(),
|
|
TestRunnerState {
|
|
container_name: name,
|
|
rx,
|
|
},
|
|
);
|
|
}
|
|
println!("{:?}", tests);
|
|
SuiteRunnerState {
|
|
suite: suite.clone(),
|
|
tests,
|
|
}
|
|
}
|
|
|
|
async fn collect_results(mut self, ctx: &crate::ctx::ArcCtx) -> ArchivableSuiteResult {
|
|
let mut tests = Vec::with_capacity(self.tests.len());
|
|
for (auth, mut r) in self.tests.drain() {
|
|
let result = r.rx.recv().await.unwrap();
|
|
{
|
|
let mut w = ctx.results.write().unwrap();
|
|
w.remove(&auth);
|
|
}
|
|
let log = ctx.docker.get_logs(&r.container_name);
|
|
ctx.docker.rm_container(&r.container_name, true);
|
|
let container = ArchivableContainer {
|
|
name: r.container_name,
|
|
logs: log,
|
|
};
|
|
|
|
let res = ArchivableTestResult {
|
|
success: result.success,
|
|
logs: result.logs,
|
|
container,
|
|
};
|
|
let s = ArchivableTest {
|
|
name: result.test.name,
|
|
url: result.test.url,
|
|
version: result.test.version,
|
|
container: result.test.container,
|
|
env_vars: result.test.env_vars,
|
|
result: res,
|
|
};
|
|
|
|
tests.push(s);
|
|
}
|
|
|
|
let res = ArchivableSuiteResult {
|
|
suite: self.suite.clone(),
|
|
tests,
|
|
};
|
|
res
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
use crate::complaince::result::Result as CResult;
|
|
use crate::complaince::suite::Test;
|
|
use crate::{AppCtx, Ctx, Settings};
|
|
|
|
use std::sync::Arc;
|
|
|
|
use url::Url;
|
|
// use std::sync::RwLock;
|
|
|
|
// #[derive(Clone, Eq, PartialEq, Debug)]
|
|
// enum ContainerState {
|
|
// NoContainer,
|
|
// Running(String),
|
|
// Stopped,
|
|
// Removed,
|
|
// }
|
|
//
|
|
// #[derive(Clone)]
|
|
// struct TestDocker {
|
|
// state: Arc<RwLock<ContainerState>>,
|
|
// }
|
|
//
|
|
// impl TestDocker {
|
|
// pub fn new() -> Self {
|
|
// Self {
|
|
// state: Arc::new(RwLock::new(ContainerState::NoContainer)),
|
|
// }
|
|
// }
|
|
// }
|
|
// impl DockerLike for TestDocker {
|
|
// fn version(&self) -> String {
|
|
// unimplemented!();
|
|
// }
|
|
// fn run_container(
|
|
// &self,
|
|
// name: &str,
|
|
// img: &str,
|
|
// detached: bool,
|
|
// env: &HashMap<String, String>,
|
|
// network: Option<String>,
|
|
// pull: bool,
|
|
// ) {
|
|
// let mut w = self.state.write().unwrap();
|
|
// if *w == ContainerState::NoContainer {
|
|
// *w = ContainerState::Running(name.to_string());
|
|
// } else {
|
|
// panic!("Container is {:?}", w);
|
|
// }
|
|
// }
|
|
// fn get_exit_status(&self, name: &str) -> isize {
|
|
// 0
|
|
// }
|
|
// fn get_logs(&self, name: &str) -> String {
|
|
// "".into()
|
|
// }
|
|
// fn rm_container(&self, name: &str, force: bool) {
|
|
// let mut w = self.state.write().unwrap();
|
|
// if *w == ContainerState::Stopped {
|
|
// *w = ContainerState::Removed;
|
|
// } else {
|
|
// panic!("Container is {:?}", w);
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
#[actix_rt::test]
|
|
async fn suite_runner_works() {
|
|
const LOGS: &str = "SUITE RUNNER LOG STRING";
|
|
|
|
let settings = Settings::new().unwrap();
|
|
let ctx = Ctx::new(settings.clone()).await;
|
|
// ctx.docker = Arc::new(TestDocker::new());
|
|
let ctx = AppCtx::new(Arc::new(ctx));
|
|
|
|
let mut dummy_test = Test {
|
|
name: "suite_runner_works".into(),
|
|
url: Url::parse("https://git.batsense.net/ForgeFlux/ftest").unwrap(),
|
|
version: semver::Version::parse("1.0.1").unwrap(),
|
|
container: "forgeflux/ftest-docker-cmd-tester".into(),
|
|
env_vars: None,
|
|
};
|
|
let mut env = HashMap::new();
|
|
env.insert("TEST_NAME".into(), dummy_test.name.clone());
|
|
dummy_test.env_vars = Some(env);
|
|
|
|
let mut dummy_test2 = dummy_test.clone();
|
|
dummy_test2.name = "suite_runner_works2".into();
|
|
let mut env = HashMap::new();
|
|
env.insert("TEST_NAME".into(), dummy_test2.name.clone());
|
|
dummy_test2.env_vars = Some(env);
|
|
|
|
let suite = Suite {
|
|
name: "suite_runner_works".into(),
|
|
description: "testing suite runner".into(),
|
|
version: semver::Version::parse("1.0.1").unwrap(),
|
|
tests: vec![dummy_test.clone(), dummy_test2.clone()],
|
|
};
|
|
|
|
let state = SuiteRunnerState::launch_suite(
|
|
&Url::parse("http://suite_runner_works.service").unwrap(),
|
|
&suite,
|
|
&ctx,
|
|
);
|
|
assert_eq!(state.tests.len(), 2);
|
|
std::thread::sleep(std::time::Duration::new(13, 0));
|
|
|
|
for (k, v) in state.tests.iter() {
|
|
assert_eq!(ctx.docker.get_exit_status(&v.container_name), 0);
|
|
{
|
|
let r = ctx.results.read().unwrap();
|
|
let tx = r.get(k).unwrap();
|
|
let tx_result = CResult {
|
|
test: dummy_test.clone(),
|
|
success: true,
|
|
// sent by the app
|
|
logs: format!("{}{LOGS}", v.container_name),
|
|
};
|
|
|
|
tx.send(tx_result).await.unwrap();
|
|
}
|
|
}
|
|
let results = state.collect_results(&ctx).await;
|
|
assert_eq!(results.tests.len(), 2);
|
|
|
|
for archivable_test in results.tests.iter() {
|
|
//let archivable_test = results.tests.get(0).unwrap();
|
|
let t = if archivable_test.name == dummy_test.name.as_ref() {
|
|
dummy_test.clone()
|
|
} else {
|
|
dummy_test2.clone()
|
|
};
|
|
assert_eq!(archivable_test.name, t.name);
|
|
assert!(archivable_test.result.success);
|
|
assert!(archivable_test.result.container.name.contains(&t.name));
|
|
assert_eq!(
|
|
archivable_test.result.logs,
|
|
format!("{}{LOGS}", archivable_test.result.container.name)
|
|
);
|
|
println!("{}", archivable_test.result.container.logs);
|
|
assert!(archivable_test.result.container.logs.contains("FTEST_AUTH"));
|
|
assert!(archivable_test.result.container.logs.contains("FTEST_HOST"));
|
|
assert!(archivable_test
|
|
.result
|
|
.container
|
|
.logs
|
|
.contains("FTEST_TARGET_HOST"));
|
|
assert!(archivable_test.result.container.logs.contains("FTEST_USER"));
|
|
assert!(archivable_test
|
|
.result
|
|
.container
|
|
.logs
|
|
.contains(&format!("TEST_NAME={}", t.name)));
|
|
}
|
|
}
|
|
}
|