From b2fc0976bd188b434d73ee54087a87851e503ef7 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Thu, 28 Aug 2025 03:50:07 +0300 Subject: [PATCH] style: format code and reorganize imports across rfsclient codebase --- .../rfsclient/examples/authentication.rs | 12 +- .../rfsclient/examples/block_management.rs | 93 +++-- .../rfsclient/examples/file_management.rs | 38 +- .../rfsclient/examples/flist_operations.rs | 70 ++-- .../rfsclient/examples/wait_for_flist.rs | 53 +-- packages/clients/rfsclient/src/client.rs | 253 ++++++++----- packages/clients/rfsclient/src/diff.rs | 153 ++++++++ packages/clients/rfsclient/src/lib.rs | 2 +- packages/clients/rfsclient/src/rhai.rs | 222 +++++------ packages/clients/rfsclient/src/types.rs | 20 +- .../rfsclient/tests/rhai_integration_tests.rs | 348 +++++++++++------- 11 files changed, 795 insertions(+), 469 deletions(-) create mode 100644 packages/clients/rfsclient/src/diff.rs diff --git a/packages/clients/rfsclient/examples/authentication.rs b/packages/clients/rfsclient/examples/authentication.rs index 43c86d5..8608e9f 100644 --- a/packages/clients/rfsclient/examples/authentication.rs +++ b/packages/clients/rfsclient/examples/authentication.rs @@ -1,5 +1,5 @@ -use sal_rfs_client::RfsClient; use sal_rfs_client::types::{ClientConfig, Credentials}; +use sal_rfs_client::RfsClient; #[tokio::main] async fn main() -> Result<(), Box> { @@ -12,10 +12,10 @@ async fn main() -> Result<(), Box> { }), timeout_seconds: 30, }; - + let mut client = RfsClient::new(config); println!("Client created with authentication credentials"); - + // Authenticate with the server client.authenticate().await?; if client.is_authenticated() { @@ -30,13 +30,13 @@ async fn main() -> Result<(), Box> { credentials: None, timeout_seconds: 30, }; - + let client_no_auth = RfsClient::new(config_no_auth); println!("Client created without authentication credentials"); - + // Check health endpoint (doesn't require authentication) let health = client_no_auth.health_check().await?; println!("Server health: {:?}", health); - + Ok(()) } diff --git a/packages/clients/rfsclient/examples/block_management.rs b/packages/clients/rfsclient/examples/block_management.rs index 69f6b98..3bcfdba 100644 --- a/packages/clients/rfsclient/examples/block_management.rs +++ b/packages/clients/rfsclient/examples/block_management.rs @@ -1,6 +1,6 @@ -use sal_rfs_client::RfsClient; -use sal_rfs_client::types::{ClientConfig, Credentials}; use openapi::models::{VerifyBlock, VerifyBlocksRequest}; +use sal_rfs_client::types::{ClientConfig, Credentials}; +use sal_rfs_client::RfsClient; #[tokio::main] async fn main() -> Result<(), Box> { @@ -13,45 +13,52 @@ async fn main() -> Result<(), Box> { }), timeout_seconds: 60, }; - + let mut client = RfsClient::new(config); - + // Authenticate with the server client.authenticate().await?; println!("Authentication successful"); - + // Create a test file to upload for block testing let test_file_path = "/tmp/block_test.txt"; let test_content = "This is a test file for RFS client block management"; std::fs::write(test_file_path, test_content)?; println!("Created test file at {}", test_file_path); - + // Upload the file to get blocks println!("Uploading file to get blocks..."); let file_hash = client.upload_file(test_file_path, None).await?; println!("File uploaded with hash: {}", file_hash); - + // Get blocks by file hash println!("Getting blocks for file hash: {}", file_hash); let blocks = client.get_blocks_by_hash(&file_hash).await?; println!("Found {} blocks for the file", blocks.blocks.len()); - + // Print block information for (i, block_data) in blocks.blocks.iter().enumerate() { - println!("Block {}: Hash={}, Index={}", i, block_data.hash, block_data.index); + println!( + "Block {}: Hash={}, Index={}", + i, block_data.hash, block_data.index + ); } - + // Verify blocks with complete information println!("Verifying blocks..."); - + // Create a list of VerifyBlock objects with complete information - let verify_blocks = blocks.blocks.iter().map(|block| { - VerifyBlock { - block_hash: block.hash.clone(), - block_index: block.index, - file_hash: file_hash.clone(), // Using the actual file hash - } - }).collect::>(); + let verify_blocks = blocks + .blocks + .iter() + .map(|block| { + VerifyBlock { + block_hash: block.hash.clone(), + block_index: block.index, + file_hash: file_hash.clone(), // Using the actual file hash + } + }) + .collect::>(); // Create the request with the complete block information for block in verify_blocks.iter() { @@ -59,27 +66,34 @@ async fn main() -> Result<(), Box> { println!("Block index: {}", block.block_index); println!("File hash: {}", block.file_hash); } - let request = VerifyBlocksRequest { blocks: verify_blocks }; - + let request = VerifyBlocksRequest { + blocks: verify_blocks, + }; + // Send the verification request let verify_result = client.verify_blocks(request).await?; - println!("Verification result: {} missing blocks", verify_result.missing.len()); + println!( + "Verification result: {} missing blocks", + verify_result.missing.len() + ); for block in verify_result.missing.iter() { println!("Missing block: {}", block); } - + // List blocks (list_blocks_handler) println!("\n1. Listing all blocks with pagination..."); let blocks_list = client.list_blocks(None).await?; println!("Server has {} blocks in total", blocks_list.len()); if !blocks_list.is_empty() { - let first_few = blocks_list.iter().take(3) + let first_few = blocks_list + .iter() + .take(3) .map(|s| s.as_str()) .collect::>() .join(", "); println!("First few blocks: {}", first_few); } - + // Check if a block exists (check_block_handler) if !blocks.blocks.is_empty() { let block_to_check = &blocks.blocks[0].hash; @@ -87,15 +101,21 @@ async fn main() -> Result<(), Box> { let exists = client.check_block(block_to_check).await?; println!("Block exists: {}", exists); } - + // Get block downloads statistics (get_block_downloads_handler) if !blocks.blocks.is_empty() { let block_to_check = &blocks.blocks[0].hash; - println!("\n3. Getting download statistics for block: {}", block_to_check); + println!( + "\n3. Getting download statistics for block: {}", + block_to_check + ); let downloads = client.get_block_downloads(block_to_check).await?; - println!("Block has been downloaded {} times", downloads.downloads_count); + println!( + "Block has been downloaded {} times", + downloads.downloads_count + ); } - + // Get a specific block content (get_block_handler) if !blocks.blocks.is_empty() { let block_to_get = &blocks.blocks[0].hash; @@ -103,26 +123,31 @@ async fn main() -> Result<(), Box> { let block_content = client.get_block(block_to_get).await?; println!("Retrieved block with {} bytes", block_content.len()); } - + // Get user blocks (get_user_blocks_handler) println!("\n6. Listing user blocks..."); let user_blocks = client.get_user_blocks(Some(1), Some(10)).await?; - println!("User has {} blocks (showing page 1 with 10 per page)", user_blocks.total); + println!( + "User has {} blocks (showing page 1 with 10 per page)", + user_blocks.total + ); for block in user_blocks.blocks.iter().take(3) { println!(" - Block: {}, Size: {}", block.hash, block.size); } - + // Upload a block (upload_block_handler) println!("\n7. Uploading a new test block..."); let test_block_data = b"This is test block data for direct block upload"; let new_file_hash = "test_file_hash_for_block_upload"; let block_index = 0; - let block_hash = client.upload_block(new_file_hash, block_index, test_block_data.to_vec()).await?; + let block_hash = client + .upload_block(new_file_hash, block_index, test_block_data.to_vec()) + .await?; println!("Uploaded block with hash: {}", block_hash); - + // Clean up std::fs::remove_file(test_file_path)?; println!("Test file cleaned up"); - + Ok(()) } diff --git a/packages/clients/rfsclient/examples/file_management.rs b/packages/clients/rfsclient/examples/file_management.rs index e039e17..1139e26 100644 --- a/packages/clients/rfsclient/examples/file_management.rs +++ b/packages/clients/rfsclient/examples/file_management.rs @@ -1,5 +1,5 @@ +use sal_rfs_client::types::{ClientConfig, Credentials, DownloadOptions, UploadOptions}; use sal_rfs_client::RfsClient; -use sal_rfs_client::types::{ClientConfig, Credentials, UploadOptions, DownloadOptions}; #[tokio::main] async fn main() -> Result<(), Box> { @@ -12,53 +12,55 @@ async fn main() -> Result<(), Box> { }), timeout_seconds: 60, }; - + let mut client = RfsClient::new(config); - + // Authenticate with the server client.authenticate().await?; println!("Authentication successful"); - + // Create a test file to upload let test_file_path = "/tmp/test_upload.txt"; std::fs::write(test_file_path, "This is a test file for RFS client upload")?; println!("Created test file at {}", test_file_path); - + // Upload the file with options println!("Uploading file..."); let upload_options = UploadOptions { chunk_size: Some(1024 * 1024), // 1MB chunks verify: true, }; - - let file_hash = client.upload_file(test_file_path, Some(upload_options)).await?; + + let file_hash = client + .upload_file(test_file_path, Some(upload_options)) + .await?; println!("File uploaded with hash: {}", file_hash); - + // Download the file let download_path = "/tmp/test_download.txt"; println!("Downloading file to {}...", download_path); - - let download_options = DownloadOptions { - verify: true, - }; - - client.download_file(&file_hash, download_path, Some(download_options)).await?; + + let download_options = DownloadOptions { verify: true }; + + client + .download_file(&file_hash, download_path, Some(download_options)) + .await?; println!("File downloaded to {}", download_path); - + // Verify the downloaded file matches the original let original_content = std::fs::read_to_string(test_file_path)?; let downloaded_content = std::fs::read_to_string(download_path)?; - + if original_content == downloaded_content { println!("File contents match! Download successful."); } else { println!("ERROR: File contents do not match!"); } - + // Clean up test files std::fs::remove_file(test_file_path)?; std::fs::remove_file(download_path)?; println!("Test files cleaned up"); - + Ok(()) } diff --git a/packages/clients/rfsclient/examples/flist_operations.rs b/packages/clients/rfsclient/examples/flist_operations.rs index 7b411d1..48a43bf 100644 --- a/packages/clients/rfsclient/examples/flist_operations.rs +++ b/packages/clients/rfsclient/examples/flist_operations.rs @@ -1,5 +1,5 @@ -use sal_rfs_client::RfsClient; use sal_rfs_client::types::{ClientConfig, Credentials, FlistOptions, WaitOptions}; +use sal_rfs_client::RfsClient; #[tokio::main] async fn main() -> Result<(), Box> { @@ -13,17 +13,17 @@ async fn main() -> Result<(), Box> { }), timeout_seconds: 60, }; - + let mut client = RfsClient::new(config); - + // Authenticate with the server client.authenticate().await?; println!("Authentication successful"); - + println!("\n1. CREATE FLIST - Creating an FList from a Docker image"); let image_name = "alpine:latest"; println!("Creating FList for image: {}", image_name); - + // Use FlistOptions to specify additional parameters let options = FlistOptions { auth: None, @@ -34,13 +34,13 @@ async fn main() -> Result<(), Box> { identity_token: None, registry_token: None, }; - + // Create the FList and handle potential conflict error let job_id = match client.create_flist(&image_name, Some(options)).await { Ok(id) => { println!("FList creation started with job ID: {}", id); Some(id) - }, + } Err(e) => { if e.to_string().contains("Conflict") { println!("FList already exists"); @@ -50,51 +50,55 @@ async fn main() -> Result<(), Box> { } } }; - + // 2. Check FList state if we have a job ID if let Some(job_id) = &job_id { println!("\n2. GET FLIST STATE - Checking FList creation state"); let state = client.get_flist_state(job_id).await?; println!("Current FList state: {:?}", state.flist_state); - + // 3. Wait for FList creation with progress reporting println!("\n3. WAIT FOR FLIST CREATION - Waiting for FList to be created with progress reporting"); let wait_options = WaitOptions { - timeout_seconds: 60, // Shorter timeout for the example + timeout_seconds: 60, // Shorter timeout for the example poll_interval_ms: 1000, progress_callback: Some(Box::new(|state| { println!("Progress: FList state is now {:?}", state); // No return value needed (returns unit type) })), }; - + // Wait for the FList to be created (with a timeout) - match client.wait_for_flist_creation(job_id, Some(wait_options)).await { + match client + .wait_for_flist_creation(job_id, Some(wait_options)) + .await + { Ok(final_state) => { println!("FList creation completed with state: {:?}", final_state); - }, + } Err(e) => { println!("Error waiting for FList creation: {}", e); // Continue with the example even if waiting fails } }; } - + // 4. List all available FLists println!("\n4. LIST FLISTS - Listing all available FLists"); - + // Variable to store the FList path for preview and download let mut flist_path_for_preview: Option = None; - + match client.list_flists().await { Ok(flists) => { println!("Found {} FList categories", flists.len()); - + for (category, files) in &flists { println!("Category: {}", category); - for file in files.iter().take(2) { // Show only first 2 files per category + for file in files.iter().take(2) { + // Show only first 2 files per category println!(" - {} (size: {} bytes)", file.name, file.size); - + // Save the first FList path for preview if flist_path_for_preview.is_none() { let path = format!("{}/{}/{}", parent_dir, category, file.name); @@ -105,7 +109,7 @@ async fn main() -> Result<(), Box> { println!(" - ... and {} more files", files.len() - 2); } } - + // 5. Preview an FList if we found one if let Some(ref flist_path) = flist_path_for_preview { println!("\n5. PREVIEW FLIST - Previewing FList: {}", flist_path); @@ -114,57 +118,59 @@ async fn main() -> Result<(), Box> { println!("FList preview for {}:", flist_path); println!(" - Checksum: {}", preview.checksum); println!(" - Metadata: {}", preview.metadata); - + // Display content (list of strings) if !preview.content.is_empty() { println!(" - Content entries:"); for (i, entry) in preview.content.iter().enumerate().take(5) { - println!(" {}. {}", i+1, entry); + println!(" {}. {}", i + 1, entry); } if preview.content.len() > 5 { println!(" ... and {} more entries", preview.content.len() - 5); } } - }, + } Err(e) => println!("Error previewing FList: {}", e), } } else { println!("No FLists available for preview"); } - }, + } Err(e) => println!("Error listing FLists: {}", e), } - + // 6. DOWNLOAD FLIST - Downloading an FList to a local file if let Some(ref flist_path) = flist_path_for_preview { println!("\n6. DOWNLOAD FLIST - Downloading FList: {}", flist_path); - + // Create a temporary output path for the downloaded FList let output_path = "/tmp/downloaded_flist.fl"; - + match client.download_flist(flist_path, output_path).await { Ok(_) => { println!("FList successfully downloaded to {}", output_path); - + // Get file size match std::fs::metadata(output_path) { Ok(metadata) => println!("Downloaded file size: {} bytes", metadata.len()), Err(e) => println!("Error getting file metadata: {}", e), } - }, + } Err(e) => println!("Error downloading FList: {}", e), } } else { println!("\n6. DOWNLOAD FLIST - No FList available for download"); } - + println!("\nAll FList operations demonstrated:"); println!("1. create_flist - Create a new FList from a Docker image"); println!("2. get_flist_state - Check the state of an FList creation job"); - println!("3. wait_for_flist_creation - Wait for an FList to be created with progress reporting"); + println!( + "3. wait_for_flist_creation - Wait for an FList to be created with progress reporting" + ); println!("4. list_flists - List all available FLists"); println!("5. preview_flist - Preview the content of an FList"); println!("6. download_flist - Download an FList to a local file"); - + Ok(()) } diff --git a/packages/clients/rfsclient/examples/wait_for_flist.rs b/packages/clients/rfsclient/examples/wait_for_flist.rs index 345b90c..e776102 100644 --- a/packages/clients/rfsclient/examples/wait_for_flist.rs +++ b/packages/clients/rfsclient/examples/wait_for_flist.rs @@ -1,6 +1,6 @@ -use sal_rfs_client::RfsClient; -use sal_rfs_client::types::{ClientConfig, Credentials, WaitOptions}; use openapi::models::FlistState; +use sal_rfs_client::types::{ClientConfig, Credentials, WaitOptions}; +use sal_rfs_client::RfsClient; #[tokio::main] async fn main() -> Result<(), Box> { @@ -13,49 +13,52 @@ async fn main() -> Result<(), Box> { }), timeout_seconds: 60, }; - + let mut client = RfsClient::new(config); - + // Authenticate with the server client.authenticate().await?; println!("Authentication successful"); - + // Create an FList from a Docker image let image_name = "redis:latest"; println!("Creating FList for image: {}", image_name); - + let job_id = client.create_flist(&image_name, None).await?; println!("FList creation started with job ID: {}", job_id); - + // Set up options for waiting with progress reporting let options = WaitOptions { - timeout_seconds: 600, // 10 minutes timeout + timeout_seconds: 600, // 10 minutes timeout poll_interval_ms: 2000, // Check every 2 seconds - progress_callback: Some(Box::new(|state| { - match state { - FlistState::FlistStateInProgress(info) => { - println!("Progress: {:.1}% - {}", info.in_progress.progress, info.in_progress.msg); - }, - FlistState::FlistStateStarted(_) => { - println!("FList creation started..."); - }, - FlistState::FlistStateAccepted(_) => { - println!("FList creation request accepted..."); - }, - _ => println!("State: {:?}", state), + progress_callback: Some(Box::new(|state| match state { + FlistState::FlistStateInProgress(info) => { + println!( + "Progress: {:.1}% - {}", + info.in_progress.progress, info.in_progress.msg + ); } + FlistState::FlistStateStarted(_) => { + println!("FList creation started..."); + } + FlistState::FlistStateAccepted(_) => { + println!("FList creation request accepted..."); + } + _ => println!("State: {:?}", state), })), }; - + // Wait for the FList to be created println!("Waiting for FList creation to complete..."); - + // Use ? operator to propagate errors properly - let state = client.wait_for_flist_creation(&job_id, Some(options)).await + let state = client + .wait_for_flist_creation(&job_id, Some(options)) + .await .map_err(|e| -> Box { Box::new(e) })?; - + println!("FList created successfully!"); println!("Final state: {:?}", state); - + Ok(()) } diff --git a/packages/clients/rfsclient/src/client.rs b/packages/clients/rfsclient/src/client.rs index 367b56c..1b0d34d 100644 --- a/packages/clients/rfsclient/src/client.rs +++ b/packages/clients/rfsclient/src/client.rs @@ -1,24 +1,23 @@ +use bytes::Bytes; +use std::collections::HashMap; use std::path::Path; use std::sync::Arc; -use std::collections::HashMap; -use bytes::Bytes; use openapi::{ apis::{ - authentication_api, block_management_api, flist_management_api, - file_management_api, system_api, website_serving_api, - configuration::Configuration, + authentication_api, block_management_api, configuration::Configuration, + file_management_api, flist_management_api, system_api, website_serving_api, Error as OpenApiError, }, models::{ - SignInBody, ListBlocksParams, - VerifyBlocksRequest, VerifyBlocksResponse, FlistBody, UserBlocksResponse, BlockDownloadsResponse, - BlocksResponse, PreviewResponse, FileInfo, FlistState, FlistStateResponse, + BlockDownloadsResponse, BlocksResponse, FileInfo, FlistBody, FlistState, + FlistStateResponse, ListBlocksParams, PreviewResponse, SignInBody, UserBlocksResponse, + VerifyBlocksRequest, VerifyBlocksResponse, }, }; -use crate::error::{RfsError, Result, map_openapi_error}; -use crate::types::{ClientConfig, UploadOptions, DownloadOptions, FlistOptions, WaitOptions}; +use crate::error::{map_openapi_error, Result, RfsError}; +use crate::types::{ClientConfig, DownloadOptions, FlistOptions, UploadOptions, WaitOptions}; /// Main client for interacting with the RFS server #[derive(Clone)] @@ -33,16 +32,18 @@ impl RfsClient { pub fn new(client_config: ClientConfig) -> Self { // Create a custom reqwest client with timeout configuration let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(client_config.timeout_seconds)) + .timeout(std::time::Duration::from_secs( + client_config.timeout_seconds, + )) .build() .unwrap_or_default(); - + // Create OpenAPI configuration with our custom client let mut config = Configuration::new(); config.base_path = client_config.base_url.clone(); - config.user_agent = Some(format!("rfs-client/0.1.0")); + config.user_agent = Some("rfs-client/0.1.0".to_string()); config.client = client; - + Self { config: Arc::new(config), client_config, @@ -70,22 +71,26 @@ impl RfsClient { if let Some(token) = Some(result.access_token) { // Create a custom reqwest client with timeout configuration let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(self.client_config.timeout_seconds)) + .timeout(std::time::Duration::from_secs( + self.client_config.timeout_seconds, + )) .build() .unwrap_or_default(); - + // Create a new configuration with the auth token and timeout let mut new_config = Configuration::new(); new_config.base_path = self.client_config.base_url.clone(); - new_config.user_agent = Some(format!("rfs-client/0.1.0")); + new_config.user_agent = Some("rfs-client/0.1.0".to_string()); new_config.bearer_access_token = Some(token.clone()); new_config.client = client; - + self.config = Arc::new(new_config); self.auth_token = Some(token); Ok(()) } else { - Err(RfsError::AuthError("No token received from server".to_string())) + Err(RfsError::AuthError( + "No token received from server".to_string(), + )) } } else { Err(RfsError::AuthError("No credentials provided".to_string())) @@ -102,62 +107,79 @@ impl RfsClient { let result = system_api::health_check_handler(&self.config) .await .map_err(map_openapi_error)?; - + Ok(result.msg) } /// Upload a file to the RFS server - pub async fn upload_file>(&self, file_path: P, options: Option) -> Result { + pub async fn upload_file>( + &self, + file_path: P, + options: Option, + ) -> Result { let file_path = file_path.as_ref(); let _options = options.unwrap_or_default(); - + // Check if file exists if !file_path.exists() { - return Err(RfsError::FileSystemError(format!("File not found: {}", file_path.display()))); + return Err(RfsError::FileSystemError(format!( + "File not found: {}", + file_path.display() + ))); } - + // Use the OpenAPI client to upload the file - let result = file_management_api::upload_file_handler(&self.config, file_path.to_path_buf()) - .await - .map_err(map_openapi_error)?; - + let result = + file_management_api::upload_file_handler(&self.config, file_path.to_path_buf()) + .await + .map_err(map_openapi_error)?; + // Extract the file hash from the response Ok(result.file_hash.clone()) } /// Download a file from the RFS server - pub async fn download_file>(&self, file_id: &str, output_path: P, options: Option) -> Result<()> { + pub async fn download_file>( + &self, + file_id: &str, + output_path: P, + options: Option, + ) -> Result<()> { let output_path = output_path.as_ref(); let _options = options.unwrap_or_default(); - + // Create parent directories if needed if let Some(parent) = output_path.parent() { - std::fs::create_dir_all(parent) - .map_err(|e| RfsError::FileSystemError(format!("Failed to create directory: {}", e)))?; + std::fs::create_dir_all(parent).map_err(|e| { + RfsError::FileSystemError(format!("Failed to create directory: {}", e)) + })?; } - + // Create a FileDownloadRequest with the filename from the output path - let file_name = output_path.file_name() + let file_name = output_path + .file_name() .and_then(|n| n.to_str()) .unwrap_or("downloaded_file") .to_string(); - + let download_request = openapi::models::FileDownloadRequest::new(file_name); - + // Download the file - let response = file_management_api::get_file_handler(&self.config, file_id, download_request) - .await - .map_err(map_openapi_error)?; - + let response = + file_management_api::get_file_handler(&self.config, file_id, download_request) + .await + .map_err(map_openapi_error)?; + // Read the response body - let bytes = response.bytes() + let bytes = response + .bytes() .await - .map_err(|e| RfsError::RequestError(e))?; - + .map_err(RfsError::RequestError)?; + // Write the file to disk std::fs::write(output_path, bytes) .map_err(|e| RfsError::FileSystemError(format!("Failed to write file: {}", e)))?; - + Ok(()) } @@ -168,29 +190,38 @@ impl RfsClient { let result = block_management_api::list_blocks_handler(&self.config, page, per_page) .await .map_err(map_openapi_error)?; - + Ok(result.blocks) } /// Verify blocks - pub async fn verify_blocks(&self, request: VerifyBlocksRequest) -> Result { + pub async fn verify_blocks( + &self, + request: VerifyBlocksRequest, + ) -> Result { let result = block_management_api::verify_blocks_handler(&self.config, request) .await .map_err(map_openapi_error)?; - + Ok(result) } /// Create a new FList from a Docker image - pub async fn create_flist(&self, image_name: &str, options: Option) -> Result { + pub async fn create_flist( + &self, + image_name: &str, + options: Option, + ) -> Result { // Ensure the client is authenticated if !self.is_authenticated() { - return Err(RfsError::AuthError("Authentication required for creating FLists".to_string())); + return Err(RfsError::AuthError( + "Authentication required for creating FLists".to_string(), + )); } - + // Create FList body with the required fields let mut flist = FlistBody::new(image_name.to_string()); - + // Apply options if provided if let Some(opts) = options { flist.username = opts.username.map(Some); @@ -201,12 +232,12 @@ impl RfsClient { flist.identity_token = opts.identity_token.map(Some); flist.registry_token = opts.registry_token.map(Some); } - + // Call the API to create the FList let result = flist_management_api::create_flist_handler(&self.config, flist) .await .map_err(map_openapi_error)?; - + // Return the job ID Ok(result.id) } @@ -215,66 +246,80 @@ impl RfsClient { pub async fn get_flist_state(&self, job_id: &str) -> Result { // Ensure the client is authenticated if !self.is_authenticated() { - return Err(RfsError::AuthError("Authentication required for accessing FList state".to_string())); + return Err(RfsError::AuthError( + "Authentication required for accessing FList state".to_string(), + )); } - + // Call the API to get the FList state let result = flist_management_api::get_flist_state_handler(&self.config, job_id) - .await + .await .map_err(map_openapi_error)?; - + Ok(result) } - + /// Wait for an FList to be created - /// + /// /// This method polls the FList state until it reaches a terminal state (Created or Failed) /// or until the timeout is reached. - pub async fn wait_for_flist_creation(&self, job_id: &str, options: Option) -> Result { + pub async fn wait_for_flist_creation( + &self, + job_id: &str, + options: Option, + ) -> Result { let options = options.unwrap_or_default(); - let deadline = std::time::Instant::now() + std::time::Duration::from_secs(options.timeout_seconds); - + let deadline = + std::time::Instant::now() + std::time::Duration::from_secs(options.timeout_seconds); + loop { // Check if we've exceeded the timeout if std::time::Instant::now() > deadline { return Err(RfsError::TimeoutError(format!( - "Timed out waiting for FList creation after {} seconds", + "Timed out waiting for FList creation after {} seconds", options.timeout_seconds ))); } - + // Get the current state let state_result = self.get_flist_state(job_id).await; - + match state_result { Ok(state) => { // Call progress callback if provided if let Some(ref callback) = options.progress_callback { callback(state.flist_state.as_ref()); } - + // Check if we've reached a terminal state match state.flist_state.as_ref() { FlistState::FlistStateCreated(_) => { // Success! FList was created return Ok(state); - }, + } FlistState::FlistStateFailed(error_msg) => { // Failure! FList creation failed - return Err(RfsError::FListError(format!("FList creation failed: {}", error_msg))); - }, + return Err(RfsError::FListError(format!( + "FList creation failed: {}", + error_msg + ))); + } _ => { // Still in progress, continue polling - tokio::time::sleep(std::time::Duration::from_millis(options.poll_interval_ms)).await; + tokio::time::sleep(std::time::Duration::from_millis( + options.poll_interval_ms, + )) + .await; } } - }, + } Err(e) => { // If we get a 404 error, it might be because the FList job is still initializing // Just wait and retry println!("Warning: Error checking FList state: {}", e); println!("Retrying in {} ms...", options.poll_interval_ms); - tokio::time::sleep(std::time::Duration::from_millis(options.poll_interval_ms)).await; + tokio::time::sleep(std::time::Duration::from_millis(options.poll_interval_ms)) + .await; } } } @@ -294,7 +339,7 @@ impl RfsClient { let result = block_management_api::get_block_downloads_handler(&self.config, hash) .await .map_err(map_openapi_error)?; - + Ok(result) } @@ -303,10 +348,12 @@ impl RfsClient { let response = block_management_api::get_block_handler(&self.config, hash) .await .map_err(map_openapi_error)?; - - let bytes = response.bytes().await - .map_err(|e| RfsError::RequestError(e))?; - + + let bytes = response + .bytes() + .await + .map_err(RfsError::RequestError)?; + Ok(bytes) } @@ -315,16 +362,20 @@ impl RfsClient { let result = block_management_api::get_blocks_by_hash_handler(&self.config, hash) .await .map_err(map_openapi_error)?; - + Ok(result) } /// Get blocks uploaded by the current user - pub async fn get_user_blocks(&self, page: Option, per_page: Option) -> Result { + pub async fn get_user_blocks( + &self, + page: Option, + per_page: Option, + ) -> Result { let result = block_management_api::get_user_blocks_handler(&self.config, page, per_page) .await .map_err(map_openapi_error)?; - + Ok(result) } @@ -333,11 +384,12 @@ impl RfsClient { // Create a temporary file to hold the block data let temp_dir = std::env::temp_dir(); let temp_file_path = temp_dir.join(format!("{}-{}", file_hash, idx)); - + // Write the data to the temporary file - std::fs::write(&temp_file_path, &data) - .map_err(|e| RfsError::FileSystemError(format!("Failed to write temporary block file: {}", e)))?; - + std::fs::write(&temp_file_path, &data).map_err(|e| { + RfsError::FileSystemError(format!("Failed to write temporary block file: {}", e)) + })?; + // Upload the block let result = block_management_api::upload_block_handler( &self.config, @@ -347,12 +399,12 @@ impl RfsClient { ) .await .map_err(map_openapi_error)?; - + // Clean up the temporary file if let Err(e) = std::fs::remove_file(temp_file_path) { eprintln!("Warning: Failed to remove temporary block file: {}", e); } - + // Return the hash from the response Ok(result.hash) } @@ -362,7 +414,7 @@ impl RfsClient { let result = flist_management_api::list_flists_handler(&self.config) .await .map_err(map_openapi_error)?; - + Ok(result) } @@ -371,7 +423,7 @@ impl RfsClient { let result = flist_management_api::preview_flist_handler(&self.config, flist_path) .await .map_err(map_openapi_error)?; - + Ok(result) } @@ -380,7 +432,7 @@ impl RfsClient { let result = website_serving_api::serve_website_handler(&self.config, website_id, path) .await .map_err(map_openapi_error)?; - + Ok(result) } @@ -389,25 +441,30 @@ impl RfsClient { let result = system_api::health_check_handler(&self.config) .await .map_err(map_openapi_error)?; - + Ok(result.msg) } - /// Download an FList file - /// + /// /// This method downloads an FList from the server and saves it to the specified path. - pub async fn download_flist>(&self, flist_path: &str, output_path: P) -> Result<()> { + pub async fn download_flist>( + &self, + flist_path: &str, + output_path: P, + ) -> Result<()> { let response = flist_management_api::serve_flists(&self.config, flist_path) .await .map_err(map_openapi_error)?; - - let bytes = response.bytes().await - .map_err(|e| RfsError::RequestError(e))?; - + + let bytes = response + .bytes() + .await + .map_err(RfsError::RequestError)?; + std::fs::write(output_path, &bytes) .map_err(|e| RfsError::FileSystemError(e.to_string()))?; - + Ok(()) } } diff --git a/packages/clients/rfsclient/src/diff.rs b/packages/clients/rfsclient/src/diff.rs new file mode 100644 index 0000000..024ebfd --- /dev/null +++ b/packages/clients/rfsclient/src/diff.rs @@ -0,0 +1,153 @@ +diff --git a/packages/clients/rfsclient/src/rhai.rs b/packages/clients/rfsclient/src/rhai.rs +index fd686ba..b19c50f 100644 +--- a/packages/clients/rfsclient/src/rhai.rs ++++ b/packages/clients/rfsclient/src/rhai.rs +@@ -17,6 +17,14 @@ lazy_static! { + static ref RUNTIME: Mutex> = Mutex::new(None); + } + ++/// Overload: list blocks with explicit pagination integers ++fn rfs_list_blocks_with_pagination( ++ page: rhai::INT, ++ per_page: rhai::INT, ++) -> Result> { ++ rfs_list_blocks(Some(page), Some(per_page)) ++} ++ + /// Wrapper around RfsClient to make it thread-safe for global usage + struct RfsClientWrapper { + client: Mutex, +@@ -47,6 +55,8 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box + + // Register block management functions + engine.register_fn("rfs_list_blocks", rfs_list_blocks); ++ // Overload accepting explicit integer pagination params ++ engine.register_fn("rfs_list_blocks", rfs_list_blocks_with_pagination); + engine.register_fn("rfs_upload_block", rfs_upload_block); + engine.register_fn("rfs_check_block", rfs_check_block); + engine.register_fn("rfs_get_block_downloads", rfs_get_block_downloads); +diff --git a/packages/clients/rfsclient/tests/rhai_integration_tests.rs b/packages/clients/rfsclient/tests/rhai_integration_tests.rs +index 2c90001..cc38f4a 100644 +--- a/packages/clients/rfsclient/tests/rhai_integration_tests.rs ++++ b/packages/clients/rfsclient/tests/rhai_integration_tests.rs +@@ -114,8 +114,7 @@ fn test_rfs_flist_management_integration() { + Err(e) => { + let error_msg = e.to_string(); + println!("FList preview error: {}", error_msg); +- +- // Check if it's an authentication error (shouldn't happen with valid creds) ++ // Authentication should not fail in this integration test + if error_msg.contains("Authentication") { + panic!("❌ Authentication should work with valid credentials: {}", error_msg); + } else { +@@ -141,6 +140,7 @@ fn test_rfs_create_flist_integration() { + let create_script = format!(r#" + rfs_create_client("{}", "{}", "{}", 30); + rfs_authenticate(); ++ if !rfs_is_authenticated() {{ throw "Not authenticated after rfs_authenticate()"; }} + rfs_create_flist("busybox:latest", "docker.io", "", "") + "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD); + +@@ -466,10 +466,10 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box> { + + let result: bool = engine.eval(&create_script)?; + assert!(result, "Failed to create RFS client"); +- // Test listing blocks with default pagination - using optional parameters ++ // Test listing blocks with explicit pagination parameters + let list_script = r#" +- let result = rfs_list_blocks(); +- if typeof(result) != "string" { ++ let result = rfs_list_blocks(1, 50); ++ if result.type_of() != "string" { + throw "Expected string result "; + } + true +@@ -506,7 +506,7 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box> { + let download_script = format!( + r#" + let result = rfs_download_block("test_block_hash", '{}', false); +- if typeof(result) != "string" {{ ++ if result.type_of() != "string" {{ + throw "Expected string result"; + }} + true +@@ -540,9 +540,9 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box> { + + // Test verifying blocks with a test hash + let verify_script = r#" +- let hashes = '["test_block_hash"]'; ++ let hashes = "[\"test_block_hash\"]"; + let result = rfs_verify_blocks(hashes); +- if typeof(result) != "string" { ++ if result.type_of() != "string" { + throw "Expected string result"; + } + true +@@ -574,16 +574,29 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box> { + // Test getting block info with a test hash + let info_script = r#" + let result = rfs_get_blocks_by_hash("test_block_hash"); +- if typeof(result) != "string" { ++ if result.type_of() != "string" { + throw "Expected string result"; + } + true + "#; + +- let result: bool = engine.eval(info_script)?; +- assert!(result, "Failed to get block info"); +- +- Ok(()) ++ match engine.eval::(info_script) { ++ Ok(result) => { ++ assert!(result, "Failed to get block info"); ++ Ok(()) ++ } ++ Err(e) => { ++ let error_msg = e.to_string(); ++ println!("Block info error (may be expected): {}", error_msg); ++ assert!( ++ error_msg.contains("404") || ++ error_msg.contains("not found") || ++ error_msg.contains("OpenAPI") || ++ error_msg.contains("RFS error") ++ ); ++ Ok(()) ++ } ++ } + } + + // ============================================================================= +@@ -614,10 +627,10 @@ fn test_rfs_download_file_wrapper() -> Result<(), Box> { + // Test downloading a file (assuming test file hash exists) + let download_script = format!( + r#" +- let options = #{{ verify: false }}; +- let result = rfs_download_file("test_file_hash", '{}', options); +- if typeof(result) != "string" {{ +- throw "Expected string result"; ++ // rfs_download_file returns unit and throws on error ++ let result = rfs_download_file("test_file_hash", '{}', false); ++ if result.type_of() != "()" {{ ++ throw "Expected unit return"; + }} + true + "#, +@@ -839,7 +852,7 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box> { + let download_script = format!( + r#" + let result = rfs_download_flist("flists/test/test.fl", '{}'); +- if typeof(result) != "string" {{ ++ if result.type_of() != "string" {{ + throw "Expected string result"; + }} + true +@@ -874,7 +887,7 @@ fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box Self { - Self { client: Mutex::new(client) } + Self { + client: Mutex::new(client), + } } } @@ -44,7 +46,7 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box engine.register_fn("rfs_get_system_info", rfs_get_system_info); engine.register_fn("rfs_is_authenticated", rfs_is_authenticated); engine.register_fn("rfs_health_check", rfs_health_check); - + // Register block management functions engine.register_fn("rfs_list_blocks", rfs_list_blocks); engine.register_fn("rfs_list_blocks", rfs_list_blocks); @@ -55,11 +57,11 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box engine.register_fn("rfs_get_block", rfs_get_block); engine.register_fn("rfs_get_blocks_by_hash", rfs_get_blocks_by_hash); engine.register_fn("rfs_get_user_blocks", rfs_get_user_blocks); - + // Register file operations functions engine.register_fn("rfs_upload_file", rfs_upload_file); engine.register_fn("rfs_download_file", rfs_download_file); - + // Register FList management functions engine.register_fn("rfs_create_flist", rfs_create_flist); engine.register_fn("rfs_list_flists", rfs_list_flists); @@ -67,10 +69,10 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box engine.register_fn("rfs_preview_flist", rfs_preview_flist); engine.register_fn("rfs_download_flist", rfs_download_flist); engine.register_fn("rfs_wait_for_flist_creation", rfs_wait_for_flist_creation); - + // Register Website functions engine.register_fn("rfs_get_website", rfs_get_website); - + // Register System and Utility functions engine.register_fn("rfs_is_authenticated", rfs_is_authenticated); engine.register_fn("rfs_health_check", rfs_health_check); @@ -250,7 +252,7 @@ pub fn rfs_authenticate() -> Result> { } /// Check if the client is authenticated with the RFS server -/// +/// /// # Returns /// `true` if authenticated, `false` otherwise fn rfs_is_authenticated() -> Result> { @@ -261,7 +263,7 @@ fn rfs_is_authenticated() -> Result> { rhai::Position::NONE, )) })?; - + Ok(client.is_authenticated()) } @@ -300,7 +302,7 @@ pub fn rfs_get_system_info() -> Result> { } /// Check the health status of the RFS server -/// +/// /// # Returns /// The health status as a string fn rfs_health_check() -> Result> { @@ -321,10 +323,8 @@ fn rfs_health_check() -> Result> { )) })?; - let result = runtime.block_on(async { - client.health_check().await - }); - + let result = runtime.block_on(async { client.health_check().await }); + result.map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Health check failed: {}", e).into(), @@ -338,11 +338,11 @@ fn rfs_health_check() -> Result> { // ============================================================================= /// List all blocks with optional filtering -/// +/// /// # Arguments /// * `page` - Optional page number (1-based) /// * `per_page` - Optional number of items per page -/// +/// /// # Returns /// JSON string containing block information fn rfs_list_blocks_impl( @@ -368,20 +368,18 @@ fn rfs_list_blocks_impl( // Create ListBlocksParams with optional page and per_page let mut params = openapi::models::ListBlocksParams::new(); - + // Convert Rhai INT to i32 for the API and set the parameters if let Some(p) = page.and_then(|p| p.try_into().ok()) { params.page = Some(Some(p)); } - + if let Some(pp) = per_page.and_then(|p| p.try_into().ok()) { params.per_page = Some(Some(pp)); } - let result = runtime.block_on(async { - client.list_blocks(Some(params)).await - }); - + let result = runtime.block_on(async { client.list_blocks(Some(params)).await }); + match result { Ok(blocks) => { // Convert blocks to JSON string for Rhai @@ -400,10 +398,10 @@ fn rfs_list_blocks_impl( } /// Check if a block exists -/// +/// /// # Arguments /// * `hash` - The hash of the block to check -/// +/// /// # Returns /// `true` if the block exists, `false` otherwise fn rfs_check_block(hash: &str) -> Result> { @@ -424,10 +422,8 @@ fn rfs_check_block(hash: &str) -> Result> { )) })?; - let result = runtime.block_on(async { - client.check_block(hash).await - }); - + let result = runtime.block_on(async { client.check_block(hash).await }); + result.map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Failed to check block: {}", e).into(), @@ -437,10 +433,10 @@ fn rfs_check_block(hash: &str) -> Result> { } /// Get block download statistics -/// +/// /// # Arguments /// * `hash` - The hash of the block -/// +/// /// # Returns /// JSON string containing download statistics fn rfs_get_block_downloads(hash: &str) -> Result> { @@ -461,10 +457,8 @@ fn rfs_get_block_downloads(hash: &str) -> Result> { )) })?; - let result = runtime.block_on(async { - client.get_block_downloads(hash).await - }); - + let result = runtime.block_on(async { client.get_block_downloads(hash).await }); + match result { Ok(stats) => { // Convert stats to JSON string for Rhai @@ -483,10 +477,10 @@ fn rfs_get_block_downloads(hash: &str) -> Result> { } /// Verify blocks -/// +/// /// # Arguments /// * `hashes` - JSON array of block hashes to verify -/// +/// /// # Returns /// JSON string containing verification results fn rfs_verify_blocks(hashes: &str) -> Result> { @@ -525,16 +519,14 @@ fn rfs_verify_blocks(hashes: &str) -> Result> { .into_iter() .map(|block_hash| openapi::models::VerifyBlock { block_hash: block_hash.clone(), - block_index: 0, // Default to 0 if not specified - file_hash: block_hash, // Using the same hash as file_hash for now + block_index: 0, // Default to 0 if not specified + file_hash: block_hash, // Using the same hash as file_hash for now }) .collect(); let request = openapi::models::VerifyBlocksRequest::new(verify_blocks); - let result = runtime.block_on(async { - client.verify_blocks(request).await - }); - + let result = runtime.block_on(async { client.verify_blocks(request).await }); + match result { Ok(verification) => { // Convert verification to JSON string for Rhai @@ -553,10 +545,10 @@ fn rfs_verify_blocks(hashes: &str) -> Result> { } /// Get a block by hash -/// +/// /// # Arguments /// * `hash` - The hash of the block to retrieve -/// +/// /// # Returns /// The block data as a byte array fn rfs_get_block(hash: &str) -> Result> { @@ -577,12 +569,10 @@ fn rfs_get_block(hash: &str) -> Result> { )) })?; - let result = runtime.block_on(async { - client.get_block(hash).await - }); - + let result = runtime.block_on(async { client.get_block(hash).await }); + match result { - Ok(bytes) => Ok(bytes.to_vec().into()), + Ok(bytes) => Ok(bytes.to_vec()), Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime( format!("Failed to get block: {}", e).into(), rhai::Position::NONE, @@ -591,10 +581,10 @@ fn rfs_get_block(hash: &str) -> Result> { } /// Get blocks by file hash or block hash -/// +/// /// # Arguments /// * `hash` - The file hash or block hash to look up -/// +/// /// # Returns /// JSON string containing block information fn rfs_get_blocks_by_hash(hash: &str) -> Result> { @@ -615,10 +605,8 @@ fn rfs_get_blocks_by_hash(hash: &str) -> Result> { )) })?; - let result = runtime.block_on(async { - client.get_blocks_by_hash(hash).await - }); - + let result = runtime.block_on(async { client.get_blocks_by_hash(hash).await }); + match result { Ok(blocks) => { // Convert blocks to JSON string for Rhai @@ -637,11 +625,11 @@ fn rfs_get_blocks_by_hash(hash: &str) -> Result> { } /// Get blocks uploaded by the current user -/// +/// /// # Arguments /// * `page` - Optional page number (1-based) /// * `per_page` - Optional number of items per page -/// +/// /// # Returns /// JSON string containing user's blocks information fn rfs_get_user_blocks_impl( @@ -669,10 +657,8 @@ fn rfs_get_user_blocks_impl( let page_i32 = page.and_then(|p| p.try_into().ok()); let per_page_i32 = per_page.and_then(|p| p.try_into().ok()); - let result = runtime.block_on(async { - client.get_user_blocks(page_i32, per_page_i32).await - }); - + let result = runtime.block_on(async { client.get_user_blocks(page_i32, per_page_i32).await }); + match result { Ok(user_blocks) => { // Convert user blocks to JSON string for Rhai @@ -691,15 +677,19 @@ fn rfs_get_user_blocks_impl( } /// Upload a block to the RFS server -/// +/// /// # Arguments /// * `file_hash` - The hash of the file this block belongs to /// * `index` - The index of the block in the file /// * `data` - The block data as a byte array -/// +/// /// # Returns /// The hash of the uploaded block -fn rfs_upload_block(file_hash: &str, index: rhai::INT, data: rhai::Blob) -> Result> { +fn rfs_upload_block( + file_hash: &str, + index: rhai::INT, + data: rhai::Blob, +) -> Result> { let runtime_mutex = get_runtime()?; let runtime_guard = runtime_mutex.lock().map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( @@ -718,15 +708,14 @@ fn rfs_upload_block(file_hash: &str, index: rhai::INT, data: rhai::Blob) -> Resu })?; // Convert index to i64 for the API - let index_i64 = index as i64; - + let index_i64 = index; + // Convert the blob to Vec let data_vec = data.to_vec(); - let result = runtime.block_on(async { - client.upload_block(file_hash, index_i64, data_vec).await - }); - + let result = + runtime.block_on(async { client.upload_block(file_hash, index_i64, data_vec).await }); + result.map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Failed to upload block: {}", e).into(), @@ -747,7 +736,6 @@ fn rfs_get_user_blocks(params: Map) -> Result> { rfs_get_user_blocks_impl(page, per_page) } - /// Rhai-facing adapter: accept params map with optional keys: page, per_page fn rfs_list_blocks(params: Map) -> Result> { // Extract optional page and per_page from the map @@ -776,7 +764,11 @@ fn rfs_list_blocks(params: Map) -> Result> { /// # Returns /// /// * `Result<(), Box>` - Ok(()) if download was successful, error otherwise -fn rfs_download_file(file_id: &str, output_path: &str, verify: bool) -> Result<(), Box> { +fn rfs_download_file( + file_id: &str, + output_path: &str, + verify: bool, +) -> Result<(), Box> { let runtime_mutex = get_runtime()?; let runtime_guard = runtime_mutex.lock().map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( @@ -795,8 +787,10 @@ fn rfs_download_file(file_id: &str, output_path: &str, verify: bool) -> Result<( })?; let download_options = Some(DownloadOptions { verify }); - let result = runtime.block_on(async { - client.download_file(file_id, output_path, download_options).await + let result = runtime.block_on(async { + client + .download_file(file_id, output_path, download_options) + .await }); result.map_err(|e| { @@ -818,7 +812,11 @@ fn rfs_download_file(file_id: &str, output_path: &str, verify: bool) -> Result<( /// # Returns /// /// * `Result>` - File ID of the uploaded file -pub fn rfs_upload_file(file_path: &str, chunk_size: rhai::INT, verify: bool) -> Result> { +pub fn rfs_upload_file( + file_path: &str, + chunk_size: rhai::INT, + verify: bool, +) -> Result> { let runtime_mutex = get_runtime()?; let runtime_guard = runtime_mutex.lock().map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( @@ -838,7 +836,11 @@ pub fn rfs_upload_file(file_path: &str, chunk_size: rhai::INT, verify: bool) -> })?; let upload_options = Some(UploadOptions { - chunk_size: if chunk_size > 0 { Some(chunk_size as usize) } else { None }, + chunk_size: if chunk_size > 0 { + Some(chunk_size as usize) + } else { + None + }, verify, }); @@ -857,11 +859,11 @@ pub fn rfs_upload_file(file_path: &str, chunk_size: rhai::INT, verify: bool) -> // ============================================================================= /// Get website content from the RFS server -/// +/// /// # Arguments /// * `website_id` - The ID of the website /// * `path` - The path to the content within the website -/// +/// /// # Returns /// The website content as a string fn rfs_get_website(website_id: &str, path: &str) -> Result> { @@ -882,11 +884,14 @@ fn rfs_get_website(website_id: &str, path: &str) -> Result Result Result> { @@ -977,9 +982,9 @@ fn rfs_list_flists() -> Result> { rhai::Position::NONE, )) })?; - + let result = runtime.block_on(async { client.list_flists().await }); - + match result { Ok(flists) => { // Convert HashMap to JSON string for Rhai @@ -998,10 +1003,10 @@ fn rfs_list_flists() -> Result> { } /// Get FList creation state by job ID -/// +/// /// # Arguments /// * `job_id` - Job ID returned from create_flist -/// +/// /// # Returns /// JSON string containing FList state information fn rfs_get_flist_state(job_id: &str) -> Result> { @@ -1022,9 +1027,9 @@ fn rfs_get_flist_state(job_id: &str) -> Result> { rhai::Position::NONE, )) })?; - + let result = runtime.block_on(async { client.get_flist_state(job_id).await }); - + match result { Ok(state) => { // Convert state to JSON string for Rhai @@ -1043,10 +1048,10 @@ fn rfs_get_flist_state(job_id: &str) -> Result> { } /// Preview an FList's contents -/// +/// /// # Arguments /// * `flist_path` - Path to the FList -/// +/// /// # Returns /// JSON string containing FList preview information fn rfs_preview_flist(flist_path: &str) -> Result> { @@ -1067,9 +1072,9 @@ fn rfs_preview_flist(flist_path: &str) -> Result> { rhai::Position::NONE, )) })?; - + let result = runtime.block_on(async { client.preview_flist(flist_path).await }); - + match result { Ok(preview) => { // Convert preview to JSON string for Rhai @@ -1088,11 +1093,11 @@ fn rfs_preview_flist(flist_path: &str) -> Result> { } /// Download an FList file from the RFS server -/// +/// /// # Arguments /// * `flist_path` - Path to the FList to download (e.g., "flists/user/example.fl") /// * `output_path` - Local path where the FList will be saved -/// +/// /// # Returns /// Empty string on success, error on failure fn rfs_download_flist(flist_path: &str, output_path: &str) -> Result> { @@ -1113,10 +1118,8 @@ fn rfs_download_flist(flist_path: &str, output_path: &str) -> Result Ok(String::new()), Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime( @@ -1127,16 +1130,16 @@ fn rfs_download_flist(flist_path: &str, output_path: &str) -> Result, poll_interval_ms: Option, ) -> Result> { @@ -1163,10 +1166,9 @@ fn rfs_wait_for_flist_creation_impl( progress_callback: None, }; - let result = runtime.block_on(async { - client.wait_for_flist_creation(job_id, Some(options)).await - }); - + let result = + runtime.block_on(async { client.wait_for_flist_creation(job_id, Some(options)).await }); + match result { Ok(state) => { // Convert state to JSON string for Rhai @@ -1198,4 +1200,4 @@ fn rfs_wait_for_flist_creation(job_id: &str, params: Map) -> Result()); rfs_wait_for_flist_creation_impl(job_id, timeout_seconds, poll_interval_ms) -} \ No newline at end of file +} diff --git a/packages/clients/rfsclient/src/types.rs b/packages/clients/rfsclient/src/types.rs index c249699..b71fe96 100644 --- a/packages/clients/rfsclient/src/types.rs +++ b/packages/clients/rfsclient/src/types.rs @@ -1,8 +1,7 @@ // Re-export common types from OpenAPI client for convenience pub use openapi::models::{ - BlockDownloadsResponse, BlocksResponse, FileInfo, - FileUploadResponse, FlistBody, FlistState, Job, ListBlocksResponse, - PreviewResponse, ResponseResult, SignInResponse, VerifyBlocksResponse, + BlockDownloadsResponse, BlocksResponse, FileInfo, FileUploadResponse, FlistBody, FlistState, + Job, ListBlocksResponse, PreviewResponse, ResponseResult, SignInResponse, VerifyBlocksResponse, }; /// Authentication credentials for the RFS server @@ -74,10 +73,10 @@ pub struct FlistOptions { pub struct WaitOptions { /// Maximum time to wait in seconds pub timeout_seconds: u64, - + /// Polling interval in milliseconds pub poll_interval_ms: u64, - + /// Optional progress callback pub progress_callback: Option>, } @@ -88,7 +87,14 @@ impl std::fmt::Debug for WaitOptions { f.debug_struct("WaitOptions") .field("timeout_seconds", &self.timeout_seconds) .field("poll_interval_ms", &self.poll_interval_ms) - .field("progress_callback", &if self.progress_callback.is_some() { "Some(...)" } else { "None" }) + .field( + "progress_callback", + &if self.progress_callback.is_some() { + "Some(...)" + } else { + "None" + }, + ) .finish() } } @@ -107,7 +113,7 @@ impl Clone for WaitOptions { impl Default for WaitOptions { fn default() -> Self { Self { - timeout_seconds: 300, // 5 minutes default timeout + timeout_seconds: 300, // 5 minutes default timeout poll_interval_ms: 1000, // 1 second default polling interval progress_callback: None, } diff --git a/packages/clients/rfsclient/tests/rhai_integration_tests.rs b/packages/clients/rfsclient/tests/rhai_integration_tests.rs index fb5ef4c..f62b81b 100644 --- a/packages/clients/rfsclient/tests/rhai_integration_tests.rs +++ b/packages/clients/rfsclient/tests/rhai_integration_tests.rs @@ -1,7 +1,7 @@ //! Integration tests for RFS client Rhai wrappers //! //! These tests verify that the Rhai wrappers work correctly with the RFS client. -//! +//! //! Test Categories: //! - Unit tests: Test wrapper logic without requiring a running server //! - Integration tests: Test with a real RFS server (when available) @@ -15,7 +15,14 @@ use tempfile::NamedTempFile; fn is_server_running(url: &str) -> bool { // Try to make a simple HTTP request to check if server is available match std::process::Command::new("curl") - .args(["-s", "-o", "/dev/null", "-w", "%{http_code}", &format!("{}/api/v1", url)]) + .args([ + "-s", + "-o", + "/dev/null", + "-w", + "%{http_code}", + &format!("{}/api/v1", url), + ]) .output() { Ok(output) => { @@ -39,15 +46,15 @@ const TEST_PASSWORD: &str = "password"; fn test_rhai_engine_setup() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Test that we can create a client successfully let script = r#" rfs_create_client("http://localhost:8080", "user", "password", 30) "#; - + let result: bool = engine.eval(script)?; assert!(result); - + Ok(()) } @@ -56,15 +63,15 @@ fn test_rhai_engine_setup() -> Result<(), Box> { fn test_rfs_create_client() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + let script = r#" let result = rfs_create_client("http://localhost:8080", "user", "password", 30); result "#; - + let result: bool = engine.eval(script)?; assert!(result); - + Ok(()) } @@ -73,15 +80,15 @@ fn test_rfs_create_client() -> Result<(), Box> { fn test_rfs_create_client_no_credentials() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + let script = r#" let result = rfs_create_client("http://localhost:8080", "", "", 30); result "#; - + let result: bool = engine.eval(script)?; assert!(result); - + Ok(()) } @@ -92,36 +99,48 @@ fn test_rfs_flist_management_integration() { println!("Skipping FList integration test - no server detected"); return; } - + let mut engine = Engine::new(); register_rfs_module(&mut engine).expect("Failed to register RFS module"); - + // Test FList listing with proper credentials - let list_script = format!(r#" + let list_script = format!( + r#" rfs_create_client("{}", "{}", "{}", 30); rfs_authenticate(); rfs_list_flists() - "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD); - + "#, + TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD + ); + let result = engine.eval::(&list_script); match result { Ok(flists_json) => { println!("FLists retrieved: {}", flists_json); // Should be valid JSON - assert!(serde_json::from_str::(&flists_json).is_ok(), - "FList data should be valid JSON"); + assert!( + serde_json::from_str::(&flists_json).is_ok(), + "FList data should be valid JSON" + ); } Err(e) => { let error_msg = e.to_string(); println!("FList preview error: {}", error_msg); - + // Check if it's an authentication error (shouldn't happen with valid creds) if error_msg.contains("Authentication") { - panic!("❌ Authentication should work with valid credentials: {}", error_msg); + panic!( + "❌ Authentication should work with valid credentials: {}", + error_msg + ); } else { // Other errors are acceptable (not found, permissions, etc.) println!("Server error (may be expected): {}", error_msg); - assert!(error_msg.contains("OpenAPI") || error_msg.contains("FList") || error_msg.contains("not found")); + assert!( + error_msg.contains("OpenAPI") + || error_msg.contains("FList") + || error_msg.contains("not found") + ); } } } @@ -133,23 +152,26 @@ fn test_rfs_create_flist_integration() { println!("Skipping FList creation test - no server detected"); return; } - + let mut engine = Engine::new(); register_rfs_module(&mut engine).expect("Failed to register RFS module"); - + // Test FList creation with proper authentication - let create_script = format!(r#" + let create_script = format!( + r#" rfs_create_client("{}", "{}", "{}", 30); rfs_authenticate(); rfs_create_flist("busybox:latest", "docker.io", "", "") - "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD); - + "#, + TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD + ); + let result = engine.eval::(&create_script); match result { Ok(job_id) => { println!("✅ FList creation job started: {}", job_id); assert!(!job_id.is_empty(), "Job ID should not be empty"); - + // Test getting FList state with the job ID let state_script = format!("rfs_get_flist_state(\"{}\")", job_id); let state_result = engine.eval::(&state_script); @@ -166,12 +188,15 @@ fn test_rfs_create_flist_integration() { Err(e) => { let error_msg = e.to_string(); println!("FList creation error: {}", error_msg); - + // Check if it's a 409 Conflict (FList already exists) - this is acceptable if error_msg.contains("409 Conflict") { println!("✅ FList already exists (409 Conflict) - this is expected behavior"); } else if error_msg.contains("Authentication") { - panic!("❌ Authentication should work with valid credentials: {}", error_msg); + panic!( + "❌ Authentication should work with valid credentials: {}", + error_msg + ); } else { // Other server errors are acceptable (permissions, etc.) println!("Server error (may be expected): {}", error_msg); @@ -187,17 +212,20 @@ fn test_rfs_preview_flist_integration() { println!("Skipping FList preview test - no server detected"); return; } - + let mut engine = Engine::new(); register_rfs_module(&mut engine).expect("Failed to register RFS module"); - + // Test FList preview with proper authentication and correct path format - let preview_script = format!(r#" + let preview_script = format!( + r#" rfs_create_client("{}", "{}", "{}", 30); rfs_authenticate(); rfs_preview_flist("flists/user/alpine-latest.fl") - "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD); - + "#, + TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD + ); + let result = engine.eval::(&preview_script); match result { Ok(preview_json) => { @@ -206,10 +234,17 @@ fn test_rfs_preview_flist_integration() { } Err(e) => { let error_msg = e.to_string(); - println!("Expected FList preview error (not found/auth): {}", error_msg); + println!( + "Expected FList preview error (not found/auth): {}", + error_msg + ); // Should be a proper server error - assert!(error_msg.contains("Authentication") || error_msg.contains("OpenAPI") || - error_msg.contains("FList") || error_msg.contains("not found")); + assert!( + error_msg.contains("Authentication") + || error_msg.contains("OpenAPI") + || error_msg.contains("FList") + || error_msg.contains("not found") + ); } } } @@ -219,12 +254,12 @@ fn test_rfs_preview_flist_integration() { fn test_rfs_get_system_info_wrapper() { let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + let script = r#" rfs_create_client("http://localhost:8080", "", "", 30); rfs_get_system_info() "#; - + let result = engine.eval::(script); match result { Ok(info) => { @@ -246,12 +281,12 @@ fn test_rfs_get_system_info_wrapper() { fn test_rfs_authenticate_wrapper() { let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + let script = r#" rfs_create_client("http://localhost:8080", "user", "password", 30); rfs_authenticate() "#; - + let result = engine.eval::(script); match result { Ok(success) => { @@ -273,17 +308,20 @@ fn test_rfs_authenticate_wrapper() { fn test_rfs_upload_file_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a temporary file for testing let temp_file = NamedTempFile::new()?; fs::write(&temp_file, b"test content")?; let file_path = temp_file.path().to_string_lossy(); - - let script = format!(r#" + + let script = format!( + r#" rfs_create_client("http://localhost:8080", "", "", 30); rfs_upload_file("{}", 0, false) - "#, file_path); - + "#, + file_path + ); + let result = engine.eval::(&script); match result { Ok(upload_result) => { @@ -298,7 +336,7 @@ fn test_rfs_upload_file_wrapper() -> Result<(), Box> { assert!(error_msg.contains("RFS error") || error_msg.contains("OpenAPI")); } } - + Ok(()) } @@ -307,7 +345,7 @@ fn test_rfs_upload_file_wrapper() -> Result<(), Box> { fn test_complete_rhai_script() { let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + let script = r#" // Create client let client_created = rfs_create_client("http://localhost:8080", "user", "password", 60); @@ -315,7 +353,7 @@ fn test_complete_rhai_script() { // Return success if we got this far client_created "#; - + let result: bool = engine.eval(script).unwrap(); assert!(result); } @@ -325,17 +363,17 @@ fn test_complete_rhai_script() { fn test_error_handling() { let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + // Test calling a protected endpoint without authentication - should fail // Note: get_system_info is NOT protected, but create_flist IS protected let script = r#" rfs_create_client("http://localhost:8080", "", "", 30); rfs_create_flist("test:latest", "docker.io", "", "") "#; - + let result = engine.eval::(script); assert!(result.is_err()); - + // Check that the error message contains authentication error let error_msg = result.unwrap_err().to_string(); println!("Expected authentication error: {}", error_msg); @@ -347,23 +385,26 @@ fn test_error_handling() { fn test_rfs_is_authenticated_wrapper() { let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + // Test without authenticating first let script1 = r#" rfs_create_client("http://localhost:8080", "", "", 30); rfs_is_authenticated() "#; - + let result1 = engine.eval::(script1).unwrap(); - assert!(!result1, "Should not be authenticated before calling authenticate()"); - + assert!( + !result1, + "Should not be authenticated before calling authenticate()" + ); + // Test after authenticating (may still fail if server requires valid credentials) let script2 = r#" rfs_create_client("http://localhost:8080", "user", "password", 30); rfs_authenticate(); rfs_is_authenticated() "#; - + let result2 = engine.eval::(script2); match result2 { Ok(auth_status) => { @@ -382,12 +423,12 @@ fn test_rfs_is_authenticated_wrapper() { fn test_rfs_health_check_wrapper() { let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + let script = r#" rfs_create_client("http://localhost:8080", "", "", 30); rfs_health_check() "#; - + let result = engine.eval::(script); match result { Ok(health_status) => { @@ -400,9 +441,9 @@ fn test_rfs_health_check_wrapper() { println!("Health check error (may be expected): {}", error_msg); // Acceptable errors if server is not running or requires auth assert!( - error_msg.contains("RFS error") || - error_msg.contains("OpenAPI") || - error_msg.contains("failed") + error_msg.contains("RFS error") + || error_msg.contains("OpenAPI") + || error_msg.contains("failed") ); } } @@ -415,17 +456,20 @@ fn test_rfs_get_website_wrapper() { println!("Skipping website test - no server detected"); return; } - + let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + // Test with a non-existent website (should fail gracefully) - let script = format!(r#" + let script = format!( + r#" rfs_create_client("{}", "{}", "{}", 30); rfs_authenticate(); rfs_get_website("nonexistent-website", "index.html") - "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD); - + "#, + TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD + ); + let result = engine.eval::(&script); match result { Ok(content) => { @@ -437,10 +481,10 @@ fn test_rfs_get_website_wrapper() { let error_msg = e.to_string(); println!("Expected website error: {}", error_msg); assert!( - error_msg.contains("404") || - error_msg.contains("not found") || - error_msg.contains("OpenAPI") || - error_msg.contains("RFS error") + error_msg.contains("404") + || error_msg.contains("not found") + || error_msg.contains("OpenAPI") + || error_msg.contains("RFS error") ); } } @@ -455,7 +499,7 @@ fn test_rfs_get_website_wrapper() { fn test_rfs_list_blocks_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a client first let create_script = format!( r#" @@ -463,10 +507,10 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box> { "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD ); - + let result: bool = engine.eval(&create_script)?; assert!(result, "Failed to create RFS client"); - + // Authenticate before invoking operations that require it let auth_script = r#" rfs_authenticate() @@ -481,10 +525,10 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box> { } true "#; - + let result: bool = engine.eval(list_script)?; assert!(result, "Failed to list blocks"); - + Ok(()) } @@ -493,7 +537,7 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box> { fn test_rfs_download_block_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a client first let create_script = format!( r#" @@ -501,18 +545,18 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box> { "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD ); - + let result: bool = engine.eval(&create_script)?; assert!(result, "Failed to create RFS client"); - + // Authenticate before invoking operations that require it let authed: bool = engine.eval(r#" rfs_authenticate() "#)?; assert!(authed, "Authentication failed in download wrapper test"); - + // Create a temporary file for download let temp_file = NamedTempFile::new()?; let temp_path = temp_file.path().to_str().unwrap(); - + // Test downloading a block (assuming test block hash exists) let download_script = format!( r#" @@ -522,13 +566,13 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box> { }} true "#, - temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths + temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths ); - + // This might fail if the test block doesn't exist, but we're testing the wrapper, not the actual download let result: bool = engine.eval(&download_script).unwrap_or_else(|_| true); assert!(result, "Failed to execute download block script"); - + Ok(()) } @@ -537,7 +581,7 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box> { fn test_rfs_verify_blocks_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a client first let create_script = format!( r#" @@ -545,10 +589,10 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box> { "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD ); - + let result: bool = engine.eval(&create_script)?; assert!(result, "Failed to create RFS client"); - + // Test verifying blocks with a test hash let verify_script = r#" let hashes = "[\"test_block_hash\"]"; @@ -558,10 +602,10 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box> { }} true "#; - + let result: bool = engine.eval(verify_script)?; assert!(result, "Failed to verify blocks"); - + Ok(()) } @@ -570,7 +614,7 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box> { fn test_rfs_get_block_info_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a client first let create_script = format!( r#" @@ -578,10 +622,10 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box> { "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD ); - + let result: bool = engine.eval(&create_script)?; assert!(result, "Failed to create RFS client"); - + // Test getting block info with a test hash let info_script = r#" let result = rfs_get_blocks_by_hash("test_block_hash"); @@ -590,7 +634,7 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box> { } true "#; - + match engine.eval::(info_script) { Ok(result) => { assert!(result, "Failed to get block info"); @@ -600,10 +644,10 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box> { let error_msg = e.to_string(); println!("Block info error (may be expected): {}", error_msg); assert!( - error_msg.contains("404") || - error_msg.contains("not found") || - error_msg.contains("OpenAPI") || - error_msg.contains("RFS error") + error_msg.contains("404") + || error_msg.contains("not found") + || error_msg.contains("OpenAPI") + || error_msg.contains("RFS error") ); Ok(()) } @@ -619,7 +663,7 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box> { fn test_rfs_download_file_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a client first let create_script = format!( r#" @@ -627,14 +671,14 @@ fn test_rfs_download_file_wrapper() -> Result<(), Box> { "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD ); - + let result: bool = engine.eval(&create_script)?; assert!(result, "Failed to create RFS client"); - + // Create a temporary file for download let temp_file = NamedTempFile::new()?; let temp_path = temp_file.path().to_str().unwrap(); - + // Test downloading a file (assuming test file hash exists) let download_script = format!( r#" @@ -645,13 +689,13 @@ fn test_rfs_download_file_wrapper() -> Result<(), Box> { }} true "#, - temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths + temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths ); - + // This might fail if the test file doesn't exist, but we're testing the wrapper let result: bool = engine.eval(&download_script).unwrap_or_else(|_| true); assert!(result, "Failed to execute download file script"); - + Ok(()) } @@ -678,7 +722,7 @@ fn test_flist_operations_workflow() -> Result<(), Box> { let temp_dir = tempfile::tempdir()?; let output_path = temp_dir.path().join("downloaded_flist.fl"); let output_path_str = output_path.to_str().unwrap(); - + let mut engine = Engine::new(); register_rfs_module(&mut engine).expect("Failed to register RFS module"); @@ -808,19 +852,19 @@ fn test_flist_operations_workflow() -> Result<(), Box> { "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD ); - + // Add a helper function to parse JSON in Rhai engine.register_fn("parse_json", |json_str: &str| -> String { // Just return the JSON string as is - Rhai can work with it directly json_str.to_string() }); - + // Execute the script match engine.eval::(&script) { Ok(success) => { assert!(success, "FList operations workflow test failed"); Ok(()) - }, + } Err(e) => { println!("Error in FList operations workflow test: {}", e); // Don't fail the test if the server doesn't have the expected data @@ -843,7 +887,7 @@ fn test_flist_operations_workflow() -> Result<(), Box> { fn test_rfs_download_flist_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a client first let create_script = format!( r#" @@ -851,14 +895,14 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box> { "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD ); - + let result: bool = engine.eval(&create_script)?; assert!(result, "Failed to create RFS client"); - + // Create a temporary file for download let temp_file = NamedTempFile::new()?; let temp_path = temp_file.path().to_str().unwrap(); - + // Test downloading an FList (assuming test flist exists) let download_script = format!( r#" @@ -868,13 +912,13 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box> { }} true "#, - temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths + temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths ); - + // This might fail if the test flist doesn't exist, but we're testing the wrapper let result: bool = engine.eval(&download_script).unwrap_or_else(|_| true); assert!(result, "Failed to execute download flist script"); - + Ok(()) } @@ -883,7 +927,7 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box> { fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box> { let mut engine = Engine::new(); register_rfs_module(&mut engine)?; - + // Create a client first let create_script = format!( r#" @@ -891,14 +935,14 @@ fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box Result<(), Box(wait_script); match eval_res { - Ok(s) => panic!("Expected failure for dummy job id, but got success with result: {}", s), + Ok(s) => panic!( + "Expected failure for dummy job id, but got success with result: {}", + s + ), Err(e) => { let msg = e.to_string(); - assert!(msg.contains("Operation timed out"), "Unexpected error message: {}", msg); + assert!( + msg.contains("Operation timed out"), + "Unexpected error message: {}", + msg + ); } } - + Ok(()) } @@ -925,18 +976,24 @@ fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box(&script); match result { Ok(info) => { @@ -954,19 +1011,25 @@ fn test_rfs_get_system_info_with_server() { #[test] fn test_rfs_authenticate_with_server() { if !is_server_running(TEST_SERVER_URL) { - println!("Skipping integration test - no RFS server running at {}", TEST_SERVER_URL); + println!( + "Skipping integration test - no RFS server running at {}", + TEST_SERVER_URL + ); return; } let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - + // Test with dummy credentials (will likely fail, but tests the flow) - let script = format!(r#" + let script = format!( + r#" rfs_create_client("{}", "{}", "{}", 30); rfs_authenticate() - "#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD); - + "#, + TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD + ); + let result = engine.eval::(&script); match result { Ok(success) => { @@ -974,7 +1037,10 @@ fn test_rfs_authenticate_with_server() { assert!(success); } Err(e) => { - println!("Expected authentication failure with dummy credentials: {}", e); + println!( + "Expected authentication failure with dummy credentials: {}", + e + ); // This is expected with dummy credentials assert!(e.to_string().contains("Authentication failed")); } @@ -985,14 +1051,18 @@ fn test_rfs_authenticate_with_server() { #[test] fn test_complete_workflow_with_server() { if !is_server_running(TEST_SERVER_URL) { - println!("Skipping integration test - no RFS server running at {}", TEST_SERVER_URL); + println!( + "Skipping integration test - no RFS server running at {}", + TEST_SERVER_URL + ); return; } let mut engine = Engine::new(); register_rfs_module(&mut engine).unwrap(); - - let script = format!(r#" + + let script = format!( + r#" // Create client let client_created = rfs_create_client("{}", "", "", 60); print("Client created: " + client_created); @@ -1003,8 +1073,10 @@ fn test_complete_workflow_with_server() { // Return success client_created && info_result.len() > 0 - "#, TEST_SERVER_URL); - + "#, + TEST_SERVER_URL + ); + let result = engine.eval::(&script); match result { Ok(success) => {