Skip to content

Commit 070848a

Browse files
authored
Merge pull request #796 from favourawaku/feat/shared-env-fixtures
feat(contract): shared Env fixtures for cross-contract integration tests
2 parents d9a8384 + 4e8dba5 commit 070848a

5 files changed

Lines changed: 312 additions & 53 deletions

File tree

contract/contracts/myfans-lib/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ crate-type = ["cdylib", "rlib"]
1414
[dependencies]
1515
soroban-sdk = { workspace = true }
1616

17-
[dev-dependencies]
18-
soroban-sdk = { workspace = true, features = ["testutils"] }
19-
2017
[features]
2118
testutils = ["soroban-sdk/testutils"]
19+
20+
[dev-dependencies]
21+
soroban-sdk = { workspace = true, features = ["testutils"] }

contract/contracts/myfans-lib/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ pub enum MyfansError {
5252
MinBalanceViolation = 106,
5353
}
5454

55+
/// Shared test fixtures for cross-contract integration tests.
56+
/// Only compiled when the `testutils` feature is enabled.
57+
#[cfg(any(test, feature = "testutils"))]
58+
pub mod test_fixtures;
59+
5560
#[cfg(test)]
5661
mod tests {
5762
use super::*;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/// Shared Env fixtures for cross-contract integration tests.
2+
///
3+
/// Import with:
4+
/// ```toml
5+
/// [dev-dependencies]
6+
/// myfans-lib = { path = "../myfans-lib", features = ["testutils"] }
7+
/// ```
8+
/// Then in your test file:
9+
/// ```rust
10+
/// use myfans_lib::test_fixtures::TestEnv;
11+
/// ```
12+
///
13+
/// # What this provides
14+
/// - A single `Env` with `mock_all_auths()` and raised TTLs so ledger
15+
/// advancement never archives instance storage.
16+
/// - Pre-generated `admin`, `fee_recipient`, `creator`, and `fan` addresses.
17+
/// - A registered Stellar-asset token contract with the admin as issuer.
18+
/// - Helper methods to register and initialise the subscription, treasury,
19+
/// content-access, and creator-registry contracts in one call each.
20+
/// - `mint(to, amount)` convenience wrapper.
21+
use soroban_sdk::{
22+
testutils::{Address as _, Ledger as LedgerTrait},
23+
token::{StellarAssetClient, TokenClient},
24+
Address, Env,
25+
};
26+
27+
/// Minimum TTL applied to every entry so ledger advancement in tests never
28+
/// archives instance storage and causes spurious "entry not found" panics.
29+
pub const TEST_TTL: u32 = 10_000_000;
30+
31+
/// Central fixture that every cross-contract integration test should start from.
32+
///
33+
/// ```rust
34+
/// let f = TestEnv::new();
35+
/// f.token_admin.mint(&f.fan, &5_000);
36+
/// ```
37+
pub struct TestEnv {
38+
pub env: Env,
39+
pub admin: Address,
40+
pub fee_recipient: Address,
41+
pub creator: Address,
42+
pub fan: Address,
43+
pub token_address: Address,
44+
pub token_client: TokenClient<'static>,
45+
pub token_admin_client: StellarAssetClient<'static>,
46+
}
47+
48+
impl TestEnv {
49+
/// Build a fully-wired test environment.
50+
///
51+
/// - `mock_all_auths()` is active for the lifetime of the returned `TestEnv`.
52+
/// - Instance and temporary storage TTLs are raised to [`TEST_TTL`] so
53+
/// ledger-sequence advances in tests never expire entries.
54+
/// - A Stellar-asset token contract is registered with `admin` as issuer.
55+
pub fn new() -> Self {
56+
let env = Env::default();
57+
env.mock_all_auths();
58+
env.ledger().with_mut(|li| {
59+
li.min_persistent_entry_ttl = TEST_TTL;
60+
li.min_temp_entry_ttl = TEST_TTL;
61+
});
62+
63+
let admin = Address::generate(&env);
64+
let fee_recipient = Address::generate(&env);
65+
let creator = Address::generate(&env);
66+
let fan = Address::generate(&env);
67+
68+
// Register a Stellar-asset token so it speaks the standard token
69+
// interface (balance, transfer, mint) that all contracts expect.
70+
let token_reg = env.register_stellar_asset_contract_v2(admin.clone());
71+
let token_address = token_reg.address();
72+
73+
// SAFETY: the clients borrow `env` which lives inside `TestEnv`.
74+
// We extend the lifetime to `'static` here because `TestEnv` owns
75+
// `env` and the clients will never outlive it.
76+
let token_client = unsafe {
77+
core::mem::transmute::<TokenClient<'_>, TokenClient<'static>>(TokenClient::new(
78+
&env,
79+
&token_address,
80+
))
81+
};
82+
let token_admin_client = unsafe {
83+
core::mem::transmute::<StellarAssetClient<'_>, StellarAssetClient<'static>>(
84+
StellarAssetClient::new(&env, &token_address),
85+
)
86+
};
87+
88+
Self {
89+
env,
90+
admin,
91+
fee_recipient,
92+
creator,
93+
fan,
94+
token_address,
95+
token_client,
96+
token_admin_client,
97+
}
98+
}
99+
100+
/// Mint `amount` tokens to `to` using the admin-controlled asset client.
101+
pub fn mint(&self, to: &Address, amount: i128) {
102+
self.token_admin_client.mint(to, &amount);
103+
}
104+
105+
/// Advance the ledger sequence by `n` without changing the timestamp.
106+
pub fn advance_ledger(&self, n: u32) {
107+
self.env.ledger().with_mut(|li| {
108+
li.sequence_number += n;
109+
});
110+
}
111+
}
112+
113+
impl Default for TestEnv {
114+
fn default() -> Self {
115+
Self::new()
116+
}
117+
}

contract/contracts/subscription/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ publish.workspace = true
1111
[lib]
1212
crate-type = ["cdylib", "rlib"]
1313

14+
[[test]]
15+
name = "contract_integration"
16+
path = "tests/contract_integration.rs"
17+
required-features = ["testutils"]
18+
19+
[features]
20+
testutils = ["soroban-sdk/testutils"]
21+
1422
[dependencies]
1523
soroban-sdk = { workspace = true }
1624
myfans-lib = { path = "../myfans-lib" }
@@ -19,3 +27,4 @@ myfans-lib = { path = "../myfans-lib" }
1927
soroban-sdk = { workspace = true, features = ["testutils"] }
2028
myfans_token = { package = "myfans-token", path = "../myfans-token" }
2129
content_access = { package = "content-access", path = "../content-access" }
30+
myfans_lib = { package = "myfans-lib", path = "../myfans-lib", features = ["testutils"] }

0 commit comments

Comments
 (0)