Skip to main content

xrpl_common_stdlib/host/
host_bindings_test.rs

1#[cfg(not(target_arch = "wasm32"))]
2use crate::host::host_bindings_trait::{HostBindings, MockHostBindings};
3use std::cell::RefCell;
4
5#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
6pub struct MockGuard;
7
8#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
9impl Drop for MockGuard {
10    fn drop(&mut self) {
11        clear_mock_host_bindings();
12    }
13}
14
15#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
16pub fn setup_mock(mock: MockHostBindings) -> MockGuard {
17    set_mock_host_bindings(mock);
18    MockGuard
19}
20
21// Create a default mock with stub return values matching the old host_bindings_for_testing.rs
22#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
23pub fn create_default_mock() -> MockHostBindings {
24    let mut mock = MockHostBindings::new();
25    apply_default_expectations(&mut mock);
26    mock
27}
28
29/// Applies the same default `.returning(...)` wiring as [`create_default_mock`] onto an
30/// existing mock instead of constructing a new one.
31///
32/// Exposed so callers (e.g. scenario builders in `xrpl-stdlib-test-utils`) can register their
33/// own expectations on a fresh mock first, then layer these defaults on top as a fallback:
34/// mockall checks expectations in the order they were registered, so registering
35/// scenario-specific expectations before calling this function lets them take priority over
36/// the unconditional defaults added here.
37#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
38pub fn apply_default_expectations(mock: &mut MockHostBindings) {
39    // Ledger info functions - return small positive values
40    mock.expect_ldgr_index()
41        .returning(|_, out_buff_len| out_buff_len as i32);
42    mock.expect_parent_ldgr_time()
43        .returning(|_, out_buff_len| out_buff_len as i32);
44    mock.expect_base_fee()
45        .returning(|_, out_buff_len| out_buff_len as i32);
46
47    // Functions that return buffer length
48    mock.expect_parent_ldgr_hash()
49        .returning(|_, out_buff_len| out_buff_len as i32);
50    mock.expect_amendment_enabled()
51        .returning(|_, amendment_len| amendment_len as i32);
52    mock.expect_cache_le()
53        .returning(|_, id_len, _| id_len as i32);
54    // A real host returns the number of bytes it actually wrote, which for an `Amount` is the
55    // variant's wire length (8 XRP / 33 MPT / 48 IOU), not the full buffer. Amount-typed fields
56    // are exactly those whose serialized type code (high 16 bits) is `STI_AMOUNT` (6); the zeroed
57    // default buffer decodes as XRP, so report 8 for them. Every other (fixed-size) field still
58    // reports the full buffer length, which equals its exact size. This applies uniformly to
59    // reads from the current transaction, the current ledger object, and a slot-cached one.
60    const STI_AMOUNT: i32 = 6;
61    mock.expect_tx_field().returning(|field, _, out_buff_len| {
62        if field >> 16 == STI_AMOUNT {
63            8
64        } else {
65            out_buff_len as i32
66        }
67    });
68    mock.expect_home_le_field()
69        .returning(|field, _, out_buff_len| {
70            if field >> 16 == STI_AMOUNT {
71                8
72            } else {
73                out_buff_len as i32
74            }
75        });
76    mock.expect_le_field()
77        .returning(|_, field, _, out_buff_len| {
78            if field >> 16 == STI_AMOUNT {
79                8
80            } else {
81                out_buff_len as i32
82            }
83        });
84    mock.expect_tx_inner()
85        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
86    mock.expect_home_le_inner()
87        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
88    mock.expect_le_inner()
89        .returning(|_, _, _, _, out_buff_len| out_buff_len as i32);
90
91    // Array length functions
92    mock.expect_tx_arr_len().returning(|_| 0);
93    mock.expect_home_le_arr_len().returning(|_| 0);
94    mock.expect_le_arr_len().returning(|_, _| 0);
95    mock.expect_tx_inner_arr_len().returning(|_, _| 0);
96    // Note: These two return locator_len, not 0
97    mock.expect_home_le_inner_arr_len()
98        .returning(|_, locator_len| locator_len as i32);
99    mock.expect_le_inner_arr_len()
100        .returning(|_, _, locator_len| locator_len as i32);
101
102    // Update and crypto functions
103    mock.expect_set_data()
104        .returning(|_, data_len| data_len as i32);
105    mock.expect_sha512_half()
106        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
107    mock.expect_check_sig().returning(|_, _, _, _, _, _| 0);
108
109    // Ledger entry ID functions - all return buffer length
110    mock.expect_accountroot_id()
111        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
112    mock.expect_amm_id()
113        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
114    mock.expect_check_id()
115        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
116    mock.expect_credential_id()
117        .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
118    mock.expect_delegate_id()
119        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
120    mock.expect_deposit_preauth_id()
121        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
122    mock.expect_did_id()
123        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
124    mock.expect_escrow_id()
125        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
126    mock.expect_trustline_id()
127        .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
128    mock.expect_mpt_issuance_id()
129        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
130    mock.expect_mptoken_id()
131        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
132    mock.expect_nft_offer_id()
133        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
134    mock.expect_offer_id()
135        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
136    mock.expect_oracle_id()
137        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
138    mock.expect_paychan_id()
139        .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
140    mock.expect_permissioned_domain_id()
141        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
142    mock.expect_signers_id()
143        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
144    mock.expect_ticket_id()
145        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
146    mock.expect_vault_id()
147        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
148    mock.expect_sponsorship_id()
149        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
150    mock.expect_loan_broker_id()
151        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
152    mock.expect_loan_id()
153        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
154
155    // NFT functions
156    mock.expect_nft_uri()
157        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
158    mock.expect_nft_issuer()
159        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
160    mock.expect_nft_taxon()
161        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
162    mock.expect_nft_flags()
163        .returning(|_, nft_id_len| nft_id_len as i32);
164    mock.expect_nft_xfer_fee()
165        .returning(|_, nft_id_len| nft_id_len as i32);
166    mock.expect_nft_serial()
167        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
168
169    // Float functions
170    mock.expect_float_from_int()
171        .returning(|_, _, out_buff_len, _| out_buff_len as i32);
172    mock.expect_float_from_uint()
173        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
174    mock.expect_float_from_mant_exp()
175        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
176    mock.expect_float_from_stamount()
177        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
178    mock.expect_float_from_stnumber()
179        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
180    mock.expect_float_to_int()
181        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
182    mock.expect_float_to_mant_exp()
183        .returning(|_, _, _, _, _, _| 8);
184    mock.expect_float_cmp().returning(|_, _, _, _| 0);
185    mock.expect_float_add()
186        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
187    mock.expect_float_sub()
188        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
189    mock.expect_float_mult()
190        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
191    mock.expect_float_div()
192        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
193    mock.expect_float_pow()
194        .returning(|_, _, _, _, out_buff_len, _| out_buff_len as i32);
195
196    // Trace
197    mock.expect_trace().returning(|_, _, _, _, _| ());
198}
199
200// #[cfg(test)]
201#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
202thread_local! {
203    static MOCK_STATE: RefCell<Option<MockHostBindings>> = RefCell::new(Some(create_default_mock()));
204}
205
206// Helper functions to manage the mock state
207#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
208pub fn set_mock_host_bindings(mock: MockHostBindings) {
209    MOCK_STATE.with(|state| {
210        *state.borrow_mut() = Some(mock);
211    });
212}
213
214#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
215pub fn clear_mock_host_bindings() {
216    MOCK_STATE.with(|state| {
217        *state.borrow_mut() = None;
218    });
219}
220
221#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
222// Macro to generate stub functions for non-WASM targets
223// These functions delegate to the MockHostBindings in MOCK_STATE
224macro_rules! export_host_functions {
225    ($(
226        $(#[$attr:meta])*
227        fn $name:ident($($param:ident: $param_ty:ty),*) -> $ret:ty;
228    )*) => {
229        $(
230            #[allow(clippy::too_many_arguments)]
231            #[allow(clippy::missing_safety_doc)]
232            $(#[$attr])*
233            pub unsafe fn $name($($param: $param_ty),*) -> $ret {
234                MOCK_STATE.with(|state|  {
235                    // The mock should always be present due to default initialization
236                    // If it's not, panic with a clear error message
237                    let mock = state.borrow();
238                    let mock_ref = mock.as_ref().expect("MockHostBindings not initialized");
239                    unsafe { mock_ref.$name($($param),*) }
240                })
241            }
242        )*
243    };
244}
245
246// Re-export all host functions as public functions for use by the rest of the codebase
247// For non-WASM targets, these are stub implementations that panic
248// The actual test implementations using MockHostBindings are in the tests module below
249
250// Generate all the stub functions
251export_host_functions! {
252    // Host Function Category: ledger and transaction info
253    fn ldgr_index(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
254    fn parent_ldgr_time(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
255    fn parent_ldgr_hash(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
256    fn base_fee(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
257    fn amendment_enabled(amendment_ptr: *const u8, amendment_len: usize) -> i32;
258    fn cache_le(id_ptr: *const u8, id_len: usize, cache_num: i32) -> i32;
259    fn tx_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
260    fn home_le_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
261    fn le_field(cache_num: i32, field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
262    fn tx_inner(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
263    fn home_le_inner(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
264    fn le_inner(cache_num: i32, locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
265    fn tx_arr_len(field: i32) -> i32;
266    fn home_le_arr_len(field: i32) -> i32;
267    fn le_arr_len(cache_num: i32, field: i32) -> i32;
268    fn tx_inner_arr_len(locator_ptr: *const u8, locator_len: usize) -> i32;
269    fn home_le_inner_arr_len(locator_ptr: *const u8, locator_len: usize) -> i32;
270    fn le_inner_arr_len(cache_num: i32, locator_ptr: *const u8, locator_len: usize) -> i32;
271
272    // Host Function Category: update current ledger entry
273    fn set_data(data_ptr: *const u8, data_len: usize) -> i32;
274
275    // Host Function Category: hash and ledger entry ID computation
276    fn sha512_half(data_ptr: *const u8, data_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
277    fn check_sig(message_ptr: *const u8, message_len: usize, signature_ptr: *const u8, signature_len: usize, pubkey_ptr: *const u8, pubkey_len: usize) -> i32;
278    fn accountroot_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
279    fn amm_id(issue1_ptr: *const u8, issue1_len: usize, issue2_ptr: *const u8, issue2_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
280    fn check_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
281    fn credential_id(subject_ptr: *const u8, subject_len: usize, issuer_ptr: *const u8, issuer_len: usize, cred_type_ptr: *const u8, cred_type_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
282    fn delegate_id(account_ptr: *const u8, account_len: usize, authorize_ptr: *const u8, authorize_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
283    fn deposit_preauth_id(account_ptr: *const u8, account_len: usize, authorize_ptr: *const u8, authorize_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
284    fn did_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
285    fn escrow_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
286    fn trustline_id(account1_ptr: *const u8, account1_len: usize, account2_ptr: *const u8, account2_len: usize, currency_ptr: *const u8, currency_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
287    fn mpt_issuance_id(issuer_ptr: *const u8, issuer_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
288    fn mptoken_id(mptid_ptr: *const u8, mptid_len: usize, holder_ptr: *const u8, holder_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
289    fn nft_offer_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
290    fn offer_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
291    fn oracle_id(account_ptr: *const u8, account_len: usize, document_id_ptr: *const u8, document_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
292    fn paychan_id(account_ptr: *const u8, account_len: usize, destination_ptr: *const u8, destination_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
293    fn permissioned_domain_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
294    fn signers_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
295    fn ticket_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
296    fn vault_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
297    fn sponsorship_id(sponsor_ptr: *const u8, sponsor_len: usize, sponsee_ptr: *const u8, sponsee_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
298    fn loan_broker_id(owner_ptr: *const u8, owner_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
299    fn loan_id(loan_broker_id_ptr: *const u8, loan_broker_id_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
300
301    // Host Function Category: NFT
302    fn nft_uri(account_ptr: *const u8, account_len: usize, nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
303    fn nft_issuer(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
304    fn nft_taxon(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
305    fn nft_flags(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
306    fn nft_xfer_fee(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
307    fn nft_serial(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
308
309    // Host Function Category: FLOAT
310    fn float_from_int(in_int: i64, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
311    fn float_from_uint(in_uint_ptr: *const u8, in_uint_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
312    fn float_from_mant_exp(mantissa: i64, exponent: i32, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
313    fn float_from_stamount(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
314    fn float_from_stnumber(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
315    fn float_to_int(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
316    fn float_to_mant_exp(in_buff: *const u8, in_buff_len: usize, mant_buff: *mut u8, mant_buff_len: usize, exp_buff: *mut u8, exp_buff_len: usize) -> i32;
317    fn float_cmp(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize) -> i32;
318    fn float_add(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
319    fn float_sub(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
320    fn float_mult(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
321    fn float_div(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
322    fn float_pow(in_buff: *const u8, in_buff_len: usize, pow: i32, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
323
324    // Host Function Category: TRACE
325    fn trace(msg_read_ptr: *const u8, msg_read_len: usize, data_type: i32, data_read_ptr: *const u8, data_read_len: usize) -> ();
326
327}
328
329#[cfg(test)]
330mod tests {
331    use super::*;
332    use crate::host::trace::TraceDataType;
333
334    #[test]
335    fn test_ledger_functions_with_mock() {
336        let mut mock = MockHostBindings::new();
337
338        // Set up expectations - these functions now take buffer parameters
339        mock.expect_ldgr_index().times(1).returning(|_, _| 12345);
340        mock.expect_parent_ldgr_time()
341            .times(1)
342            .returning(|_, _| 1234567890);
343        mock.expect_base_fee().times(1).returning(|_, _| 10);
344
345        // Set the mock in thread-local storage
346        set_mock_host_bindings(mock);
347
348        // Test the exported functions (they will use the mock)
349        let mut buffer = [0u8; 32];
350        unsafe {
351            assert_eq!(ldgr_index(buffer.as_mut_ptr(), buffer.len()), 12345);
352            assert_eq!(
353                parent_ldgr_time(buffer.as_mut_ptr(), buffer.len()),
354                1234567890
355            );
356            assert_eq!(base_fee(buffer.as_mut_ptr(), buffer.len()), 10);
357        }
358
359        // Clean up
360        clear_mock_host_bindings();
361    }
362
363    #[test]
364    fn test_buffer_operations_with_mock() {
365        let mut mock = MockHostBindings::new();
366
367        // Mock parent_ldgr_hash to write test data
368        mock.expect_parent_ldgr_hash()
369            .times(1)
370            .returning(|out_buff_ptr, out_buff_len| {
371                if out_buff_len >= 32 {
372                    unsafe {
373                        // Write test hash data
374                        for i in 0..32 {
375                            *out_buff_ptr.add(i) = (i * 2) as u8;
376                        }
377                    }
378                    32 // Return bytes written
379                } else {
380                    -1 // Buffer too small error
381                }
382            });
383
384        // Test it
385        let mut buffer = [0u8; 32];
386        unsafe {
387            let result = mock.parent_ldgr_hash(buffer.as_mut_ptr(), buffer.len());
388            assert_eq!(result, 32);
389
390            // Verify the mock wrote the expected data
391            for (i, _) in buffer.iter().enumerate() {
392                assert_eq!(buffer[i], (i * 2) as u8);
393            }
394        }
395    }
396
397    #[test]
398    fn test_trace_functions_with_mock() {
399        let mut mock = MockHostBindings::new();
400
401        mock.expect_trace()
402            .times(2)
403            .returning(|_msg_ptr, _msg_len, _data_type, _data_ptr, _data_len| ());
404
405        let message = b"Test message";
406        let data = b"Test data";
407        let number = 42i64.to_le_bytes();
408
409        unsafe {
410            mock.trace(
411                message.as_ptr(),
412                message.len(),
413                TraceDataType::AsText as i32,
414                data.as_ptr(),
415                data.len(),
416            );
417
418            mock.trace(
419                message.as_ptr(),
420                message.len(),
421                TraceDataType::Int64 as i32,
422                number.as_ptr(),
423                number.len(),
424            );
425        }
426    }
427
428    #[test]
429    fn test_id_functions_with_mock() {
430        let mut mock = MockHostBindings::new();
431
432        // Mock accountroot_id to return a test ledger entry ID
433        mock.expect_accountroot_id().times(1).returning(
434            |_account_ptr, _account_len, out_buff_ptr, out_buff_len| {
435                if out_buff_len >= 32 {
436                    unsafe {
437                        // Write a test ledger entry ID (32 bytes of 0xAA)
438                        for i in 0..32 {
439                            *out_buff_ptr.add(i) = 0xAA;
440                        }
441                    }
442                    32
443                } else {
444                    -1
445                }
446            },
447        );
448
449        // Test ledger entry ID generation
450        let account = [0u8; 20]; // Mock account ID
451        let mut id_buffer = [0u8; 32];
452
453        unsafe {
454            let result = mock.accountroot_id(
455                account.as_ptr(),
456                account.len(),
457                id_buffer.as_mut_ptr(),
458                id_buffer.len(),
459            );
460
461            assert_eq!(result, 32);
462            assert_eq!(id_buffer, [0xAA; 32]);
463        }
464    }
465
466    #[test]
467    fn test_error_conditions_with_mock() {
468        let mut mock = MockHostBindings::new();
469
470        // Mock a function to return an error code
471        mock.expect_ldgr_index().times(1).returning(|_, _| -1); // Return error
472
473        mock.expect_parent_ldgr_hash()
474            .times(1)
475            .returning(|_out_buff_ptr, _out_buff_len| -2); // Buffer too small
476
477        unsafe {
478            // Test error conditions
479            let mut buffer = [0u8; 32];
480            assert_eq!(mock.ldgr_index(buffer.as_mut_ptr(), buffer.len()), -1);
481
482            let mut small_buffer = [0u8; 16]; // Too small buffer
483            let result = mock.parent_ldgr_hash(small_buffer.as_mut_ptr(), small_buffer.len());
484            assert_eq!(result, -2);
485        }
486    }
487
488    #[test]
489    fn test_generic_function_with_mock() {
490        // Example of testing a function that takes HostBindings as a parameter
491        fn get_ledger_info<H: HostBindings>(host: &H) -> (i32, i32, i32) {
492            let mut buffer = [0u8; 32];
493            unsafe {
494                let sqn = host.ldgr_index(buffer.as_mut_ptr(), buffer.len());
495                let time = host.parent_ldgr_time(buffer.as_mut_ptr(), buffer.len());
496                let fee = host.base_fee(buffer.as_mut_ptr(), buffer.len());
497                (sqn, time, fee)
498            }
499        }
500
501        let mut mock = MockHostBindings::new();
502
503        mock.expect_ldgr_index().returning(|_, _| 999);
504        mock.expect_parent_ldgr_time().returning(|_, _| 888);
505        mock.expect_base_fee().returning(|_, _| 777);
506
507        let (sqn, time, fee) = get_ledger_info(&mock);
508        assert_eq!(sqn, 999);
509        assert_eq!(time, 888);
510        assert_eq!(fee, 777);
511    }
512}