Skip to main content

xrpl_wasm_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_get_ledger_sqn()
41        .returning(|_, out_buff_len| out_buff_len as i32);
42    mock.expect_get_parent_ledger_time()
43        .returning(|_, out_buff_len| out_buff_len as i32);
44    mock.expect_get_base_fee()
45        .returning(|_, out_buff_len| out_buff_len as i32);
46
47    // Functions that return buffer length
48    mock.expect_get_parent_ledger_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_ledger_obj()
53        .returning(|_, keylet_len, _| keylet_len as i32);
54    mock.expect_get_tx_field()
55        .returning(|_, _, out_buff_len| out_buff_len as i32);
56    mock.expect_get_current_ledger_obj_field()
57        .returning(|_, _, out_buff_len| out_buff_len as i32);
58    mock.expect_get_ledger_obj_field()
59        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
60    mock.expect_get_tx_nested_field()
61        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
62    mock.expect_get_current_ledger_obj_nested_field()
63        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
64    mock.expect_get_ledger_obj_nested_field()
65        .returning(|_, _, _, _, out_buff_len| out_buff_len as i32);
66
67    // Array length functions
68    mock.expect_get_tx_array_len().returning(|_| 0);
69    mock.expect_get_current_ledger_obj_array_len()
70        .returning(|_| 0);
71    mock.expect_get_ledger_obj_array_len().returning(|_, _| 0);
72    mock.expect_get_tx_nested_array_len().returning(|_, _| 0);
73    // Note: These two return locator_len, not 0
74    mock.expect_get_current_ledger_obj_nested_array_len()
75        .returning(|_, locator_len| locator_len as i32);
76    mock.expect_get_ledger_obj_nested_array_len()
77        .returning(|_, _, locator_len| locator_len as i32);
78
79    // Update and crypto functions
80    mock.expect_update_data()
81        .returning(|_, data_len| data_len as i32);
82    mock.expect_compute_sha512_half()
83        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
84    mock.expect_check_sig().returning(|_, _, _, _, _, _| 0);
85
86    // Keylet functions - all return buffer length
87    mock.expect_account_keylet()
88        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
89    mock.expect_amm_keylet()
90        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
91    mock.expect_check_keylet()
92        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
93    mock.expect_credential_keylet()
94        .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
95    mock.expect_delegate_keylet()
96        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
97    mock.expect_deposit_preauth_keylet()
98        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
99    mock.expect_did_keylet()
100        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
101    mock.expect_escrow_keylet()
102        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
103    mock.expect_line_keylet()
104        .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
105    mock.expect_mpt_issuance_keylet()
106        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
107    mock.expect_mptoken_keylet()
108        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
109    mock.expect_nft_offer_keylet()
110        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
111    mock.expect_offer_keylet()
112        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
113    mock.expect_oracle_keylet()
114        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
115    mock.expect_paychan_keylet()
116        .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
117    mock.expect_permissioned_domain_keylet()
118        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
119    mock.expect_signers_keylet()
120        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
121    mock.expect_ticket_keylet()
122        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
123    mock.expect_vault_keylet()
124        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
125
126    // NFT functions
127    mock.expect_get_nft()
128        .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
129    mock.expect_get_nft_issuer()
130        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
131    mock.expect_get_nft_taxon()
132        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
133    mock.expect_get_nft_flags()
134        .returning(|_, nft_id_len| nft_id_len as i32);
135    mock.expect_get_nft_transfer_fee()
136        .returning(|_, nft_id_len| nft_id_len as i32);
137    mock.expect_get_nft_serial()
138        .returning(|_, _, _, out_buff_len| out_buff_len as i32);
139
140    // Float functions
141    mock.expect_float_from_int()
142        .returning(|_, _, out_buff_len, _| out_buff_len as i32);
143    mock.expect_float_from_uint()
144        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
145    mock.expect_float_from_mant_exp()
146        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
147    mock.expect_float_from_stamount()
148        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
149    mock.expect_float_from_stnumber()
150        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
151    mock.expect_float_to_int()
152        .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
153    mock.expect_float_to_mant_exp()
154        .returning(|_, _, _, _, _, _| 8);
155    mock.expect_float_compare().returning(|_, _, _, _| 0);
156    mock.expect_float_add()
157        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
158    mock.expect_float_subtract()
159        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
160    mock.expect_float_multiply()
161        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
162    mock.expect_float_divide()
163        .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
164    mock.expect_float_pow()
165        .returning(|_, _, _, _, out_buff_len, _| out_buff_len as i32);
166    mock.expect_float_root()
167        .returning(|_, _, _, _, out_buff_len, _| out_buff_len as i32);
168
169    // Helper to calculate sum of two lengths, clamping to i32::MAX
170    let sum_lengths = |len1: usize, len2: usize| -> i32 {
171        len1.saturating_add(len2).min(i32::MAX as usize) as i32
172    };
173
174    // Trace functions - return sum of lengths (matching old host_bindings_for_testing.rs)
175    mock.expect_trace()
176        .returning(move |_, msg_len, _, data_len, _| sum_lengths(msg_len, data_len));
177    mock.expect_trace_num()
178        .returning(move |_, msg_len, _| sum_lengths(msg_len, 8));
179    mock.expect_trace_account()
180        .returning(move |_, msg_len, _, acc_len| sum_lengths(msg_len, acc_len));
181    mock.expect_trace_opaque_float()
182        .returning(move |_, msg_len, _, float_len| sum_lengths(msg_len, float_len));
183    mock.expect_trace_amount()
184        .returning(move |_, msg_len, _, amt_len| sum_lengths(msg_len, amt_len));
185}
186
187// #[cfg(test)]
188#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
189thread_local! {
190    static MOCK_STATE: RefCell<Option<MockHostBindings>> = RefCell::new(Some(create_default_mock()));
191}
192
193// Helper functions to manage the mock state
194#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
195pub fn set_mock_host_bindings(mock: MockHostBindings) {
196    MOCK_STATE.with(|state| {
197        *state.borrow_mut() = Some(mock);
198    });
199}
200
201#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
202pub fn clear_mock_host_bindings() {
203    MOCK_STATE.with(|state| {
204        *state.borrow_mut() = None;
205    });
206}
207
208#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
209// Macro to generate stub functions for non-WASM targets
210// These functions delegate to the MockHostBindings in MOCK_STATE
211macro_rules! export_host_functions {
212    ($(
213        $(#[$attr:meta])*
214        fn $name:ident($($param:ident: $param_ty:ty),*) -> $ret:ty;
215    )*) => {
216        $(
217            #[allow(clippy::too_many_arguments)]
218            #[allow(clippy::missing_safety_doc)]
219            $(#[$attr])*
220            pub unsafe fn $name($($param: $param_ty),*) -> $ret {
221                MOCK_STATE.with(|state|  {
222                    // The mock should always be present due to default initialization
223                    // If it's not, panic with a clear error message
224                    let mock = state.borrow();
225                    let mock_ref = mock.as_ref().expect("MockHostBindings not initialized");
226                    unsafe { mock_ref.$name($($param),*) }
227                })
228            }
229        )*
230    };
231}
232
233// Re-export all host functions as public functions for use by the rest of the codebase
234// For non-WASM targets, these are stub implementations that panic
235// The actual test implementations using MockHostBindings are in the tests module below
236
237// Generate all the stub functions
238export_host_functions! {
239    // Host Function Category: ledger and transaction info
240    fn get_ledger_sqn(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
241    fn get_parent_ledger_time(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
242    fn get_parent_ledger_hash(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
243    fn get_base_fee(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
244    fn amendment_enabled(amendment_ptr: *const u8, amendment_len: usize) -> i32;
245    fn cache_ledger_obj(keylet_ptr: *const u8, keylet_len: usize, cache_num: i32) -> i32;
246    fn get_tx_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
247    fn get_current_ledger_obj_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
248    fn get_ledger_obj_field(cache_num: i32, field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
249    fn get_tx_nested_field(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
250    fn get_current_ledger_obj_nested_field(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
251    fn get_ledger_obj_nested_field(cache_num: i32, locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
252    fn get_tx_array_len(field: i32) -> i32;
253    fn get_current_ledger_obj_array_len(field: i32) -> i32;
254    fn get_ledger_obj_array_len(cache_num: i32, field: i32) -> i32;
255    fn get_tx_nested_array_len(locator_ptr: *const u8, locator_len: usize) -> i32;
256    fn get_current_ledger_obj_nested_array_len(locator_ptr: *const u8, locator_len: usize) -> i32;
257    fn get_ledger_obj_nested_array_len(cache_num: i32, locator_ptr: *const u8, locator_len: usize) -> i32;
258
259    // Host Function Category: update current ledger entry
260    fn update_data(data_ptr: *const u8, data_len: usize) -> i32;
261
262    // Host Function Category: hash and keylet computation
263    fn compute_sha512_half(data_ptr: *const u8, data_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
264    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;
265    fn account_keylet(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
266    fn amm_keylet(issue1_ptr: *const u8, issue1_len: usize, issue2_ptr: *const u8, issue2_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
267    fn check_keylet(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
268    fn credential_keylet(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;
269    fn delegate_keylet(account_ptr: *const u8, account_len: usize, authorize_ptr: *const u8, authorize_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
270    fn deposit_preauth_keylet(account_ptr: *const u8, account_len: usize, authorize_ptr: *const u8, authorize_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
271    fn did_keylet(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
272    fn escrow_keylet(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
273    fn line_keylet(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;
274    fn mpt_issuance_keylet(issuer_ptr: *const u8, issuer_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
275    fn mptoken_keylet(mptid_ptr: *const u8, mptid_len: usize, holder_ptr: *const u8, holder_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
276    fn nft_offer_keylet(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
277    fn offer_keylet(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
278    fn oracle_keylet(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;
279    fn paychan_keylet(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;
280    fn permissioned_domain_keylet(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 signers_keylet(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
282    fn ticket_keylet(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
283    fn vault_keylet(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
285    // Host Function Category: NFT
286    fn get_nft(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;
287    fn get_nft_issuer(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
288    fn get_nft_taxon(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
289    fn get_nft_flags(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
290    fn get_nft_transfer_fee(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
291    fn get_nft_serial(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
292
293    // Host Function Category: FLOAT
294    fn float_from_int(in_int: i64, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
295    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;
296    fn float_from_mant_exp(mantissa: i64, exponent: i32, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
297    fn float_from_stamount(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
298    fn float_from_stnumber(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
299    fn float_to_int(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
300    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;
301    fn float_compare(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize) -> i32;
302    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;
303    fn float_subtract(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;
304    fn float_multiply(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;
305    fn float_divide(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;
306    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;
307    fn float_root(in_buff: *const u8, in_buff_len: usize, root: i32, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
308
309    // Host Function Category: TRACE
310    fn trace(msg_read_ptr: *const u8, msg_read_len: usize, data_read_ptr: *const u8, data_read_len: usize, as_hex: i32) -> i32;
311    fn trace_num(msg_read_ptr: *const u8, msg_read_len: usize, number: i64) -> i32;
312    fn trace_account(msg_read_ptr: *const u8, msg_read_len: usize, account_ptr: *const u8, account_len: usize) -> i32;
313    fn trace_opaque_float(msg_read_ptr: *const u8, msg_read_len: usize, opaque_float_ptr: *const u8, opaque_float_len: usize) -> i32;
314    fn trace_amount(msg_read_ptr: *const u8, msg_read_len: usize, amount_ptr: *const u8, amount_len: usize) -> i32;
315
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn test_ledger_functions_with_mock() {
324        let mut mock = MockHostBindings::new();
325
326        // Set up expectations - these functions now take buffer parameters
327        mock.expect_get_ledger_sqn()
328            .times(1)
329            .returning(|_, _| 12345);
330        mock.expect_get_parent_ledger_time()
331            .times(1)
332            .returning(|_, _| 1234567890);
333        mock.expect_get_base_fee().times(1).returning(|_, _| 10);
334
335        // Set the mock in thread-local storage
336        set_mock_host_bindings(mock);
337
338        // Test the exported functions (they will use the mock)
339        let mut buffer = [0u8; 32];
340        unsafe {
341            assert_eq!(get_ledger_sqn(buffer.as_mut_ptr(), buffer.len()), 12345);
342            assert_eq!(
343                get_parent_ledger_time(buffer.as_mut_ptr(), buffer.len()),
344                1234567890
345            );
346            assert_eq!(get_base_fee(buffer.as_mut_ptr(), buffer.len()), 10);
347        }
348
349        // Clean up
350        clear_mock_host_bindings();
351    }
352
353    #[test]
354    fn test_buffer_operations_with_mock() {
355        let mut mock = MockHostBindings::new();
356
357        // Mock get_parent_ledger_hash to write test data
358        mock.expect_get_parent_ledger_hash()
359            .times(1)
360            .returning(|out_buff_ptr, out_buff_len| {
361                if out_buff_len >= 32 {
362                    unsafe {
363                        // Write test hash data
364                        for i in 0..32 {
365                            *out_buff_ptr.add(i) = (i * 2) as u8;
366                        }
367                    }
368                    32 // Return bytes written
369                } else {
370                    -1 // Buffer too small error
371                }
372            });
373
374        // Test it
375        let mut buffer = [0u8; 32];
376        unsafe {
377            let result = mock.get_parent_ledger_hash(buffer.as_mut_ptr(), buffer.len());
378            assert_eq!(result, 32);
379
380            // Verify the mock wrote the expected data
381            for (i, _) in buffer.iter().enumerate() {
382                assert_eq!(buffer[i], (i * 2) as u8);
383            }
384        }
385    }
386
387    #[test]
388    fn test_trace_functions_with_mock() {
389        let mut mock = MockHostBindings::new();
390
391        // Mock trace function
392        mock.expect_trace().times(1).returning(
393            |_msg_ptr, msg_len, _data_ptr, data_len, _as_hex| (msg_len + data_len) as i32,
394        );
395
396        // Mock trace_num function
397        mock.expect_trace_num()
398            .times(1)
399            .returning(|_msg_ptr, msg_len, _number| msg_len as i32);
400
401        // Test trace functions
402        let message = b"Test message";
403        let data = b"Test data";
404
405        unsafe {
406            let result = mock.trace(
407                message.as_ptr(),
408                message.len(),
409                data.as_ptr(),
410                data.len(),
411                0,
412            );
413            assert_eq!(result, (message.len() + data.len()) as i32);
414
415            let result = mock.trace_num(message.as_ptr(), message.len(), 42);
416            assert_eq!(result, message.len() as i32);
417        }
418    }
419
420    #[test]
421    fn test_keylet_functions_with_mock() {
422        let mut mock = MockHostBindings::new();
423
424        // Mock account_keylet to return a test keylet
425        mock.expect_account_keylet().times(1).returning(
426            |_account_ptr, _account_len, out_buff_ptr, out_buff_len| {
427                if out_buff_len >= 32 {
428                    unsafe {
429                        // Write a test keylet (32 bytes of 0xAA)
430                        for i in 0..32 {
431                            *out_buff_ptr.add(i) = 0xAA;
432                        }
433                    }
434                    32
435                } else {
436                    -1
437                }
438            },
439        );
440
441        // Test keylet generation
442        let account = [0u8; 20]; // Mock account ID
443        let mut keylet_buffer = [0u8; 32];
444
445        unsafe {
446            let result = mock.account_keylet(
447                account.as_ptr(),
448                account.len(),
449                keylet_buffer.as_mut_ptr(),
450                keylet_buffer.len(),
451            );
452
453            assert_eq!(result, 32);
454            assert_eq!(keylet_buffer, [0xAA; 32]);
455        }
456    }
457
458    #[test]
459    fn test_error_conditions_with_mock() {
460        let mut mock = MockHostBindings::new();
461
462        // Mock a function to return an error code
463        mock.expect_get_ledger_sqn().times(1).returning(|_, _| -1); // Return error
464
465        mock.expect_get_parent_ledger_hash()
466            .times(1)
467            .returning(|_out_buff_ptr, _out_buff_len| -2); // Buffer too small
468
469        unsafe {
470            // Test error conditions
471            let mut buffer = [0u8; 32];
472            assert_eq!(mock.get_ledger_sqn(buffer.as_mut_ptr(), buffer.len()), -1);
473
474            let mut small_buffer = [0u8; 16]; // Too small buffer
475            let result = mock.get_parent_ledger_hash(small_buffer.as_mut_ptr(), small_buffer.len());
476            assert_eq!(result, -2);
477        }
478    }
479
480    #[test]
481    fn test_generic_function_with_mock() {
482        // Example of testing a function that takes HostBindings as a parameter
483        fn get_ledger_info<H: HostBindings>(host: &H) -> (i32, i32, i32) {
484            let mut buffer = [0u8; 32];
485            unsafe {
486                let sqn = host.get_ledger_sqn(buffer.as_mut_ptr(), buffer.len());
487                let time = host.get_parent_ledger_time(buffer.as_mut_ptr(), buffer.len());
488                let fee = host.get_base_fee(buffer.as_mut_ptr(), buffer.len());
489                (sqn, time, fee)
490            }
491        }
492
493        let mut mock = MockHostBindings::new();
494
495        mock.expect_get_ledger_sqn().returning(|_, _| 999);
496        mock.expect_get_parent_ledger_time().returning(|_, _| 888);
497        mock.expect_get_base_fee().returning(|_, _| 777);
498
499        let (sqn, time, fee) = get_ledger_info(&mock);
500        assert_eq!(sqn, 999);
501        assert_eq!(time, 888);
502        assert_eq!(fee, 777);
503    }
504}