use primitive_types::U512; use quantus_miner::*; use resonance_miner_api::*; use warp::test::request; use warp::Filter; // Import shared API types #[tokio::test] async fn test_mine_endpoint() { let state = MiningState::new(); let state_clone = state.clone(); let state_filter = warp::any().map(move || state_clone.clone()); let mine_route = warp::post() .and(warp::path("mine")) .and(warp::body::json()) .and(state_filter.clone()) .and_then(handle_mine_request); // Test valid request let valid_request = MiningRequest { job_id: "test".to_string(), mining_hash: "a".repeat(64), distance_threshold: "1000".to_string(), nonce_start: "0".repeat(128), nonce_end: "1".repeat(128), }; let resp = request() .method("POST") .path("/mine") .json(&valid_request) .reply(&mine_route) .await; assert_eq!(resp.status(), 200); let body: MiningResponse = serde_json::from_slice(resp.body()).unwrap(); assert_eq!(body.status, ApiResponseStatus::Accepted); assert_eq!(body.job_id, "test"); // Test duplicate job ID let resp = request() .method("POST") .path("/mine") .json(&valid_request) .reply(&mine_route) .await; assert_eq!(resp.status(), 409); let body: MiningResponse = serde_json::from_slice(resp.body()).unwrap(); assert_eq!(body.status, ApiResponseStatus::Error); assert!(body.message.is_some()); assert!(body.message.unwrap().contains("Job already exists")); // Test invalid request let invalid_request = MiningRequest { job_id: "".to_string(), // Empty job ID mining_hash: "a".repeat(64), distance_threshold: "1000".to_string(), nonce_start: "0".repeat(128), nonce_end: "1".repeat(128), }; let resp = request() .method("POST") .path("/mine") .json(&invalid_request) .reply(&mine_route) .await; assert_eq!(resp.status(), 400); } #[tokio::test] async fn test_result_endpoint() { let state = MiningState::new(); let state_clone = state.clone(); let state_filter = warp::any().map(move || state_clone.clone()); // First create a job let job = MiningJob::new([0; 32], U512::from(1000), U512::from(0), U512::from(1000)); state.add_job("test".to_string(), job).await.unwrap(); let result_route = warp::get() .and(warp::path("result")) .and(warp::path::param()) .and(state_filter.clone()) .and_then(handle_result_request); // Test existing job let resp = request() .method("GET") .path("/result/test") .reply(&result_route) .await; assert_eq!(resp.status(), 200); let body: MiningResult = serde_json::from_slice(resp.body()).unwrap(); assert_eq!(body.status, ApiResponseStatus::Running); assert_eq!(body.job_id, "test"); // Test non-existent job let resp = request() .method("GET") .path("/result/nonexistent") .reply(&result_route) .await; assert_eq!(resp.status(), 404); let body: MiningResult = serde_json::from_slice(resp.body()).unwrap(); assert_eq!(body.status, ApiResponseStatus::NotFound); } #[tokio::test] async fn test_cancel_endpoint() { let state = MiningState::new(); let state_clone = state.clone(); let state_filter = warp::any().map(move || state_clone.clone()); // First create a job let job = MiningJob::new([0; 32], U512::from(1000), U512::from(0), U512::from(1000)); state.add_job("test".to_string(), job).await.unwrap(); let cancel_route = warp::post() .and(warp::path("cancel")) .and(warp::path::param()) .and(state_filter.clone()) .and_then(handle_cancel_request); // Test cancel existing job let resp = request() .method("POST") .path("/cancel/test") .reply(&cancel_route) .await; assert_eq!(resp.status(), 200); let body: MiningResponse = serde_json::from_slice(resp.body()).unwrap(); assert_eq!(body.status, ApiResponseStatus::Cancelled); assert_eq!(body.job_id, "test"); // Test cancel non-existent job let resp = request() .method("POST") .path("/cancel/nonexistent") .reply(&cancel_route) .await; assert_eq!(resp.status(), 404); let body: MiningResponse = serde_json::from_slice(resp.body()).unwrap(); assert_eq!(body.status, ApiResponseStatus::NotFound); } #[tokio::test] async fn test_concurrent_access() { let state = MiningState::new(); let state_clone = state.clone(); let state_filter = warp::any().map(move || state_clone.clone()); // Create multiple jobs concurrently let mut handles = vec![]; for i in 0..10 { let state = state.clone(); let handle = tokio::spawn(async move { let job = MiningJob::new([0; 32], U512::from(1000), U512::from(0), U512::from(1000)); state.add_job(format!("test{}", i), job).await }); handles.push(handle); } // Wait for all jobs to be created for handle in handles { assert!(handle.await.unwrap().is_ok()); } // Verify all jobs exist for i in 0..10 { assert!(state.get_job(&format!("test{}", i)).await.is_some()); } // Test concurrent result checks let result_route = warp::get() .and(warp::path("result")) .and(warp::path::param()) .and(state_filter.clone()) .and_then(handle_result_request); let mut result_handles = vec![]; for i in 0..10 { let route = result_route.clone(); let handle = tokio::spawn(async move { request() .method("GET") .path(&format!("/result/test{}", i)) .reply(&route) .await }); result_handles.push(handle); } // Verify all result checks succeed for handle in result_handles { let resp = handle.await.unwrap(); assert_eq!(resp.status(), 200); let body: MiningResult = serde_json::from_slice(resp.body()).unwrap(); assert_eq!(body.status, ApiResponseStatus::Running); } }