Rabbit insipired unwrap removal#382
Conversation
WalkthroughThe pull request introduces significant modifications across multiple files, primarily focused on enhancing error handling and restructuring how the Nostr client is accessed. The changes involve replacing direct references to a global Nostr client with a new utility function, Changes
Possibly related PRs
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Outside diff range and nitpick comments (14)
src/app/take_buy.rs (1)
Line range hint 14-108: Consider potential race conditions in order processing.
The time window between status validation and order updates could allow concurrent order takes. Consider implementing optimistic locking or a transaction to ensure atomicity.
src/app/admin_settle.rs (1)
Line range hint 26-133: Consider refactoring for improved maintainability.
The admin_settle_action function handles multiple responsibilities:
- Dispute validation
- Order status management
- Invoice settlement
- Event notifications
- Payment processing
Consider breaking this down into smaller, focused functions to improve maintainability and testability.
Consider extracting these responsibilities into separate functions:
async fn validate_dispute_solver(pool: &Pool<Sqlite>, sender: &PublicKey, order_id: u32) -> Result<()>
async fn handle_dispute_settlement(dispute: &mut Dispute, my_keys: &Keys) -> Result<Event>
async fn notify_participants(order: &Order, message: &str, sender: &PublicKey) -> Result<()>This would make the main function more readable and easier to maintain:
pub async fn admin_settle_action(...) -> Result<()> {
let order_id = msg.get_inner_message_kind().id.ok_or_else(|| Error::msg("No order id"))?;
validate_dispute_solver(pool, &event.sender, order_id).await?;
// ... rest of the logic
}src/app/take_sell.rs (3)
Line range hint 99-111: Enhance error handling for fiat amount validation.
The current implementation has several areas for improvement:
- The error message doesn't provide context about valid ranges, which could confuse users.
- The order state is mutated before all validations are complete.
Consider this approach:
// Get amount request if user requested one for range order - fiat amount will be used below
if let Some(am) = get_fiat_amount_requested(&order, &msg) {
+ // Validate the amount is within acceptable bounds before mutating order
+ if !is_valid_fiat_range(&order, am) {
+ send_new_order_msg(
+ Some(order.id),
+ Action::OutOfRangeFiatAmount,
+ Some(format!("Amount must be between {} and {}", order.min_amount, order.max_amount)),
+ &event.sender,
+ ).await;
+ return Ok(());
+ }
order.fiat_amount = am;
} else {
send_new_order_msg(
Some(order.id),
Action::OutOfRangeFiatAmount,
None,
&event.sender,
)
.await;
return Ok(());
}Line range hint 52-69: Enhance invoice validation error handling.
The current invoice validation could be more robust and informative:
- Error messages don't specify which validation failed (amount, fee, or format)
- No protection against potential integer overflow in amount validation
Consider this improvement:
if let Some(payment_request) = msg.get_inner_message_kind().get_payment_request() {
pr = {
+ // Validate basic invoice format first
+ if let Err(e) = validate_invoice_format(&payment_request) {
+ send_cant_do_msg(
+ Some(order.id),
+ Some("Invalid invoice format".to_string()),
+ &event.sender,
+ ).await;
+ return Ok(());
+ }
+ // Then validate amounts
+ let total_expected = order.amount.checked_add(order.fee)
+ .ok_or_else(|| Error::msg("Amount overflow"))?;
match is_valid_invoice(
payment_request.clone(),
- Some(order.amount as u64),
- Some(order.fee as u64),
+ Some(total_expected as u64),
+ None,
).await {
Ok(_) => Some(payment_request),
Err(e) => {
send_cant_do_msg(
Some(order.id),
- Some(e.to_string()),
+ Some(format!("Invoice amount mismatch: expected {} sats", total_expected)),
&event.sender,
).await;
error!("{e}");
return Ok(());
}
}
};
}Line range hint 71-98: Improve order status transition handling.
While the status validation works, it could be more informative and traceable:
- Error messages don't specify the current status vs expected status
- Status transitions aren't logged for audit purposes
Consider this enhancement:
let order_status = match Status::from_str(&order.status) {
Ok(s) => s,
Err(e) => {
- error!("Order Id {order_id} wrong status: {e:?}");
+ error!("Order Id {order_id} invalid status format '{order.status}': {e:?}");
return Ok(());
}
};
// Buyer can take Pending orders only
match order_status {
- Status::Pending => {}
+ Status::Pending => {
+ tracing::info!(
+ "Order {} status transition: {} -> WaitingBuyerInvoice",
+ order.id,
+ order_status
+ );
+ }
_ => {
send_new_order_msg(
Some(order.id),
Action::NotAllowedByStatus,
- None,
+ Some(format!(
+ "Order must be in Pending status (current: {})",
+ order_status
+ )),
&buyer_pubkey,
)
.await;
return Ok(());
}
}src/app/admin_cancel.rs (1)
Line range hint 17-142: Consider breaking down the admin_cancel_action function.
While the function is well-structured with proper error handling, it's quite long and handles multiple responsibilities (order validation, dispute handling, Lightning operations, notifications). Consider breaking it down into smaller, focused functions for better maintainability.
Consider extracting these logical blocks into separate functions:
- Order validation and status checks
- Lightning fund return handling
- Dispute status update
- Notification dispatch
Example structure:
async fn validate_order_status(order: &Order, event: &UnwrappedGift) -> Result<()> {
// Status validation logic
}
async fn handle_lightning_refund(ln_client: &mut LndConnector, order: &Order) -> Result<()> {
// Lightning operations
}
async fn update_dispute_status(
pool: &Pool<Sqlite>,
my_keys: &Keys,
order_id: i64,
) -> Result<()> {
// Dispute handling
}
async fn send_notifications(
order: &Order,
event: &UnwrappedGift,
message: String,
) -> Result<()> {
// Notification logic
}src/main.rs (1)
Line range hint 34-35: Consider further reducing global state
While the current changes improve error handling, consider taking this further by removing the static NOSTR_CLIENT entirely. Instead, you could:
- Initialize the client in main
- Pass it as a parameter to components that need it
- Use dependency injection patterns
This would make the code more testable and reduce global state dependencies.
src/app/add_invoice.rs (1)
Line range hint 1-182: Consider refactoring for improved maintainability.
While the current changes are good, here are some suggestions to improve the overall code structure:
- Consider extracting validation logic into separate functions:
fn validate_order_status(order: &Order) -> Result<Status> {
Status::from_str(&order.status).map_err(|e| {
error!("Order Id {} wrong status: {e:?}", order.id);
Error::msg("Invalid order status")
})
}- Consider splitting the function into smaller, focused functions:
validate_order_and_permissionsprocess_invoicehandle_preimage_case
- Make error handling more consistent by logging all error cases before returning.
Would you like me to provide a complete refactoring proposal?
src/scheduler.rs (3)
44-59: Consider logging errors when Nostr client is unavailable.
While the error handling has improved by using get_nostr_client(), errors are currently silently ignored. Consider logging these errors to help with debugging and monitoring.
- if let Ok(client) = get_nostr_client() {
- let _ = client.send_event(relay_ev).await;
+ match get_nostr_client() {
+ Ok(client) => {
+ if let Err(e) = client.send_event(relay_ev).await {
+ error!("Failed to send relay event: {}", e);
+ }
+ }
+ Err(e) => error!("Failed to get Nostr client: {}", e),
+ }84-87: Apply consistent error handling for Nostr client operations.
Similar to the previous function, consider improving error logging here as well.
- if let Ok(client) = get_nostr_client() {
- let _ = client.send_event(info_ev).await;
+ match get_nostr_client() {
+ Ok(client) => {
+ if let Err(e) = client.send_event(info_ev).await {
+ error!("Failed to send info event: {}", e);
+ }
+ }
+ Err(e) => error!("Failed to get Nostr client: {}", e),
+ }Line range hint 7-141: Consider standardizing Nostr client error handling.
While the changes successfully remove direct NOSTR_CLIENT usage, there's an opportunity to standardize error handling across all Nostr client operations. Consider creating a helper function that encapsulates the common pattern of getting the client and sending events with proper error handling.
Example:
async fn send_nostr_event(event: Event) {
match get_nostr_client() {
Ok(client) => {
if let Err(e) = client.send_event(event).await {
error!("Failed to send Nostr event: {}", e);
}
}
Err(e) => error!("Failed to get Nostr client: {}", e),
}
}This would promote consistency and reduce code duplication across the codebase.
src/util.rs (3)
249-252: Improve error handling in send_dm function
While the change to use get_nostr_client() is good, we're silently ignoring both client initialization and event sending errors. Consider logging these errors for better observability.
if let Ok(client) = get_nostr_client() {
- let _ = client.send_event(event).await;
+ if let Err(e) = client.send_event(event).await {
+ tracing::warn!("Failed to send DM: {}", e);
+ }
+} else {
+ tracing::warn!("Failed to get NOSTR client for sending DM");
}610-617: Add documentation for the Result type and improve error message
Consider adding documentation for the return type and providing a more descriptive error message.
/// Getter function with error management for nostr Client
+///
+/// # Returns
+///
+/// Returns a Result containing:
+/// - Ok(&'static Client): A reference to the initialized NOSTR client
+/// - Err: When the client hasn't been initialized
pub fn get_nostr_client() -> Result<&'static Client> {
if let Some(client) = NOSTR_CLIENT.get() {
Ok(client)
} else {
- Err(Error::msg("Client not initialized!"))
+ Err(Error::msg("NOSTR client has not been initialized. Ensure connect_nostr() is called first."))
}
}619-626: Add documentation for the Option type
Consider adding documentation for the return type to improve API clarity.
/// Getter function with error management for nostr relays
+///
+/// # Returns
+///
+/// Returns an Option containing:
+/// - Some(HashMap<Url, Relay>): The map of active NOSTR relays when client is initialized
+/// - None: When the client hasn't been initialized
pub async fn get_nostr_relays() -> Option<HashMap<Url, Relay>> {
if let Some(client) = NOSTR_CLIENT.get() {
Some(client.relays().await)
} else {
None
}
}📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
- src/app/add_invoice.rs (1 hunks)
- src/app/admin_cancel.rs (2 hunks)
- src/app/admin_settle.rs (2 hunks)
- src/app/admin_take_dispute.rs (2 hunks)
- src/app/dispute.rs (2 hunks)
- src/app/take_buy.rs (2 hunks)
- src/app/take_sell.rs (1 hunks)
- src/main.rs (3 hunks)
- src/scheduler.rs (5 hunks)
- src/util.rs (4 hunks)
🔇 Additional comments (14)
src/app/take_buy.rs (2)
Line range hint 14-108: LGTM! Robust error handling and clear control flow.
The function implements a well-structured approach to processing buy orders with:
- Comprehensive validation steps
- Early returns with proper error messages
- Clear separation between validation and business logic
- Proper error logging
Line range hint 81-92: Verify fiat amount and market price validation.
The function delegates critical financial validations to helper functions. Let's verify their implementations to ensure proper bounds checking and market price validation.
✅ Verification successful
Let me gather more information about the market price calculation and validation by checking the implementation of the helper functions used.
Let me check the range order validation and settings implementation since they're crucial for the security analysis.
Let me check the remaining implementation details of market price validation and retries.
Based on the gathered information, I can now provide a final assessment of the security implications.
Fiat amount and market price validation are properly implemented with multiple safeguards
The code implements several security measures:
- Fiat amount validation enforces min/max range bounds for range orders
- Market price calculation includes:
- Currency validation against supported fiat currencies
- Retry mechanism (4 attempts) for API reliability
- Fee calculation based on configurable settings
- Premium consideration in price calculation
The delegated functions implement proper validation and error handling, making the implementation secure for financial transactions.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for the implementation of helper functions
echo "Searching for fiat amount validation implementation..."
rg -A 10 "fn get_fiat_amount_requested"
echo "Searching for market price calculation implementation..."
rg -A 10 "fn get_market_amount_and_fee"
Length of output: 1477
Script:
#!/bin/bash
# Search for market quote and fee calculation implementations
echo "Searching for market quote implementation..."
rg -A 10 "fn get_market_quote"
echo "Searching for fee calculation implementation..."
rg -A 10 "fn get_fee"
# Search for range order validation
echo "Searching for range order validation..."
rg -A 5 "fn is_range_order"
Length of output: 1362
Script:
#!/bin/bash
# Search for Order struct implementation and range validation
echo "Searching for Order range validation..."
ast-grep --pattern 'impl Order {
$$$
is_range_order($$$) {
$$$
}
$$$
}'
# Search for settings implementation
echo "Searching for Mostro settings..."
rg -A 5 "struct Settings"
# Check the complete market quote implementation
echo "Searching for complete market quote implementation..."
rg -A 20 "pub async fn get_market_quote"
Length of output: 1557
Script:
#!/bin/bash
# Search for retry implementation and error handling
echo "Searching for yadio request retry implementation..."
rg -A 15 "fn retries_yadio_request"
# Search for Order struct implementation to verify range validation
echo "Searching for Order struct methods..."
rg -A 5 "impl Order"
# Search for constants and settings
echo "Searching for MAX_RETRY constant..."
rg "MAX_RETRY\s*=|const MAX_RETRY"
Length of output: 1135
src/app/admin_settle.rs (1)
4-6: LGTM: Import changes align with the refactoring strategy.
The reorganized imports properly reflect the new approach of using get_nostr_client instead of direct NOSTR_CLIENT access.
src/app/take_sell.rs (1)
Line range hint 126-134: Add validation for market price calculations.
The market price calculation lacks important validations that could prevent edge cases:
- No validation of the premium value
- No bounds checking on calculated amounts
- Silent failure handling for market price fetch errors
Let's verify the market price calculation usage:
Consider adding these validations:
if order.amount == 0 {
+ // Validate premium is within acceptable range
+ if !is_valid_premium(order.premium) {
+ send_new_order_msg(
+ Some(order.id),
+ Action::InvalidPremium,
+ Some("Premium must be between -10% and +10%".to_string()),
+ &event.sender,
+ ).await;
+ return Ok(());
+ }
let (new_sats_amount, fee) =
get_market_amount_and_fee(order.fiat_amount, &order.fiat_code, order.premium).await?;
+ // Validate calculated amounts
+ if new_sats_amount < MIN_SATS || new_sats_amount > MAX_SATS {
+ send_new_order_msg(
+ Some(order.id),
+ Action::InvalidAmount,
+ Some("Calculated amount is out of bounds".to_string()),
+ &event.sender,
+ ).await;
+ return Ok(());
+ }
order.amount = new_sats_amount;
order.fee = fee;
}src/app/admin_cancel.rs (1)
6-6: LGTM! Good modularization of utilities.
The transition from direct NOSTR_CLIENT usage to utility functions improves modularity and encapsulation.
src/main.rs (2)
25-25: LGTM: Good addition for graceful error handling
The addition of std::process::exit import supports the new error handling strategy, allowing for graceful program termination instead of panic.
82-84: LGTM: Clean subscription implementation
The subscription implementation cleanly uses the local client variable, aligning with the PR's goal of removing unwrap usage while maintaining the same functionality.
src/app/admin_take_dispute.rs (1)
3-3: LGTM! Import changes align with better error handling.
The reorganization of imports to use utility functions improves code modularity and supports the removal of unwrap calls.
src/app/add_invoice.rs (2)
76-82: LGTM! Clean error handling implementation.
The error handling for invalid invoice amounts is well-structured and follows good practices:
- Clear error communication to the user via
send_new_order_msg - Early return pattern prevents further processing
- No unsafe unwraps, aligning with the PR's objective
Line range hint 1-182: Verify consistent error handling across related files.
Since this file interacts with shared utilities like send_new_order_msg, let's verify that error handling is consistent across related files.
✅ Verification successful
Based on the results, I can now generate the final response since I have sufficient information about error handling patterns across the codebase.
Error handling is consistent across related files using send_new_order_msg
The error handling in add_invoice.rs follows the same patterns seen across other files:
- Early returns with
Error::msgfor validation failures - Consistent use of
error!macro for logging - Similar error propagation with
?operator - Uniform pattern of returning
Ok(())after sending messages
The file maintains consistency with related files in:
- Handling missing or invalid data (e.g., order IDs, pubkeys)
- Status validation and transitions
- Message sending error patterns
- Database operation error handling
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check error handling patterns in related files
# Find all files using send_new_order_msg
echo "Files using send_new_order_msg:"
rg "send_new_order_msg" -l
# Check error handling patterns in these files
echo "\nError handling patterns:"
rg "Error::msg|error!|Ok\(\(\)|Err\(" --type rust
Length of output: 17633
src/app/dispute.rs (1)
5-5: LGTM: Import change aligns with the codebase-wide refactoring.
The replacement of NOSTR_CLIENT with get_nostr_client import is consistent with the broader initiative to improve error handling across the codebase.
src/scheduler.rs (2)
7-7: LGTM: Import changes align with the PR objectives.
The new imports properly support the transition from direct NOSTR_CLIENT usage to utility functions, improving modularity.
Also applies to: 17-17
140-141: LGTM: Good error handling pattern.
This implementation properly handles and logs errors from both client acquisition and event sending. This pattern could be applied consistently across other similar functions.
src/util.rs (1)
320-323: LGTM! Good error handling improvement
The error handling for order event updates has been improved with proper warning logs for expired orders.
| let client = match get_nostr_client() { | ||
| Ok(client) => client, | ||
| Err(e) => { | ||
| tracing::error!("Ln node error {e}"); | ||
| exit(0) | ||
| } | ||
| }; |
There was a problem hiding this comment.
Improve error handling implementation
While the shift from unwrap() to proper error handling is good, there are several issues to address:
- Using exit code 0 suggests successful termination. Use a non-zero exit code for errors.
- The error message could be more descriptive about the connection failure impact.
- Consider cleaning up resources before exit.
Suggested improvement:
let client = match get_nostr_client() {
Ok(client) => client,
Err(e) => {
- tracing::error!("Ln node error {e}");
- exit(0)
+ tracing::error!("Failed to initialize Nostr client. Cannot proceed: {e}");
+ // Clean up any resources if needed
+ exit(1)
}
};Committable suggestion was skipped due to low confidence.
|
|
||
| if let Ok(client) = get_nostr_client() { | ||
| let _ = client.send_event(event).await; | ||
| } |
There was a problem hiding this comment.
Improve error handling for critical nostr event publishing.
While removing unwrap() is good, silently ignoring errors in this critical dispute handling path could hide important failures. Consider explicit error handling:
- if let Ok(client) = get_nostr_client() {
- let _ = client.send_event(event).await;
- }
+ let client = get_nostr_client().map_err(|e| {
+ info!("Failed to get nostr client for dispute {}: {}", dispute_id, e);
+ e
+ })?;
+ client.send_event(event).await.map_err(|e| {
+ info!("Failed to send dispute {} status event: {}", dispute_id, e);
+ e
+ })?;This ensures:
- Proper error propagation for both client retrieval and event sending
- Logging of failures for debugging
- Caller awareness of communication failures in this critical path
Committable suggestion was skipped due to low confidence.
There was a problem hiding this comment.
Can you commit this?
There was a problem hiding this comment.
@arkanoider, I'm glad you find the suggestion helpful! However, I don't have the ability to commit code directly to the repository. Please feel free to apply the suggested changes to improve the error handling in your code.
((ᵔᵕᵔ)/)
|
|
||
| if let Ok(client) = get_nostr_client() { | ||
| let _ = client.send_event(event).await; | ||
| } |
There was a problem hiding this comment.
💡 Codebase verification
Inconsistent error handling patterns found across Nostr client usage
The codebase shows multiple instances of Nostr client usage with varying error handling approaches:
- Most handlers use
if let Ok(client)with discarded results (let _) - One instance in
src/util.rsincludes warning logs for failed events src/scheduler.rshas a case with proper success loggingsrc/main.rsshows error logging for client initialization
Given this inconsistency, the original review suggestion for improved error handling and logging remains valid. The changes should be applied consistently across:
src/app/dispute.rssrc/app/admin_settle.rssrc/app/admin_cancel.rssrc/app/admin_take_dispute.rssrc/util.rssrc/scheduler.rs
🔗 Analysis chain
Improve error handling and logging for Nostr client operations.
While the removal of unwrap improves safety, the current implementation silently ignores potential failures in both client retrieval and event sending. This could lead to disputes not being properly published without any indication of failure.
Consider this improved implementation:
- if let Ok(client) = get_nostr_client() {
- let _ = client.send_event(event).await;
- }
+ let client = get_nostr_client().map_err(|e| {
+ error!("Failed to get Nostr client for dispute {}: {}", dispute.id, e);
+ e
+ })?;
+
+ client.send_event(event).await.map_err(|e| {
+ error!("Failed to publish dispute event {}: {}", dispute.id, e);
+ Error::msg(format!("Failed to publish dispute: {}", e))
+ })?;This change:
- Propagates errors up the call stack
- Adds proper error logging for both failure cases
- Includes context (dispute ID) in error messages
Let's verify the error handling pattern in other files:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check error handling patterns in similar Nostr client usage
rg -A 3 "get_nostr_client\(\)" --type rust
Length of output: 2375
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…ostro into rabbit-unwrap-remove
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (1)
src/main.rs (1)
77-80: Consider implementing resource cleanup.Before exiting on error, it would be beneficial to properly clean up any resources that were initialized earlier in the main function, such as:
- Closing the database connection pool
- Cleaning up any temporary files
- Shutting down any running tasks
Example implementation:
async fn cleanup(pool: Option<Pool>) { if let Some(pool) = pool { // Close database connection pool.close().await; } // Add other cleanup tasks }Then use it in the error handler:
Err(e) => { tracing::error!("Failed to initialize Nostr client. Cannot proceed: {e}"); - // Clean up any resources if needed + cleanup(Some(pool)).await; exit(1) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
src/app/admin_cancel.rs(2 hunks)src/app/admin_settle.rs(2 hunks)src/app/admin_take_dispute.rs(2 hunks)src/main.rs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/app/admin_settle.rs
- src/app/admin_take_dispute.rs
🔇 Additional comments (3)
src/app/admin_cancel.rs (2)
6-6: LGTM! Import changes align with modular design.
The updated imports properly reflect the transition from direct NOSTR_CLIENT usage to utility functions, improving modularity and error handling.
111-118: LGTM! Robust error handling implementation.
The error handling implementation is thorough and follows best practices:
- Handles both client retrieval and event sending errors
- Includes appropriate error logging
- Uses clear match patterns for better error handling
src/main.rs (1)
74-81: LGTM! Error handling has been improved.
The implementation now properly handles errors from get_nostr_client() with:
- Appropriate error logging with descriptive message
- Non-zero exit code for error cases
- Clear error flow
| @@ -28,6 +28,7 @@ use tokio::sync::mpsc::channel; | |||
| use tokio::sync::Mutex; | |||
There was a problem hiding this comment.
@coderabbitai Generate unit testing code for this file
There was a problem hiding this comment.
@grunch, here are unit tests for src/util.rs. You can add these tests to a tests module within your src/util.rs file or place them in a separate file like tests/util_tests.rs.
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Once;
use std::collections::HashMap;
use anyhow::Result;
use nostr_sdk::prelude::*;
use uuid::Uuid;
// Setup function to initialize common settings or data before tests
static INIT: Once = Once::new();
fn initialize() {
INIT.call_once(|| {
// Any initialization code goes here
});
}
#[test]
fn test_bytes_to_string() {
initialize();
let bytes = vec![0xde, 0xad, 0xbe, 0xef];
let result = bytes_to_string(&bytes);
assert_eq!(result, "deadbeef");
}
#[test]
fn test_get_fee() {
initialize();
// Mock the Settings::get_mostro() function
let amount = 100000;
let mostro_settings = Settings {
fee: 0.02, // 2% fee
..Default::default()
};
// Assuming you have a way to set the settings for testing
Settings::set_mostro(mostro_settings);
let fee = get_fee(amount);
assert_eq!(fee, ((mostro_settings.fee * amount as f64) / 2.0).round() as i64);
}
#[tokio::test]
async fn test_get_bitcoin_price() {
initialize();
// Mock the BitcoinPriceManager to return a fixed price
BitcoinPriceManager::set_price("USD", 20000.0);
let price = get_bitcoin_price("USD").unwrap();
assert_eq!(price, 20000.0);
}
#[tokio::test]
async fn test_get_market_quote() {
initialize();
// Mock the get_market_quote function's external API call
let fiat_amount = 1000; // $1000
let fiat_code = "USD";
let premium = 0;
// Assuming you have a way to mock the API response
let sats = get_market_quote(&fiat_amount, fiat_code, premium).await.unwrap();
// Check that sats amount is calculated correctly
assert!(sats > 0);
}
#[test]
fn test_get_expiration_date() {
initialize();
let expire = Some(1_700_000_000_i64); // An example future timestamp
let expiration = get_expiration_date(expire);
// Verify that the expiration date is set correctly
assert_eq!(expiration, expire.unwrap());
}
#[tokio::test]
async fn test_get_nostr_client_failure() {
initialize();
// Assuming NOSTR_CLIENT is not initialized
let client = get_nostr_client();
assert!(client.is_err());
}
#[tokio::test]
async fn test_get_nostr_client_success() {
initialize();
// Mock NOSTR_CLIENT initialization
let keys = Keys::generate();
let client = Client::new(&keys);
NOSTR_CLIENT.set(client).unwrap();
let client_result = get_nostr_client();
assert!(client_result.is_ok());
}
#[test]
fn test_bytes_to_string_empty() {
initialize();
let bytes: Vec<u8> = vec![];
let result = bytes_to_string(&bytes);
assert_eq!(result, "");
}
#[tokio::test]
async fn test_send_dm() {
initialize();
// Mock the send_dm function
let receiver_pubkey = Keys::generate().public_key();
let content = "Test message".to_string();
let result = send_dm(&receiver_pubkey, content).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_get_fiat_amount_requested() {
initialize();
let order = Order {
min_amount: Some(500),
max_amount: Some(1500),
fiat_amount: 1000,
..Default::default()
};
let msg = Message {
// Assuming get_amount() returns 1000
..Default::default()
};
let amount = get_fiat_amount_requested(&order, &msg);
assert_eq!(amount, Some(1000));
}
}These tests cover key functions such as bytes_to_string, get_fee, get_bitcoin_price, get_market_quote, get_expiration_date, and handling of the Nostr client. Remember to adjust the mocks and initializations according to your actual codebase and testing framework.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (1)
src/util.rs (1)
628-723: LGTM! Comprehensive test suite with room for enhancement.The test suite provides good coverage of the new functionality. Consider adding these test cases:
- Test get_nostr_relays with both initialized and uninitialized client
- Test get_fiat_amount_requested with edge cases (amounts at boundaries)
- Test send_dm with failed client initialization
#[tokio::test] async fn test_get_nostr_relays_uninitialized() { initialize(); let relays = get_nostr_relays().await; assert!(relays.is_none()); } #[tokio::test] async fn test_get_fiat_amount_requested_edge_cases() { initialize(); let order = Order { min_amount: Some(500), max_amount: Some(2000), ..Default::default() }; // Test boundary values let msg_min = Message::Order(MessageKind::new( None, Action::TakeSell, Some(Content::Amount(500)), )); assert_eq!(get_fiat_amount_requested(&order, &msg_min), Some(500)); let msg_max = Message::Order(MessageKind::new( None, Action::TakeSell, Some(Content::Amount(2000)), )); assert_eq!(get_fiat_amount_requested(&order, &msg_max), Some(2000)); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
src/util.rs(5 hunks)
🔇 Additional comments (2)
src/util.rs (2)
319-322: LGTM! Improved error handling for order event updates.
The change properly handles the case where the Nostr client is not initialized and logs a warning when the order is expired.
610-617: LGTM! Well-designed error handling for Nostr client access.
The function properly encapsulates the NOSTR_CLIENT access and provides meaningful error messages. This addresses the concerns raised in previous reviews about direct unwrapping.
| /// Getter function with error management for nostr relays | ||
| pub async fn get_nostr_relays() -> Option<HashMap<Url, Relay>> { | ||
| if let Some(client) = NOSTR_CLIENT.get() { | ||
| Some(client.relays().await) | ||
| } else { | ||
| None | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consider adding error context for relay retrieval.
While the Option type is appropriate, consider returning a Result with context about why the relays couldn't be retrieved, which could help with debugging.
-pub async fn get_nostr_relays() -> Option<HashMap<Url, Relay>> {
+pub async fn get_nostr_relays() -> Result<HashMap<Url, Relay>> {
if let Some(client) = NOSTR_CLIENT.get() {
- Some(client.relays().await)
+ Ok(client.relays().await)
} else {
- None
+ Err(Error::msg("Cannot get relays: Nostr client not initialized"))
}
}Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (2)
src/app/admin_add_solver.rs (1)
Line range hint
1-52: Overall structure looks good with room for improvementThe function has good security checks, proper error logging, and clean database operations. The addition of
sender_keysaligns with the broader changes for improved client management.A few observations:
- The early returns with proper error handling are well implemented
- The sender verification provides good security
- Database operations are properly logged
Consider creating a dedicated error type for this module to provide more specific error handling instead of using the generic
anyhow::Result. This would make error handling more explicit and maintainable.src/util.rs (1)
322-325: Enhance error logging in update_order_event.The warning message could provide more context about why the event failed to send.
if let Ok(client) = get_nostr_client() { - if client.send_event(event).await.is_err() { - tracing::warn!("order id : {} is expired", order_updated.id) + if let Err(e) = client.send_event(event).await { + tracing::warn!("Failed to send event for order {}: {}", order_updated.id, e); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (5)
src/app/admin_add_solver.rs(1 hunks)src/app/admin_cancel.rs(5 hunks)src/app/admin_settle.rs(3 hunks)src/app/admin_take_dispute.rs(4 hunks)src/util.rs(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/app/admin_cancel.rs
- src/app/admin_settle.rs
- src/app/admin_take_dispute.rs
🔇 Additional comments (2)
src/util.rs (2)
241-254: LGTM! Improved key management and error handling.
The changes properly implement the suggested improvements:
- Accepts sender_keys parameter instead of generating new keys
- Uses get_nostr_client() for better error handling
633-729: LGTM! Comprehensive test coverage.
The test suite is well-structured and covers:
- Basic utility functions
- Network operations
- Nostr client operations
- Message handling
- Business logic
Each test includes proper setup and covers both success and failure cases.
| let sender_keys = crate::util::get_keys().unwrap(); | ||
| send_dm(&event.sender, sender_keys, message).await?; |
There was a problem hiding this comment.
Replace unwrap() with proper error handling
The use of unwrap() on get_keys() could cause a panic if key retrieval fails. This contradicts the PR's goal of improving error handling.
Consider propagating the error instead:
- let sender_keys = crate::util::get_keys().unwrap();
+ let sender_keys = crate::util::get_keys()?;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let sender_keys = crate::util::get_keys().unwrap(); | |
| send_dm(&event.sender, sender_keys, message).await?; | |
| let sender_keys = crate::util::get_keys()?; | |
| send_dm(&event.sender, sender_keys, message).await?; |
| /// Getter function with error management for nostr Client | ||
| pub fn get_nostr_client() -> Result<&'static Client> { | ||
| if let Some(client) = NOSTR_CLIENT.get() { | ||
| Ok(client) | ||
| } else { | ||
| Err(Error::msg("Client not initialized!")) | ||
| } | ||
| } | ||
|
|
||
| /// Getter function with error management for nostr relays | ||
| pub async fn get_nostr_relays() -> Option<HashMap<Url, Relay>> { | ||
| if let Some(client) = NOSTR_CLIENT.get() { | ||
| Some(client.relays().await) | ||
| } else { | ||
| None | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consider using Result for consistent error handling.
The get_nostr_client() function properly returns a Result with error context, but get_nostr_relays() returns an Option. For consistency and better error handling, consider using Result for both functions.
-pub async fn get_nostr_relays() -> Option<HashMap<Url, Relay>> {
+pub async fn get_nostr_relays() -> Result<HashMap<Url, Relay>> {
if let Some(client) = NOSTR_CLIENT.get() {
- Some(client.relays().await)
+ Ok(client.relays().await)
} else {
- None
+ Err(Error::msg("Cannot get relays: Nostr client not initialized"))
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// Getter function with error management for nostr Client | |
| pub fn get_nostr_client() -> Result<&'static Client> { | |
| if let Some(client) = NOSTR_CLIENT.get() { | |
| Ok(client) | |
| } else { | |
| Err(Error::msg("Client not initialized!")) | |
| } | |
| } | |
| /// Getter function with error management for nostr relays | |
| pub async fn get_nostr_relays() -> Option<HashMap<Url, Relay>> { | |
| if let Some(client) = NOSTR_CLIENT.get() { | |
| Some(client.relays().await) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Getter function with error management for nostr Client | |
| pub fn get_nostr_client() -> Result<&'static Client> { | |
| if let Some(client) = NOSTR_CLIENT.get() { | |
| Ok(client) | |
| } else { | |
| Err(Error::msg("Client not initialized!")) | |
| } | |
| } | |
| /// Getter function with error management for nostr relays | |
| pub async fn get_nostr_relays() -> Result<HashMap<Url, Relay>> { | |
| if let Some(client) = NOSTR_CLIENT.get() { | |
| Ok(client.relays().await) | |
| } else { | |
| Err(Error::msg("Cannot get relays: Nostr client not initialized")) | |
| } | |
| } |
@grunch
thanks to rabbit!
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Refactor
Style