Skip to content

Commit

Permalink
add verification of customer status in tests #69
Browse files Browse the repository at this point in the history
  • Loading branch information
marsella committed Feb 1, 2022
1 parent 9738623 commit 6735120
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 23 deletions.
54 changes: 40 additions & 14 deletions integration_tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,45 +36,45 @@ pub enum Party {
MerchantServer,
CustomerWatcher,
/// The process corresponding to the `Operation` executed by the test harness
ActiveOperation,
ActiveOperation(&'static str),
}

#[allow(unused)]
impl Party {
const fn to_string(&self) -> &str {
const fn to_str(&self) -> &str {
match self {
Party::MerchantServer => "merchant server",
Party::CustomerWatcher => "customer watcher",
Party::ActiveOperation => "active operation",
Party::ActiveOperation(description) => description,
}
}
}

// Form a customer CLI request. These cannot be constructed directly because the CLI types are
// non-exhaustive.
macro_rules! parse_customer_cli {
macro_rules! customer_cli {
($cli:ident, $args:expr) => {
match ::zeekoe::customer::cli::Customer::from_iter($args) {
::zeekoe::customer::cli::Customer::$cli(result) => result,
_ => panic!("Failed to parse customer CLI"),
}
};
}
pub(crate) use parse_customer_cli;
pub(crate) use customer_cli;

pub async fn setup(rng: &StdRng) -> ServerFuture {
// delete existing data from previous runs
let _ = fs::remove_dir_all("integration_tests/gen/");
let _ = fs::create_dir("integration_tests/gen");

// ...copy keys from dev/ directory to here
// TODO: call the script instead
let _ = fs::copy("dev/localhost.crt", "integration_tests/gen/localhost.crt");
let _ = fs::copy("dev/localhost.key", "integration_tests/gen/localhost.key");

// write config options for each party
let customer_config = customer_test_config().await;
let merchant_config = merchant_test_config().await;

// set up tracing for all of our own log messages
tracing_subscriber::fmt()
.with_writer(Mutex::new(
File::create(ERROR_FILENAME).expect("Failed to open log file"),
Expand All @@ -90,15 +90,15 @@ pub async fn setup(rng: &StdRng) -> ServerFuture {
// Stand-in task for the merchant server
let merchant_handle = tokio::spawn(
run.run(merchant_config)
.instrument(info_span!(Party::MerchantServer.to_string())),
.instrument(info_span!(Party::MerchantServer.to_str())),
);

let watch = parse_customer_cli!(Watch, vec!["./target/debug/zkchannel-customer", "watch"]);
let watch = customer_cli!(Watch, vec!["./target/debug/zkchannel-customer", "watch"]);

let customer_handle = tokio::spawn(
watch
.run(rng.clone(), customer_config)
.instrument(info_span!(Party::CustomerWatcher.to_string())),
.instrument(info_span!(Party::CustomerWatcher.to_str())),
);

future::join(customer_handle, merchant_handle)
Expand All @@ -109,6 +109,9 @@ pub async fn teardown(server_future: ServerFuture) {
let _result = server_future
.with_timeout(tokio::time::Duration::new(1, 0))
.await;

// delete data from this run
let _ = fs::remove_dir_all("integration_tests/gen/");
}

/// Encode the customizable fields of the zeekoe customer Config struct for testing.
Expand Down Expand Up @@ -195,17 +198,40 @@ pub enum LogError {
ReadFailed(std::io::Error),
}

#[allow(unused)]
pub enum LogType {
Info,
Error,
Warn,
}

#[allow(unused)]
impl LogType {
pub fn to_str(&self) -> &str {
match self {
LogType::Info => "INFO",
LogType::Error => "ERROR",
LogType::Warn => "WARN",
}
}
}

/// Get any errors from the log file.
///
/// Current behavior: outputs the entire log
/// Ideal behavior: pass a Party, maybe an indicator of which test / channel name we want. Return
/// only the lines relevant to that setting.
#[allow(unused)]
pub fn get_error() -> Result<String, LogError> {
pub fn get_logs(log_type: LogType, party: Party) -> Result<String, LogError> {
let mut file = File::open(ERROR_FILENAME).map_err(LogError::OpenFailed)?;
let mut error = String::new();
file.read_to_string(&mut error)
let mut logs = String::new();
file.read_to_string(&mut logs)
.map_err(LogError::ReadFailed)?;

Ok(error)
Ok(logs
.lines()
.filter(|s| s.contains("zeekoe::"))
.filter(|s| s.contains(log_type.to_str()))
.filter(|s| s.contains(party.to_str()))
.fold("".to_string(), |acc, s| format!("{}{}\n", acc, s)))
}
56 changes: 47 additions & 9 deletions integration_tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ pub(crate) mod common;

use rand::SeedableRng;
use zeekoe::{
customer::{self, database::StateName as CustomerStatus, zkchannels::Command},
customer::{
self,
database::StateName as CustomerStatus,
zkchannels::{Command, PublicChannelDetails},
},
protocol::ChannelStatus as MerchantStatus,
};
use {
common::{parse_customer_cli, Party},
common::{customer_cli, Party},
rand::prelude::StdRng,
std::panic,
structopt::StructOpt,
tracing::{info_span, Instrument},
};

#[tokio::main]
Expand All @@ -30,8 +35,8 @@ pub async fn main() {
for test in tests {
println!("Now running: {}", test.name);
let result = test.execute(&rng, &customer_config).await;
if result.is_err() {
eprintln!("Test failed: {}\n", test.name)
if let Err(error) = &result {
eprintln!("Test failed: {}\n{}", test.name, error)
}
results.push(result);
}
Expand All @@ -43,7 +48,11 @@ pub async fn main() {
common::teardown(server_future).await;
}

/// Get a list of tests to execute.
/// Assumption: none of these will cause a fatal error to the long-running processes (merchant
/// server or customer watcher).
fn tests() -> Vec<Test> {
/*
vec![Test {
name: "Channel establishes correctly".to_string(),
operations: vec![(
Expand All @@ -55,19 +64,29 @@ fn tests() -> Vec<Test> {
},
)],
}]
*/
vec![Test {
name: "Channel establishes correctly".to_string(),
operations: vec![(
Operation::NoOp, // this is a testing-the-tests hack to skip waiting for establish
Outcome {
customer_status: CustomerStatus::Ready,
merchant_status: MerchantStatus::Active,
error: None,
},
)],
}]
}

impl Test {
async fn execute(&self, rng: &StdRng, config: &customer::Config) -> Result<(), String> {
for (op, _expected_outcome) in &self.operations {
for (op, expected_outcome) in &self.operations {
// Clone inputs. A future refactor should look into having the `Command` trait take
// these by reference instead.
let config = config.clone();
let rng = rng.clone();

let outcome = match op {
Operation::Establish => {
let est = parse_customer_cli!(
let est = customer_cli!(
Establish,
vec![
"./target/debug/zkchannel-customer",
Expand All @@ -79,14 +98,32 @@ impl Test {
"5 XTZ"
]
);
est.run(rng, config)
est.run(rng.clone(), config.clone())
}
Operation::NoOp => Box::pin(async { Ok(()) }),
_ => return Err("Operation not implemented yet".to_string()),
}
.await;

// Check customer status
let show = customer_cli!(
Show,
vec!["zkchannel-customer", "show", &self.name, "--json"]
);
let channel_detail_json = show
.run(rng.clone(), config.clone())
.instrument(info_span!("customer show"))
.await
.map_err(|e| e.to_string())?;

let channel_details: PublicChannelDetails =
serde_json::from_str(&channel_detail_json).map_err(|err| format!("{}", err))?;

assert_eq!(channel_details.status(), expected_outcome.customer_status);

// TODO: Compare outcome + error log to expected outcome
eprintln!("op outcome: {:?}", outcome);

//eprintln!("log output: {}", common::get_error().map_err(|e| format!("{:?}", e))?)

// TODO: Call list to check status of each party
Expand All @@ -111,6 +148,7 @@ enum Operation {
MerchantExpiry,
Store,
Restore,
NoOp,
}

#[derive(PartialEq)]
Expand Down
26 changes: 26 additions & 0 deletions src/zkchannels/customer/manage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,32 @@ impl From<ChannelDetails> for PublicChannelDetails {
}
}

impl PublicChannelDetails {
pub fn label(&self) -> &ChannelName {
&self.label
}

pub fn status(&self) -> StateName {
self.state
}

pub fn customer_balance(&self) -> CustomerBalance {
self.customer_balance
}

pub fn merchant_balance(&self) -> MerchantBalance {
self.merchant_balance
}

pub fn channel_id(&self) -> ChannelId {
self.channel_id
}

pub fn contract_id(&self) -> Option<&ContractId> {
self.contract_id.as_ref()
}
}

#[async_trait]
impl Command for Show {
type Output = String;
Expand Down

0 comments on commit 6735120

Please sign in to comment.