Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests(docker): port docker tests to sia-rust #8

Open
wants to merge 1 commit into
base: release_candidate_1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ http = "0.2.12"
async-trait = "0.1.76"
thiserror = "1.0.40"
percent-encoding = "2.1.0"
[dev-dependencies]
once_cell = "1.18.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.9", features = ["js"] }
Expand All @@ -40,6 +38,11 @@ wasm-bindgen-futures = "0.4.21"
web-sys = { version = "0.3.55", features = ["Request", "RequestInit", "RequestMode", "Window"] }
# web-sys = { version = "0.3.55", features = ["console", "Headers", "Request", "RequestInit", "RequestMode", "Response", "Window"] }

[dev-dependencies]
once_cell = "1.18.0"
testcontainers = "0.15.0"
lazy_static = "1.4"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = { version = "0.3.2" }

Expand Down
83 changes: 83 additions & 0 deletions tests/docker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#![feature(test)]
#![feature(custom_test_frameworks)]
#![test_runner(docker_tests_runner)]

#[path = "docker/mod.rs"] mod docker;

#[macro_use] extern crate lazy_static;
extern crate test;

use docker::utils::{SIA_DOCKER_IMAGE, SIA_DOCKER_IMAGE_WITH_TAG, SIA_WALLATD_RPC_PORT};

use std::env;
use std::io::{BufRead, BufReader};
use std::process::Command;
use test::{test_main, StaticBenchFn, StaticTestFn, TestDescAndFn};
use testcontainers::clients::Cli;
use testcontainers::{Container, GenericImage, RunnableImage};

/// Custom test runner intended to initialize the SIA coin daemon in a Docker container.
pub fn docker_tests_runner(tests: &[&TestDescAndFn]) {
let docker = Cli::default();

pull_docker_image(SIA_DOCKER_IMAGE_WITH_TAG);
remove_docker_containers(SIA_DOCKER_IMAGE_WITH_TAG);
let _sia_node = sia_docker_node(&docker, SIA_WALLATD_RPC_PORT);

let owned_tests: Vec<_> = tests
.iter()
.map(|t| match t.testfn {
StaticTestFn(f) => TestDescAndFn {
testfn: StaticTestFn(f),
desc: t.desc.clone(),
},
StaticBenchFn(f) => TestDescAndFn {
testfn: StaticBenchFn(f),
desc: t.desc.clone(),
},
_ => panic!("non-static tests passed to the test runner"),
})
.collect();

let args: Vec<_> = env::args().collect();
test_main(&args, owned_tests, None);
}

fn pull_docker_image(name: &str) {
Command::new("docker")
.arg("pull")
.arg(name)
.status()
.expect("Failed to execute docker command");
}

fn remove_docker_containers(name: &str) {
let stdout = Command::new("docker")
.arg("ps")
.arg("-f")
.arg(format!("ancestor={}", name))
.arg("-q")
.output()
.expect("Failed to execute docker command");

let reader = BufReader::new(stdout.stdout.as_slice());
let ids: Vec<_> = reader.lines().map(|line| line.unwrap()).collect();
if !ids.is_empty() {
Command::new("docker")
.arg("rm")
.arg("-f")
.args(ids)
.status()
.expect("Failed to execute docker command");
}
}

fn sia_docker_node(docker: &Cli, port: u16) -> Container<'_, GenericImage> {
let image =
GenericImage::new(SIA_DOCKER_IMAGE, "latest").with_env_var("WALLETD_API_PASSWORD", "password".to_string());
let args = vec![];
let image = RunnableImage::from((image, args))
.with_mapped_port((port, port))
.with_container_name("sia-docker");
docker.run(image)
}
2 changes: 2 additions & 0 deletions tests/docker/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod tests;
pub mod utils;
113 changes: 113 additions & 0 deletions tests/docker/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use super::utils::{block_on, mine_blocks, SIA_WALLETD_RPC_URL};

use http::StatusCode;
use sia_rust::transport::client::native::{Conf, NativeClient};
use sia_rust::transport::client::{ApiClient, ApiClientError};
use sia_rust::transport::endpoints::{AddressBalanceRequest, ConsensusTipRequest, GetAddressUtxosRequest,
TxpoolBroadcastRequest};
use sia_rust::types::{Address, Currency, Keypair, SiacoinOutput, SpendPolicy, V2TransactionBuilder};
use std::str::FromStr;
use url::Url;

#[test]
fn test_sia_new_client() {
let conf = Conf {
server_url: Url::parse(SIA_WALLETD_RPC_URL).unwrap(),
password: Some("password".to_string()),
timeout: None,
};
let _api_client = block_on(NativeClient::new(conf)).unwrap();
}

#[test]
fn test_sia_client_bad_auth() {
let conf = Conf {
server_url: Url::parse(SIA_WALLETD_RPC_URL).unwrap(),
password: Some("foo".to_string()),
timeout: None,
};
let result = block_on(NativeClient::new(conf));
assert!(matches!(
result,
Err(ApiClientError::UnexpectedHttpStatus {
status: StatusCode::UNAUTHORIZED,
..
})
));
}

#[test]
fn test_sia_client_consensus_tip() {
let conf = Conf {
server_url: Url::parse(SIA_WALLETD_RPC_URL).unwrap(),
password: Some("password".to_string()),
timeout: None,
};
let api_client = block_on(NativeClient::new(conf)).unwrap();
let _response = block_on(api_client.dispatcher(ConsensusTipRequest)).unwrap();
}

// This test likely needs to be removed because mine_blocks has possibility of interfering with other async tests
// related to block height
#[test]
fn test_sia_client_address_balance() {
let conf = Conf {
server_url: Url::parse(SIA_WALLETD_RPC_URL).unwrap(),
password: Some("password".to_string()),
timeout: None,
};
let api_client = block_on(NativeClient::new(conf)).unwrap();

let address =
Address::from_str("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f").unwrap();
mine_blocks(10, &address);

let request = AddressBalanceRequest { address };
let response = block_on(api_client.dispatcher(request)).unwrap();

assert_eq!(response.siacoins, Currency(1000000000000000000000000000000000000));
}

#[test]
fn test_sia_client_build_tx() {
let conf = Conf {
server_url: Url::parse(SIA_WALLETD_RPC_URL).unwrap(),
password: Some("password".to_string()),
timeout: None,
};
let api_client = block_on(NativeClient::new(conf)).unwrap();
let keypair = Keypair::from_private_bytes(
&hex::decode("0100000000000000000000000000000000000000000000000000000000000000").unwrap(),
)
.unwrap();
let spend_policy = SpendPolicy::PublicKey(keypair.public());

let address = spend_policy.address();

mine_blocks(201, &address);

let utxos = block_on(api_client.dispatcher(GetAddressUtxosRequest {
address: address.clone(),
limit: None,
offset: None,
}))
.unwrap();
let spend_this = utxos[0].clone();
let vin = spend_this.clone();
println!("utxo[0]: {:?}", spend_this);
let vout = SiacoinOutput {
value: spend_this.siacoin_output.value,
address,
};
let tx = V2TransactionBuilder::new()
.add_siacoin_input(vin, spend_policy)
.add_siacoin_output(vout)
.sign_simple(vec![&keypair])
.build();

let req = TxpoolBroadcastRequest {
transactions: vec![],
v2transactions: vec![tx],
};
let _response = block_on(api_client.dispatcher(req)).unwrap();
}
41 changes: 41 additions & 0 deletions tests/docker/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use sia_rust::types::Address;
use std::future::Future;
use std::process::Command;

pub const SIA_DOCKER_IMAGE: &str = "docker.io/alrighttt/walletd-komodo";
pub const SIA_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/alrighttt/walletd-komodo:latest";
pub const SIA_WALLATD_RPC_PORT: u16 = 9980;
pub const SIA_WALLETD_RPC_URL: &str = "http://localhost:9980/";

pub fn mine_blocks(n: u64, addr: &Address) {
Command::new("docker")
.arg("exec")
.arg("sia-docker")
.arg("walletd")
.arg("mine")
.arg(format!("-addr={}", addr))
.arg(format!("-n={}", n))
.status()
.expect("Failed to execute docker command");
}

pub fn block_on<F>(fut: F) -> F::Output
where
F: Future,
{
#[cfg(not(target = "wasm32"))]
{
lazy_static! {
pub static ref RUNTIME: tokio::runtime::Runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
}
RUNTIME.block_on(fut)
}
// Not actually needed since we don't run end-to-end tests for wasm.
// TODO: Generalize over the construction of platform-specific objects and use
// #[wasm_bindgen_test(unsupported = test)] macro to test both wasm and non-wasm targets
#[cfg(target = "wasm32")]
futures::executor::block_on(fut)
}