Skip to content

Commit

Permalink
update emulator tests for new testcontainers version
Browse files Browse the repository at this point in the history
  • Loading branch information
elizabethengelman committed Sep 12, 2024
1 parent 4f7f673 commit 04ddc4f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 102 deletions.
87 changes: 24 additions & 63 deletions cmd/crates/stellar-ledger/tests/test/emulator_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use std::{collections::HashMap, time::Duration};
use stellar_xdr::curr::{
Memo, MuxedAccount, PaymentOp, Preconditions, SequenceNumber, TransactionExt,
};

use testcontainers::{clients, core::Port, RunnableImage};
use testcontainers::{core::ContainerPort, runners::AsyncRunner, ContainerAsync, ImageExt};
use tokio::time::sleep;

static PORT_RANGE: Lazy<Mutex<Range<u16>>> = Lazy::new(|| Mutex::new(40000..50000));
Expand All @@ -40,21 +39,16 @@ mod test_helpers {
}

use test_case::test_case;
use test_helpers::test::{
emulator_http_transport::EmulatorHttpTransport,
speculos::{Args, Speculos},
};
use test_helpers::test::{emulator_http_transport::EmulatorHttpTransport, speculos::Speculos};

#[test_case("nanos".to_string() ; "when the device is NanoS")]
#[test_case("nanox".to_string() ; "when the device is NanoX")]
#[test_case("nanosp".to_string() ; "when the device is NanoS Plus")]
#[tokio::test]
async fn test_get_public_key(ledger_device_model: String) {
let runnable_image = get_runnable_image(ledger_device_model.clone());
let docker = clients::Cli::default();
let node = docker.run(runnable_image);
let host_port = node.get_host_port_ipv4(9998);
let ui_host_port: u16 = node.get_host_port_ipv4(5000);
let container = get_container(ledger_device_model.clone()).await;
let host_port = container.get_host_port_ipv4(9998).await.unwrap();
let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap();
wait_for_emulator_start_text(ui_host_port).await;

let ledger = ledger(host_port).await;
Expand All @@ -68,25 +62,20 @@ async fn test_get_public_key(ledger_device_model: String) {
assert_eq!(public_key_string, expected_public_key);
}
Err(e) => {
node.stop();
println!("{e}");
assert!(false);
}
}

node.stop();
}

#[test_case("nanos".to_string() ; "when the device is NanoS")]
#[test_case("nanox".to_string() ; "when the device is NanoX")]
#[test_case("nanosp".to_string() ; "when the device is NanoS Plus")]
#[tokio::test]
async fn test_get_app_configuration(ledger_device_model: String) {
let runnable_image = get_runnable_image(ledger_device_model.clone());
let docker = clients::Cli::default();
let node = docker.run(runnable_image);
let host_port = node.get_host_port_ipv4(9998);
let ui_host_port: u16 = node.get_host_port_ipv4(5000);
let container = get_container(ledger_device_model.clone()).await;
let host_port = container.get_host_port_ipv4(9998).await.unwrap();
let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap();
wait_for_emulator_start_text(ui_host_port).await;

let ledger = ledger(host_port).await;
Expand All @@ -96,25 +85,20 @@ async fn test_get_app_configuration(ledger_device_model: String) {
assert_eq!(config, vec![0, 5, 0, 3]);
}
Err(e) => {
node.stop();
println!("{e}");
assert!(false);
}
};

node.stop();
}

#[test_case("nanos".to_string() ; "when the device is NanoS")]
#[test_case("nanox".to_string() ; "when the device is NanoX")]
#[test_case("nanosp".to_string() ; "when the device is NanoS Plus")]
#[tokio::test]
async fn test_sign_tx(ledger_device_model: String) {
let runnable_image = get_runnable_image(ledger_device_model.clone());
let docker = clients::Cli::default();
let node = docker.run(runnable_image);
let host_port = node.get_host_port_ipv4(9998);
let ui_host_port: u16 = node.get_host_port_ipv4(5000);
let container = get_container(ledger_device_model.clone()).await;
let host_port = container.get_host_port_ipv4(9998).await.unwrap();
let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap();
wait_for_emulator_start_text(ui_host_port).await;

let ledger = Arc::new(ledger(host_port).await);
Expand Down Expand Up @@ -185,25 +169,20 @@ async fn test_sign_tx(ledger_device_model: String) {
assert_eq!( hex::encode(response), "5c2f8eb41e11ab922800071990a25cf9713cc6e7c43e50e0780ddc4c0c6da50c784609ef14c528a12f520d8ea9343b49083f59c51e3f28af8c62b3edeaade60e");
}
Err(e) => {
node.stop();
println!("{e}");
assert!(false);
}
};

node.stop();
}

#[test_case("nanos".to_string() ; "when the device is NanoS")]
#[test_case("nanox".to_string() ; "when the device is NanoX")]
#[test_case("nanosp".to_string() ; "when the device is NanoS Plus")]
#[tokio::test]
async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: String) {
let runnable_image = get_runnable_image(ledger_device_model.clone());
let docker = clients::Cli::default();
let node = docker.run(runnable_image);
let host_port = node.get_host_port_ipv4(9998);
let ui_host_port: u16 = node.get_host_port_ipv4(5000);
let container = get_container(ledger_device_model.clone()).await;
let host_port = container.get_host_port_ipv4(9998).await.unwrap();
let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap();
wait_for_emulator_start_text(ui_host_port).await;

let ledger = ledger(host_port).await;
Expand All @@ -216,23 +195,18 @@ async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model:
assert_eq!(msg, "Ledger APDU retcode: 0x6C66");
// this error code is SW_TX_HASH_SIGNING_MODE_NOT_ENABLED https://github.com/LedgerHQ/app-stellar/blob/develop/docs/COMMANDS.md
} else {
node.stop();
panic!("Unexpected result: {:?}", result);
}

node.stop();
}

#[test_case("nanos".to_string() ; "when the device is NanoS")]
#[test_case("nanox".to_string() ; "when the device is NanoX")]
#[test_case("nanosp".to_string() ; "when the device is NanoS Plus")]
#[tokio::test]
async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: String) {
let runnable_image = get_runnable_image(ledger_device_model.clone());
let docker = clients::Cli::default();
let node = docker.run(runnable_image);
let host_port = node.get_host_port_ipv4(9998);
let ui_host_port: u16 = node.get_host_port_ipv4(5000);
let container = get_container(ledger_device_model.clone()).await;
let host_port = container.get_host_port_ipv4(9998).await.unwrap();
let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap();

wait_for_emulator_start_text(ui_host_port).await;
enable_hash_signing(ui_host_port).await;
Expand All @@ -248,7 +222,6 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str
) {
Ok(()) => {}
Err(e) => {
node.stop();
panic!("Unexpected result: {e}");
}
}
Expand All @@ -267,12 +240,9 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str
assert_eq!( hex::encode(response), "e0fa9d19f34ddd494bbb794645fc82eb5ebab29e74160f1b1d5697e749aada7c6b367236df87326b0fdc921ed39702242fc8b14414f4e0ee3e775f1fd0208101");
}
Err(e) => {
node.stop();
panic!("Unexpected result: {e}");
}
}

node.stop();
}

async fn click(ui_host_port: u16, url: &str) {
Expand Down Expand Up @@ -330,23 +300,14 @@ struct EventsResponse {
events: Vec<EmulatorEvent>,
}

fn get_runnable_image(ledger_device_model: String) -> RunnableImage<Speculos> {
let args = Args {
ledger_device_model,
};
let runnable_image: RunnableImage<Speculos> = (Speculos::new(), args).into();

// doing this to randomize the ports on the host so that parallel tests don't clobber each other
async fn get_container(ledger_device_model: String) -> ContainerAsync<Speculos> {
let (tcp_port_1, tcp_port_2) = get_available_ports(2);
runnable_image
.with_mapped_port(Port {
local: tcp_port_1,
internal: 9998,
})
.with_mapped_port(Port {
local: tcp_port_2,
internal: 5000,
})
Speculos::new(ledger_device_model)
.with_mapped_port(tcp_port_1, ContainerPort::Tcp(9998))
.with_mapped_port(tcp_port_2, ContainerPort::Tcp(5000))
.start()
.await
.unwrap()
}

fn get_available_ports(n: usize) -> (u16, u16) {
Expand Down
78 changes: 39 additions & 39 deletions cmd/crates/stellar-ledger/tests/utils/speculos.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::{collections::HashMap, path::PathBuf};
use testcontainers::{core::WaitFor, Image, ImageArgs};
use std::{borrow::Cow, collections::HashMap, path::PathBuf};
use testcontainers::{
core::{Mount, WaitFor},
Image,
};

const NAME: &str = "docker.io/zondax/builder-zemu";
const TAG: &str = "speculos-3a3439f6b45eca7f56395673caaf434c202e7005";
Expand All @@ -24,72 +27,69 @@ impl From<&Map> for HashMap<String, String> {
}

#[derive(Debug, Default)]
pub struct Speculos(HashMap<String, String>, HashMap<String, String>);
pub struct Speculos {
env: HashMap<String, String>,
volumes: Vec<Mount>,
cmd: String,
}

const DEFAULT_APP_PATH: &str = "/project/app/bin";
impl Speculos {
#[allow(dead_code)]
pub fn new() -> Self {
pub fn new(ledger_device_model: String) -> Self {
#[allow(unused_mut)]
let apps_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("test_fixtures")
.join("apps");
let mut volumes = HashMap::new();
volumes.insert(
apps_dir.to_str().unwrap().to_string(),
DEFAULT_APP_PATH.to_string(),
);
Speculos(ENV.into(), volumes)
}
}

#[derive(Debug, Clone)]
pub struct Args {
pub ledger_device_model: String,
}

impl Default for Args {
fn default() -> Self {
Self {
ledger_device_model: "nanos".to_string(),
let volumes = vec![Mount::bind_mount(
apps_dir.to_str().unwrap(),
DEFAULT_APP_PATH,
)];
let cmd = Self::get_cmd(ledger_device_model);
Speculos {
env: ENV.into(),
volumes,
cmd,
}
}
}

impl ImageArgs for Args {
fn into_iterator(self) -> Box<dyn Iterator<Item = String>> {
let device_model = self.ledger_device_model.clone();
fn get_cmd(ledger_device_model: String) -> String {
let device_model = ledger_device_model.clone();
let container_elf_path = match device_model.as_str() {
"nanos" => format!("{DEFAULT_APP_PATH}/stellarNanoSApp.elf"),
"nanosp" => format!("{DEFAULT_APP_PATH}/stellarNanoSPApp.elf"),
"nanox" => format!("{DEFAULT_APP_PATH}/stellarNanoXApp.elf"),
_ => panic!("Unsupported device model"),
};
let command_string = format!("/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN --display headless -s {TEST_SEED_PHRASE} -m {device_model} {container_elf_path}");
Box::new(vec![command_string].into_iter())
format!("/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN --display headless -s {TEST_SEED_PHRASE} -m {device_model} {container_elf_path}")
}
}

impl Image for Speculos {
type Args = Args;

fn name(&self) -> String {
NAME.to_owned()
fn name(&self) -> &str {
NAME
}

fn tag(&self) -> String {
TAG.to_owned()
fn tag(&self) -> &str {
TAG
}

fn ready_conditions(&self) -> Vec<WaitFor> {
vec![WaitFor::message_on_stdout("HTTP proxy started...")]
}

fn env_vars(&self) -> Box<dyn Iterator<Item = (&String, &String)> + '_> {
Box::new(self.0.iter())
fn env_vars(
&self,
) -> impl IntoIterator<Item = (impl Into<Cow<'_, str>>, impl Into<Cow<'_, str>>)> {
self.env.clone().into_iter().collect::<Vec<_>>()
}

fn volumes(&self) -> Box<dyn Iterator<Item = (&String, &String)> + '_> {
Box::new(self.1.iter())
fn mounts(&self) -> impl IntoIterator<Item = &Mount> {
self.volumes.iter()
}
}

fn cmd(&self) -> impl IntoIterator<Item = impl Into<std::borrow::Cow<'_, str>>> {
vec![self.cmd.clone()].into_iter()
}
}

0 comments on commit 04ddc4f

Please sign in to comment.