-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from informalsystems/gabriela/snapshot-tests
Add snapshot tests for CTF contracts
- Loading branch information
Showing
15 changed files
with
1,872 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "tests/fixtures/cosmwasm-ctf"] | ||
path = tests/fixtures/cosmwasm-ctf | ||
url = git@github.com:bugarela/cosmwasm-ctf.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule cosmwasm-ctf
added at
2dfdb8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use std::{env, fs, path::Path, process::Command, sync::Once}; | ||
|
||
use anyhow::{ensure, Context, Result}; | ||
|
||
static SETUP: Once = Once::new(); | ||
|
||
fn run(dir: &str, f: impl FnOnce(&mut Command)) -> Result<String> { | ||
let root = env::temp_dir().join("cosmwasm-to-quint"); | ||
|
||
let heredir = Path::new(".").canonicalize()?; | ||
|
||
SETUP.call_once(|| { | ||
let mut cmd = Command::new("cargo"); | ||
cmd.args(["install", "--path", ".", "--debug", "--locked", "--root"]); | ||
cmd.arg(&root); | ||
cmd.current_dir(&heredir); | ||
let status = cmd.status().unwrap(); | ||
if !status.success() { | ||
panic!("installing cosmwasm-to-quint failed") | ||
} | ||
}); | ||
|
||
let mut cmd = Command::new("cargo"); | ||
cmd.arg("cosmwasm-to-quint"); | ||
|
||
let path = format!( | ||
"{}:{}", | ||
root.join("bin").display(), | ||
env::var("PATH").unwrap_or_else(|_| "".into()) | ||
); | ||
cmd.env("PATH", path); | ||
|
||
let ws = heredir.join("tests").join("fixtures").join(dir); | ||
cmd.current_dir(&ws); | ||
|
||
f(&mut cmd); | ||
|
||
let _ = fs::remove_dir_all(ws.join("target")); | ||
|
||
let output = cmd.output().context("Process failed AA")?; | ||
ensure!( | ||
output.status.success(), | ||
"Process exited with non-zero exit code. Stderr:\n{}", | ||
String::from_utf8(output.stderr)? | ||
); | ||
|
||
Ok(String::from_utf8(output.stdout)?) | ||
} | ||
|
||
// TODO: why do these tests need to be run sequentially? | ||
|
||
#[test] | ||
fn ctf01() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-01", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf02() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-02", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf03() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-03", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf04() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-04", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf05() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-05", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf06() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-06", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf07() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-07", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf08() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-08", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf09() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-09", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn ctf10() -> Result<()> { | ||
let output = run("cosmwasm-ctf/ctf-10", |_cmd| {})?; | ||
insta::assert_snapshot!(output); | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
--- | ||
source: tests/integration_tests.rs | ||
expression: output | ||
--- | ||
module oaksecurity_cosmwasm_ctf_01 { | ||
|
||
import basicSpells.* from "../lib/basicSpells" | ||
import cw_types.* from "../lib/cw_types" | ||
import messaging.* from "../lib/messaging" | ||
import bank from "../lib/bank" | ||
|
||
|
||
var contract_state: ContractState | ||
var return: Result | ||
var bank: bank::Bank | ||
var time: int | ||
|
||
|
||
pure val CONTRACT_ADDRESS = "<contract>" | ||
|
||
|
||
pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) | ||
pure val DENOMS = Set("d1", "uawesome") | ||
pure val MAX_AMOUNT = 200 | ||
|
||
|
||
type InstantiateMsg = { count: int } | ||
type Lockup = { id: int, owner: Addr, amount: int, release_timestamp: int } | ||
type ExecuteMsg = | ||
| ExecuteMsg_Deposit | ||
| ExecuteMsg_Withdraw({ ids: List[int] }) | ||
pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) | ||
pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ||
| ExecuteMsg_Deposit(__r) => deposit(state, env, info) | ||
| ExecuteMsg_Withdraw(__r) => withdraw(state, env, info, __r.ids) | ||
} | ||
pure def deposit(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) | ||
|
||
action deposit_action = { | ||
// TODO: Change next line according to fund expectations | ||
pure val max_funds = MAX_AMOUNT | ||
nondet message: ExecuteMsg = ExecuteMsg_Deposit.oneOf() | ||
execute_message(message, max_funds) | ||
} | ||
pure def withdraw(state: ContractState, env: Env, info: MessageInfo, ids: List[int]): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) | ||
|
||
action withdraw_action = { | ||
// TODO: Change next line according to fund expectations | ||
pure val max_funds = MAX_AMOUNT | ||
val possibilities = 0.to(MAX_AMOUNT).map(i => Some(i)).union(Set(None)) | ||
nondet v1 = possibilities.oneOf() | ||
nondet v2 = possibilities.oneOf() | ||
nondet v3 = possibilities.oneOf() | ||
pure val message_ids: List[int] = | ||
[v1, v2, v3].foldl([], (acc, v) => match v { | ||
| Some(i) => acc.append(i) | ||
| None => acc | ||
}) | ||
|
||
pure val message: ExecuteMsg = ExecuteMsg_Withdraw({ ids: message_ids }) | ||
execute_message(message, max_funds) | ||
} | ||
pure val DENOM = "uawesome" | ||
pure val MINIMUM_DEPOSIT_AMOUNT = 10000 | ||
pure val LOCK_PERIOD = 60 * 60 * 24 | ||
|
||
type ContractState = { | ||
last_id: int, | ||
lockups: int -> Lockup | ||
} | ||
|
||
pure val init_contract_state = { | ||
last_id: 0, | ||
lockups: Map() | ||
} | ||
|
||
action execute_step = all { | ||
any { | ||
deposit_action, | ||
withdraw_action | ||
}, | ||
advance_time, | ||
} | ||
|
||
|
||
pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) | ||
|
||
|
||
|
||
pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) | ||
|
||
val env_val = { block: { time: time } } | ||
|
||
action init = all { | ||
contract_state' = init_contract_state, | ||
bank' = init_bank_state, | ||
return' = Err("No previous request"), | ||
time' = 0, | ||
} | ||
|
||
|
||
action execute_message(message, max_funds) = { | ||
nondet sender = ADDRESSES.oneOf() | ||
nondet denom = DENOMS.oneOf() | ||
nondet amount = 0.to(max_funds).oneOf() | ||
val funds = [{ denom: denom, amount: amount }] | ||
val info = { sender: sender, funds: funds } | ||
|
||
val r = execute(contract_state, env_val, info, message) | ||
all { | ||
bank.get(sender).get(denom) >= amount, | ||
bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) | ||
.setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), | ||
return' = r._1, | ||
contract_state' = r._2, | ||
} | ||
} | ||
|
||
action advance_time = time' = time + 1 | ||
|
||
action step = { | ||
val message_getting = get_message(return) | ||
val new_return = message_getting._1 | ||
val opt_message = message_getting._2 | ||
match opt_message { | ||
| Some(submsg) => { | ||
val current_state = { bank: bank, return: new_return, contract_state: contract_state } | ||
val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) | ||
all { | ||
bank' = new_state.bank, | ||
return' = new_state.return, | ||
contract_state' = new_state.contract_state, | ||
advance_time, | ||
} | ||
} | ||
| None => execute_step | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.