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
149    // NFT functions
150    mock.expect_nft_uri()
151        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
152    mock.expect_nft_issuer()
153        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
154    mock.expect_nft_taxon()
155        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
156    mock.expect_nft_flags()
157        .returning(|_, nft_id_len| nft_id_len as i32);
158    mock.expect_nft_xfer_fee()
159        .returning(|_, nft_id_len| nft_id_len as i32);
160    mock.expect_nft_serial()
161        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
162
163    // Float functions
164    mock.expect_float_from_int()
165        .returning(|_, _, out_buff_len, _| out_buff_len as i32);
166    mock.expect_float_from_uint()
167        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
168    mock.expect_float_from_mant_exp()
169        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
170    mock.expect_float_from_stamount()
171        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
172    mock.expect_float_from_stnumber()
173        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
174    mock.expect_float_to_int()
175        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
176    mock.expect_float_to_mant_exp()
177        .returning(|_, _, _, _, _, _| 8);
178    mock.expect_float_cmp().returning(|_, _, _, _| 0);
179    mock.expect_float_add()
180        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
181    mock.expect_float_sub()
182        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
183    mock.expect_float_mult()
184        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
185    mock.expect_float_div()
186        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
187    mock.expect_float_pow()
188        .returning(|_, _, _, _, out_buff_len, _| out_buff_len as i32);
189
190    // Trace
191    mock.expect_trace().returning(|_, _, _, _, _| ());
192}
193
194// #[cfg(test)]
195#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
196thread_local! {
197    static MOCK_STATE: RefCell<Option<MockHostBindings>> = RefCell::new(Some(create_default_mock()));
198}
199
200// Helper functions to manage the mock state
201#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
202pub fn set_mock_host_bindings(mock: MockHostBindings) {
203    MOCK_STATE.with(|state| {
204        *state.borrow_mut() = Some(mock);
205    });
206}
207
208#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
209pub fn clear_mock_host_bindings() {
210    MOCK_STATE.with(|state| {
211        *state.borrow_mut() = None;
212    });
213}
214
215#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
216// Macro to generate stub functions for non-WASM targets
217// These functions delegate to the MockHostBindings in MOCK_STATE
218macro_rules! export_host_functions {
219    ($(
220        $(#[$attr:meta])*
221        fn $name:ident($($param:ident: $param_ty:ty),*) -> $ret:ty;
222    )*) => {
223        $(
224            #[allow(clippy::too_many_arguments)]
225            #[allow(clippy::missing_safety_doc)]
226            $(#[$attr])*
227            pub unsafe fn $name($($param: $param_ty),*) -> $ret {
228                MOCK_STATE.with(|state|  {
229                    // The mock should always be present due to default initialization
230                    // If it's not, panic with a clear error message
231                    let mock = state.borrow();
232                    let mock_ref = mock.as_ref().expect("MockHostBindings not initialized");
233                    unsafe { mock_ref.$name($($param),*) }
234                })
235            }
236        )*
237    };
238}
239
240// Re-export all host functions as public functions for use by the rest of the codebase
241// For non-WASM targets, these are stub implementations that panic
242// The actual test implementations using MockHostBindings are in the tests module below
243
244// Generate all the stub functions
245export_host_functions! {
246    // Host Function Category: ledger and transaction info
247    fn ldgr_index(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
248    fn parent_ldgr_time(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
249    fn parent_ldgr_hash(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
250    fn base_fee(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
251    fn amendment_enabled(amendment_ptr: *const u8, amendment_len: usize) -> i32;
252    fn cache_le(id_ptr: *const u8, id_len: usize, cache_num: i32) -> i32;
253    fn tx_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
254    fn home_le_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
255    fn le_field(cache_num: i32, field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
256    fn tx_inner(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
257    fn home_le_inner(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
258    fn le_inner(cache_num: i32, locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
259    fn tx_arr_len(field: i32) -> i32;
260    fn home_le_arr_len(field: i32) -> i32;
261    fn le_arr_len(cache_num: i32, field: i32) -> i32;
262    fn tx_inner_arr_len(locator_ptr: *const u8, locator_len: usize) -> i32;
263    fn home_le_inner_arr_len(locator_ptr: *const u8, locator_len: usize) -> i32;
264    fn le_inner_arr_len(cache_num: i32, locator_ptr: *const u8, locator_len: usize) -> i32;
265
266    // Host Function Category: update current ledger entry
267    fn set_data(data_ptr: *const u8, data_len: usize) -> i32;
268
269    // Host Function Category: hash and ledger entry ID computation
270    fn sha512_half(data_ptr: *const u8, data_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
271    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;
272    fn accountroot_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
273    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;
274    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;
275    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;
276    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;
277    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;
278    fn did_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
279    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;
280    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;
281    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;
282    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;
283    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;
284    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;
285    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;
286    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;
287    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;
288    fn signers_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
289    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;
290    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;
291
292    // Host Function Category: NFT
293    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;
294    fn nft_issuer(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
295    fn nft_taxon(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
296    fn nft_flags(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
297    fn nft_xfer_fee(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
298    fn nft_serial(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
299
300    // Host Function Category: FLOAT
301    fn float_from_int(in_int: i64, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
302    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;
303    fn float_from_mant_exp(mantissa: i64, exponent: i32, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
304    fn float_from_stamount(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
305    fn float_from_stnumber(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
306    fn float_to_int(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
307    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;
308    fn float_cmp(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize) -> i32;
309    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;
310    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;
311    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;
312    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;
313    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;
314
315    // Host Function Category: TRACE
316    fn trace(msg_read_ptr: *const u8, msg_read_len: usize, data_type: i32, data_read_ptr: *const u8, data_read_len: usize) -> ();
317
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323    use crate::host::trace::TraceDataType;
324
325    #[test]
326    fn test_ledger_functions_with_mock() {
327        let mut mock = MockHostBindings::new();
328
329        // Set up expectations - these functions now take buffer parameters
330        mock.expect_ldgr_index().times(1).returning(|_, _| 12345);
331        mock.expect_parent_ldgr_time()
332            .times(1)
333            .returning(|_, _| 1234567890);
334        mock.expect_base_fee().times(1).returning(|_, _| 10);
335
336        // Set the mock in thread-local storage
337        set_mock_host_bindings(mock);
338
339        // Test the exported functions (they will use the mock)
340        let mut buffer = [0u8; 32];
341        unsafe {
342            assert_eq!(ldgr_index(buffer.as_mut_ptr(), buffer.len()), 12345);
343            assert_eq!(
344                parent_ldgr_time(buffer.as_mut_ptr(), buffer.len()),
345                1234567890
346            );
347            assert_eq!(base_fee(buffer.as_mut_ptr(), buffer.len()), 10);
348        }
349
350        // Clean up
351        clear_mock_host_bindings();
352    }
353
354    #[test]
355    fn test_buffer_operations_with_mock() {
356        let mut mock = MockHostBindings::new();
357
358        // Mock parent_ldgr_hash to write test data
359        mock.expect_parent_ldgr_hash()
360            .times(1)
361            .returning(|out_buff_ptr, out_buff_len| {
362                if out_buff_len >= 32 {
363                    unsafe {
364                        // Write test hash data
365                        for i in 0..32 {
366                            *out_buff_ptr.add(i) = (i * 2) as u8;
367                        }
368                    }
369                    32 // Return bytes written
370                } else {
371                    -1 // Buffer too small error
372                }
373            });
374
375        // Test it
376        let mut buffer = [0u8; 32];
377        unsafe {
378            let result = mock.parent_ldgr_hash(buffer.as_mut_ptr(), buffer.len());
379            assert_eq!(result, 32);
380
381            // Verify the mock wrote the expected data
382            for (i, _) in buffer.iter().enumerate() {
383                assert_eq!(buffer[i], (i * 2) as u8);
384            }
385        }
386    }
387
388    #[test]
389    fn test_trace_functions_with_mock() {
390        let mut mock = MockHostBindings::new();
391
392        mock.expect_trace()
393            .times(2)
394            .returning(|_msg_ptr, _msg_len, _data_type, _data_ptr, _data_len| ());
395
396        let message = b"Test message";
397        let data = b"Test data";
398        let number = 42i64.to_le_bytes();
399
400        unsafe {
401            mock.trace(
402                message.as_ptr(),
403                message.len(),
404                TraceDataType::AsText as i32,
405                data.as_ptr(),
406                data.len(),
407            );
408
409            mock.trace(
410                message.as_ptr(),
411                message.len(),
412                TraceDataType::Int64 as i32,
413                number.as_ptr(),
414                number.len(),
415            );
416        }
417    }
418
419    #[test]
420    fn test_id_functions_with_mock() {
421        let mut mock = MockHostBindings::new();
422
423        // Mock accountroot_id to return a test ledger entry ID
424        mock.expect_accountroot_id().times(1).returning(
425            |_account_ptr, _account_len, out_buff_ptr, out_buff_len| {
426                if out_buff_len >= 32 {
427                    unsafe {
428                        // Write a test ledger entry ID (32 bytes of 0xAA)
429                        for i in 0..32 {
430                            *out_buff_ptr.add(i) = 0xAA;
431                        }
432                    }
433                    32
434                } else {
435                    -1
436                }
437            },
438        );
439
440        // Test ledger entry ID generation
441        let account = [0u8; 20]; // Mock account ID
442        let mut id_buffer = [0u8; 32];
443
444        unsafe {
445            let result = mock.accountroot_id(
446                account.as_ptr(),
447                account.len(),
448                id_buffer.as_mut_ptr(),
449                id_buffer.len(),
450            );
451
452            assert_eq!(result, 32);
453            assert_eq!(id_buffer, [0xAA; 32]);
454        }
455    }
456
457    #[test]
458    fn test_error_conditions_with_mock() {
459        let mut mock = MockHostBindings::new();
460
461        // Mock a function to return an error code
462        mock.expect_ldgr_index().times(1).returning(|_, _| -1); // Return error
463
464        mock.expect_parent_ldgr_hash()
465            .times(1)
466            .returning(|_out_buff_ptr, _out_buff_len| -2); // Buffer too small
467
468        unsafe {
469            // Test error conditions
470            let mut buffer = [0u8; 32];
471            assert_eq!(mock.ldgr_index(buffer.as_mut_ptr(), buffer.len()), -1);
472
473            let mut small_buffer = [0u8; 16]; // Too small buffer
474            let result = mock.parent_ldgr_hash(small_buffer.as_mut_ptr(), small_buffer.len());
475            assert_eq!(result, -2);
476        }
477    }
478
479    #[test]
480    fn test_generic_function_with_mock() {
481        // Example of testing a function that takes HostBindings as a parameter
482        fn get_ledger_info<H: HostBindings>(host: &H) -> (i32, i32, i32) {
483            let mut buffer = [0u8; 32];
484            unsafe {
485                let sqn = host.ldgr_index(buffer.as_mut_ptr(), buffer.len());
486                let time = host.parent_ldgr_time(buffer.as_mut_ptr(), buffer.len());
487                let fee = host.base_fee(buffer.as_mut_ptr(), buffer.len());
488                (sqn, time, fee)
489            }
490        }
491
492        let mut mock = MockHostBindings::new();
493
494        mock.expect_ldgr_index().returning(|_, _| 999);
495        mock.expect_parent_ldgr_time().returning(|_, _| 888);
496        mock.expect_base_fee().returning(|_, _| 777);
497
498        let (sqn, time, fee) = get_ledger_info(&mock);
499        assert_eq!(sqn, 999);
500        assert_eq!(time, 888);
501        assert_eq!(fee, 777);
502    }
503}