Add initial coordinator e2e tests

This commit is contained in:
Luke Parker
2023-08-01 19:00:48 -04:00
parent e3a70ef0dc
commit d5c787fea2
15 changed files with 314 additions and 6 deletions

View File

@@ -0,0 +1,30 @@
[package]
name = "serai-coordinator-tests"
version = "0.1.0"
description = "Tests for Serai's Coordinator"
license = "AGPL-3.0-only"
repository = "https://github.com/serai-dex/serai/tree/develop/tests/coordinator"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
publish = false
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
hex = "0.4"
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["ristretto"] }
messages = { package = "serai-processor-messages", path = "../../processor/messages" }
serai-client = { path = "../../substrate/client" }
serai-message-queue = { path = "../../message-queue" }
tokio = { version = "1", features = ["time"] }
dockertest = "0.3"
serai-docker-tests = { path = "../docker" }
serai-message-queue-tests = { path = "../message-queue" }

15
tests/coordinator/LICENSE Normal file
View File

@@ -0,0 +1,15 @@
AGPL-3.0-only license
Copyright (c) 2023 Luke Parker
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License Version 3 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@@ -0,0 +1,95 @@
#![allow(clippy::needless_pass_by_ref_mut)] // False positives
use std::sync::{OnceLock, Mutex};
use ciphersuite::{group::ff::PrimeField, Ciphersuite, Ristretto};
use serai_client::primitives::NetworkId;
use dockertest::{PullPolicy, Image, LogAction, LogPolicy, LogSource, LogOptions, StartPolicy, Composition};
#[cfg(test)]
mod tests;
static UNIQUE_ID: OnceLock<Mutex<u16>> = OnceLock::new();
pub fn coordinator_instance(message_queue_key: <Ristretto as Ciphersuite>::F) -> Composition {
serai_docker_tests::build("coordinator".to_string());
Composition::with_image(
Image::with_repository("serai-dev-coordinator").pull_policy(PullPolicy::Never),
)
.with_env(
[
("MESSAGE_QUEUE_KEY".to_string(), hex::encode(message_queue_key.to_repr())),
("DB_PATH".to_string(), "./coordinator-db".to_string()),
]
.into(),
)
}
pub fn serai_composition(name: &str) -> Composition {
serai_docker_tests::build("serai".to_string());
Composition::with_image(Image::with_repository("serai-dev-serai").pull_policy(PullPolicy::Never))
.with_cmd(vec![
"serai-node".to_string(),
"--unsafe-rpc-external".to_string(),
"--rpc-cors".to_string(),
"all".to_string(),
"--chain".to_string(),
"devnet".to_string(),
format!("--{name}"),
])
}
pub type Handles = (String, String, String);
pub fn coordinator_stack(name: &str) -> (Handles, <Ristretto as Ciphersuite>::F, Vec<Composition>) {
let serai_composition = serai_composition(name);
let (coord_key, message_queue_keys, message_queue_composition) =
serai_message_queue_tests::instance();
let coordinator_composition = coordinator_instance(message_queue_keys[&NetworkId::Bitcoin]);
// Give every item in this stack a unique ID
// Uses a Mutex as we can't generate a 8-byte random ID without hitting hostname length limits
let unique_id = {
let unique_id_mutex = UNIQUE_ID.get_or_init(|| Mutex::new(0));
let mut unique_id_lock = unique_id_mutex.lock().unwrap();
let unique_id = hex::encode(unique_id_lock.to_be_bytes());
*unique_id_lock += 1;
unique_id
};
let mut compositions = vec![];
let mut handles = vec![];
for composition in [serai_composition, message_queue_composition, coordinator_composition] {
let handle = composition.handle();
compositions.push(
composition
.with_start_policy(StartPolicy::Strict)
.with_container_name(format!("{handle}-{}", &unique_id))
.with_log_options(Some(LogOptions {
action: LogAction::Forward,
policy: if handle.contains("coordinator") {
LogPolicy::Always
} else {
LogPolicy::OnError
},
source: LogSource::Both,
})),
);
handles.push(compositions.last().unwrap().handle());
}
let coordinator_composition = compositions.last_mut().unwrap();
coordinator_composition.inject_container_name(handles.remove(0), "SERAI_HOSTNAME");
coordinator_composition.inject_container_name(handles.remove(0), "MESSAGE_QUEUE_RPC");
(
(compositions[0].handle(), compositions[1].handle(), compositions[2].handle()),
coord_key,
compositions,
)
}

View File

@@ -0,0 +1,41 @@
use std::time::Duration;
use ciphersuite::{Ciphersuite, Ristretto};
use dockertest::DockerTest;
use crate::*;
pub(crate) const COORDINATORS: usize = 4;
// pub(crate) const THRESHOLD: usize = ((COORDINATORS * 2) / 3) + 1;
fn new_test() -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)>, DockerTest) {
let mut coordinators = vec![];
let mut test = DockerTest::new();
for i in 0 .. COORDINATORS {
let (handles, coord_key, compositions) = coordinator_stack(match i {
0 => "alice",
1 => "bob",
2 => "charlie",
3 => "dave",
4 => "eve",
5 => "ferdie",
_ => panic!("needed a 6th name for a serai node"),
});
coordinators.push((handles, coord_key));
for composition in compositions {
test.add_composition(composition);
}
}
(coordinators, test)
}
#[test]
fn stack_test() {
let (_coordinators, test) = new_test();
test.run(|_ops| async move {
tokio::time::sleep(Duration::from_secs(30)).await;
});
}

View File

@@ -79,11 +79,24 @@ pub fn build(name: String) {
meta(repo_path.join("message-queue")),
meta(repo_path.join("processor")),
],
"coordinator" => vec![
meta(repo_path.join("common")),
meta(repo_path.join("crypto")),
meta(repo_path.join("coins")),
meta(repo_path.join("substrate")),
meta(repo_path.join("message-queue")),
meta(repo_path.join("coordinator")),
],
"runtime" => vec![
meta(repo_path.join("common")),
meta(repo_path.join("crypto")),
meta(repo_path.join("substrate")),
],
"serai" => vec![
meta(repo_path.join("common")),
meta(repo_path.join("crypto")),
meta(repo_path.join("substrate")),
],
_ => panic!("building unrecognized docker image"),
};