mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Add initial coordinator e2e tests
This commit is contained in:
30
tests/coordinator/Cargo.toml
Normal file
30
tests/coordinator/Cargo.toml
Normal 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
15
tests/coordinator/LICENSE
Normal 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/>.
|
||||
95
tests/coordinator/src/lib.rs
Normal file
95
tests/coordinator/src/lib.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
41
tests/coordinator/src/tests/mod.rs
Normal file
41
tests/coordinator/src/tests/mod.rs
Normal 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;
|
||||
});
|
||||
}
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user