Skip to content
Merged
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
35 changes: 34 additions & 1 deletion .github/workflows/soroban.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,40 @@ jobs:
manifest-path: quantara/soroban/contracts/Cargo.toml
arguments: --config quantara/soroban/contracts/deny.toml

# ── 4. Build ────────────────────────────────────────────────────────────────
# ── 4. Test ─────────────────────────────────────────────────────────────────
test:
name: Test (cargo test)
runs-on: ubuntu-latest
permissions:
contents: read
needs: [fmt, clippy]

steps:
- name: Checkout repository
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
targets: wasm32-unknown-unknown

- name: Cache Cargo registry & build artifacts
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
quantara/soroban/contracts/target/
key: ${{ runner.os }}-cargo-test-${{ hashFiles('quantara/soroban/contracts/Cargo.toml', 'quantara/soroban/contracts/**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-test-

- name: Run tests
run: cargo test

# ── 5. Build ────────────────────────────────────────────────────────────────
build:
name: Build (wasm32-unknown-unknown)
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions quantara/soroban/contracts/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions quantara/soroban/contracts/looping/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ doctest = false
[dependencies]
soroban-sdk = { version = "22.0.0" }
common = { path = "../common", version = "0.1.0" }

[dev-dependencies]
soroban-sdk = { version = "22.0.0", features = ["testutils"] }
ed25519-dalek = "<3"
rand_core = "<0.7"
113 changes: 113 additions & 0 deletions quantara/soroban/contracts/looping/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,116 @@ impl LoopingContract {
// Stub: full unwind logic will be implemented in a future PR.
}
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
extern crate std;

use super::*;
use soroban_sdk::testutils::Address as _;

struct Fixture {
env: Env,
contract_id: Address,
user: Address,
}

fn setup() -> Fixture {
let env = Env::default();
env.mock_all_auths();

let user = Address::generate(&env);
let contract_id = env.register(LoopingContract, ());

Fixture {
env,
contract_id,
user,
}
}

#[test]
fn test_open_position_returns_first_id() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

let id = client.open_position(&fx.user, &1_000, &200);
assert_eq!(id, 1);
}

#[test]
fn test_open_position_returns_incrementing_ids() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

assert_eq!(client.open_position(&fx.user, &1_000, &200), 1);
assert_eq!(client.open_position(&fx.user, &2_000, &300), 2);
assert_eq!(client.open_position(&fx.user, &500, &100), 3);
}

#[test]
fn test_open_position_rejects_zero_collateral() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
client.open_position(&fx.user, &0, &200);
}));
assert!(result.is_err());
}

#[test]
fn test_open_position_rejects_negative_collateral() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
client.open_position(&fx.user, &-1, &200);
}));
assert!(result.is_err());
}

#[test]
fn test_open_position_rejects_leverage_below_100() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
client.open_position(&fx.user, &1_000, &99);
}));
assert!(result.is_err());
}

#[test]
fn test_open_position_rejects_leverage_above_500() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
client.open_position(&fx.user, &1_000, &501);
}));
assert!(result.is_err());
}

#[test]
fn test_open_position_accepts_leverage_boundaries() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

assert_eq!(client.open_position(&fx.user, &1_000, &100), 1);
assert_eq!(client.open_position(&fx.user, &1_000, &500), 2);
}

#[test]
fn test_close_position_does_not_panic() {
let fx = setup();
let client = LoopingContractClient::new(&fx.env, &fx.contract_id);

let id = client.open_position(&fx.user, &1_000, &200);
client.close_position(&fx.user, &id);
}
}
56 changes: 55 additions & 1 deletion quantara/soroban/contracts/rewards/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ mod tests {
let init = MockAuthInvoke {
contract: &contract_id,
fn_name: "initialize",
args: vec![&env],
args: vec![&env, symbol_short!("init").to_val()],
sub_invokes: &[],
};
env.mock_auths(&[MockAuth {
Expand Down Expand Up @@ -186,4 +186,58 @@ mod tests {
let res = client.try_accrue(&user, &1_i128);
assert!(res.is_err(), "overflow must surface an error");
}

#[test]
fn test_claim_on_zero_balance_returns_zero() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user = Address::generate(&env);
let contract_id = env.register(RewardsContract, ());
let client = RewardsContractClient::new(&env, &contract_id);

client.initialize(&admin);

let claimed = client.claim(&user);
assert_eq!(claimed, 0);
}

#[test]
fn test_pending_rewards_query_without_claiming() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user = Address::generate(&env);
let contract_id = env.register(RewardsContract, ());
let client = RewardsContractClient::new(&env, &contract_id);

client.initialize(&admin);
client.accrue(&user, &500_i128);
let pending = client.pending_rewards(&user);

assert_eq!(pending, 500);
assert_eq!(client.pending_rewards(&user), 500);
}

#[test]
fn test_multiple_accrue_then_claim() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user = Address::generate(&env);
let contract_id = env.register(RewardsContract, ());
let client = RewardsContractClient::new(&env, &contract_id);

client.initialize(&admin);
client.accrue(&user, &100_i128);
client.accrue(&user, &200_i128);
client.accrue(&user, &50_i128);

assert_eq!(client.pending_rewards(&user), 350);
assert_eq!(client.claim(&user), 350);
assert_eq!(client.pending_rewards(&user), 0);
}
}
Loading