Implement native and WASM WebSocket client for sigsocket communication
- Added `NativeClient` for non-WASM environments with automatic reconnection and message handling. - Introduced `WasmClient` for WASM environments, supporting WebSocket communication and reconnection logic. - Created protocol definitions for `SignRequest` and `SignResponse` with serialization and deserialization. - Developed integration tests for the client functionality and sign request handling. - Implemented WASM-specific tests to ensure compatibility and functionality in browser environments.
This commit is contained in:
		
							
								
								
									
										162
									
								
								sigsocket_client/tests/integration_test.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								sigsocket_client/tests/integration_test.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
//! Integration tests for sigsocket_client
 | 
			
		||||
 | 
			
		||||
use sigsocket_client::{SigSocketClient, SignRequest, SignResponse, SignRequestHandler, Result, SigSocketError};
 | 
			
		||||
 | 
			
		||||
/// Test sign request handler
 | 
			
		||||
struct TestSignHandler {
 | 
			
		||||
    should_approve: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TestSignHandler {
 | 
			
		||||
    fn new(should_approve: bool) -> Self {
 | 
			
		||||
        Self { should_approve }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SignRequestHandler for TestSignHandler {
 | 
			
		||||
    fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>> {
 | 
			
		||||
        if self.should_approve {
 | 
			
		||||
            // Create a test signature
 | 
			
		||||
            let signature = format!("test_signature_for_{}", request.id);
 | 
			
		||||
            Ok(signature.into_bytes())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(SigSocketError::Other("User rejected request".to_string()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_sign_request_creation() {
 | 
			
		||||
    let request = SignRequest::new("test-123", "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    assert_eq!(request.id, "test-123");
 | 
			
		||||
    assert_eq!(request.message, "dGVzdCBtZXNzYWdl");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_sign_request_message_decoding() {
 | 
			
		||||
    let request = SignRequest::new("test-123", "dGVzdCBtZXNzYWdl"); // "test message" in base64
 | 
			
		||||
    
 | 
			
		||||
    let bytes = request.message_bytes().unwrap();
 | 
			
		||||
    assert_eq!(bytes, b"test message");
 | 
			
		||||
    
 | 
			
		||||
    let hex = request.message_hex().unwrap();
 | 
			
		||||
    assert_eq!(hex, hex::encode(b"test message"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_sign_response_creation() {
 | 
			
		||||
    let response = SignResponse::new("test-123", "dGVzdCBtZXNzYWdl", "c2lnbmF0dXJl");
 | 
			
		||||
    assert_eq!(response.id, "test-123");
 | 
			
		||||
    assert_eq!(response.message, "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    assert_eq!(response.signature, "c2lnbmF0dXJl");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_sign_response_from_request() {
 | 
			
		||||
    let request = SignRequest::new("test-123", "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    let signature = b"test_signature";
 | 
			
		||||
    
 | 
			
		||||
    let response = SignResponse::from_request_and_signature(&request, signature);
 | 
			
		||||
    assert_eq!(response.id, request.id);
 | 
			
		||||
    assert_eq!(response.message, request.message);
 | 
			
		||||
    assert_eq!(response.signature_bytes().unwrap(), signature);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_protocol_serialization() {
 | 
			
		||||
    // Test SignRequest serialization
 | 
			
		||||
    let request = SignRequest::new("req-456", "SGVsbG8gV29ybGQ="); // "Hello World" in base64
 | 
			
		||||
    let json = serde_json::to_string(&request).unwrap();
 | 
			
		||||
    let deserialized: SignRequest = serde_json::from_str(&json).unwrap();
 | 
			
		||||
    assert_eq!(request, deserialized);
 | 
			
		||||
    
 | 
			
		||||
    // Test SignResponse serialization
 | 
			
		||||
    let response = SignResponse::new("req-456", "SGVsbG8gV29ybGQ=", "c2lnbmF0dXJlXzEyMw==");
 | 
			
		||||
    let json = serde_json::to_string(&response).unwrap();
 | 
			
		||||
    let deserialized: SignResponse = serde_json::from_str(&json).unwrap();
 | 
			
		||||
    assert_eq!(response, deserialized);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_client_creation() {
 | 
			
		||||
    let public_key = hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    
 | 
			
		||||
    let client = SigSocketClient::new("ws://localhost:8080/ws", public_key.clone()).unwrap();
 | 
			
		||||
    assert_eq!(client.url(), "ws://localhost:8080/ws");
 | 
			
		||||
    assert_eq!(client.public_key_hex(), hex::encode(&public_key));
 | 
			
		||||
    assert!(!client.is_connected());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_client_invalid_url() {
 | 
			
		||||
    let public_key = vec![1, 2, 3];
 | 
			
		||||
    let result = SigSocketClient::new("invalid-url", public_key);
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_client_empty_public_key() {
 | 
			
		||||
    let result = SigSocketClient::new("ws://localhost:8080/ws", vec![]);
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    if let Err(error) = result {
 | 
			
		||||
        assert!(matches!(error, SigSocketError::InvalidPublicKey(_)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_sign_handler_approval() {
 | 
			
		||||
    let handler = TestSignHandler::new(true);
 | 
			
		||||
    let request = SignRequest::new("test-789", "dGVzdA==");
 | 
			
		||||
    
 | 
			
		||||
    let result = handler.handle_sign_request(&request);
 | 
			
		||||
    assert!(result.is_ok());
 | 
			
		||||
    
 | 
			
		||||
    let signature = result.unwrap();
 | 
			
		||||
    assert_eq!(signature, b"test_signature_for_test-789");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_sign_handler_rejection() {
 | 
			
		||||
    let handler = TestSignHandler::new(false);
 | 
			
		||||
    let request = SignRequest::new("test-789", "dGVzdA==");
 | 
			
		||||
    
 | 
			
		||||
    let result = handler.handle_sign_request(&request);
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert!(matches!(result.unwrap_err(), SigSocketError::Other(_)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_error_display() {
 | 
			
		||||
    let error = SigSocketError::NotConnected;
 | 
			
		||||
    assert_eq!(error.to_string(), "Client is not connected");
 | 
			
		||||
    
 | 
			
		||||
    let error = SigSocketError::Connection("test error".to_string());
 | 
			
		||||
    assert_eq!(error.to_string(), "Connection error: test error");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Test that demonstrates the expected usage pattern
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_usage_pattern() {
 | 
			
		||||
    // 1. Create client
 | 
			
		||||
    let public_key = hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let mut client = SigSocketClient::new("ws://localhost:8080/ws", public_key).unwrap();
 | 
			
		||||
    
 | 
			
		||||
    // 2. Set handler
 | 
			
		||||
    client.set_sign_handler(TestSignHandler::new(true));
 | 
			
		||||
    
 | 
			
		||||
    // 3. Verify state
 | 
			
		||||
    assert!(!client.is_connected());
 | 
			
		||||
    
 | 
			
		||||
    // 4. Create a test request/response cycle
 | 
			
		||||
    let request = SignRequest::new("test-request", "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    let handler = TestSignHandler::new(true);
 | 
			
		||||
    let signature = handler.handle_sign_request(&request).unwrap();
 | 
			
		||||
    let response = SignResponse::from_request_and_signature(&request, &signature);
 | 
			
		||||
    
 | 
			
		||||
    // 5. Verify the response
 | 
			
		||||
    assert_eq!(response.id, request.id);
 | 
			
		||||
    assert_eq!(response.message, request.message);
 | 
			
		||||
    assert_eq!(response.signature_bytes().unwrap(), signature);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										181
									
								
								sigsocket_client/tests/wasm_tests.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								sigsocket_client/tests/wasm_tests.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
			
		||||
#![cfg(target_arch = "wasm32")]
 | 
			
		||||
//! WASM/browser tests for sigsocket_client using wasm-bindgen-test
 | 
			
		||||
 | 
			
		||||
use wasm_bindgen_test::*;
 | 
			
		||||
use sigsocket_client::{SigSocketClient, SignRequest, SignResponse, SignRequestHandler, Result, SigSocketError};
 | 
			
		||||
 | 
			
		||||
wasm_bindgen_test_configure!(run_in_browser);
 | 
			
		||||
 | 
			
		||||
/// Test sign request handler for WASM tests
 | 
			
		||||
struct TestWasmSignHandler {
 | 
			
		||||
    should_approve: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TestWasmSignHandler {
 | 
			
		||||
    fn new(should_approve: bool) -> Self {
 | 
			
		||||
        Self { should_approve }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SignRequestHandler for TestWasmSignHandler {
 | 
			
		||||
    fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>> {
 | 
			
		||||
        if self.should_approve {
 | 
			
		||||
            // Create a test signature
 | 
			
		||||
            let signature = format!("wasm_test_signature_for_{}", request.id);
 | 
			
		||||
            Ok(signature.into_bytes())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(SigSocketError::Other("User rejected request in WASM test".to_string()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_sign_request_creation_wasm() {
 | 
			
		||||
    let request = SignRequest::new("wasm-test-123", "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    assert_eq!(request.id, "wasm-test-123");
 | 
			
		||||
    assert_eq!(request.message, "dGVzdCBtZXNzYWdl");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_sign_request_message_decoding_wasm() {
 | 
			
		||||
    let request = SignRequest::new("wasm-test-123", "dGVzdCBtZXNzYWdl"); // "test message" in base64
 | 
			
		||||
    
 | 
			
		||||
    let bytes = request.message_bytes().unwrap();
 | 
			
		||||
    assert_eq!(bytes, b"test message");
 | 
			
		||||
    
 | 
			
		||||
    let hex = request.message_hex().unwrap();
 | 
			
		||||
    assert_eq!(hex, hex::encode(b"test message"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_sign_response_creation_wasm() {
 | 
			
		||||
    let response = SignResponse::new("wasm-test-123", "dGVzdCBtZXNzYWdl", "c2lnbmF0dXJl");
 | 
			
		||||
    assert_eq!(response.id, "wasm-test-123");
 | 
			
		||||
    assert_eq!(response.message, "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    assert_eq!(response.signature, "c2lnbmF0dXJl");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_sign_response_from_request_wasm() {
 | 
			
		||||
    let request = SignRequest::new("wasm-test-123", "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    let signature = b"wasm_test_signature";
 | 
			
		||||
    
 | 
			
		||||
    let response = SignResponse::from_request_and_signature(&request, signature);
 | 
			
		||||
    assert_eq!(response.id, request.id);
 | 
			
		||||
    assert_eq!(response.message, request.message);
 | 
			
		||||
    assert_eq!(response.signature_bytes().unwrap(), signature);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_protocol_serialization_wasm() {
 | 
			
		||||
    // Test SignRequest serialization
 | 
			
		||||
    let request = SignRequest::new("wasm-req-456", "SGVsbG8gV29ybGQ="); // "Hello World" in base64
 | 
			
		||||
    let json = serde_json::to_string(&request).unwrap();
 | 
			
		||||
    let deserialized: SignRequest = serde_json::from_str(&json).unwrap();
 | 
			
		||||
    assert_eq!(request, deserialized);
 | 
			
		||||
    
 | 
			
		||||
    // Test SignResponse serialization
 | 
			
		||||
    let response = SignResponse::new("wasm-req-456", "SGVsbG8gV29ybGQ=", "c2lnbmF0dXJlXzEyMw==");
 | 
			
		||||
    let json = serde_json::to_string(&response).unwrap();
 | 
			
		||||
    let deserialized: SignResponse = serde_json::from_str(&json).unwrap();
 | 
			
		||||
    assert_eq!(response, deserialized);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_client_creation_wasm() {
 | 
			
		||||
    let public_key = hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    
 | 
			
		||||
    let client = SigSocketClient::new("ws://localhost:8080/ws", public_key.clone()).unwrap();
 | 
			
		||||
    assert_eq!(client.url(), "ws://localhost:8080/ws");
 | 
			
		||||
    assert_eq!(client.public_key_hex(), hex::encode(&public_key));
 | 
			
		||||
    assert!(!client.is_connected());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_client_invalid_url_wasm() {
 | 
			
		||||
    let public_key = vec![1, 2, 3];
 | 
			
		||||
    let result = SigSocketClient::new("invalid-url", public_key);
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_client_empty_public_key_wasm() {
 | 
			
		||||
    let result = SigSocketClient::new("ws://localhost:8080/ws", vec![]);
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    if let Err(error) = result {
 | 
			
		||||
        assert!(matches!(error, SigSocketError::InvalidPublicKey(_)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_sign_handler_approval_wasm() {
 | 
			
		||||
    let handler = TestWasmSignHandler::new(true);
 | 
			
		||||
    let request = SignRequest::new("wasm-test-789", "dGVzdA==");
 | 
			
		||||
    
 | 
			
		||||
    let result = handler.handle_sign_request(&request);
 | 
			
		||||
    assert!(result.is_ok());
 | 
			
		||||
    
 | 
			
		||||
    let signature = result.unwrap();
 | 
			
		||||
    assert_eq!(signature, b"wasm_test_signature_for_wasm-test-789");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_sign_handler_rejection_wasm() {
 | 
			
		||||
    let handler = TestWasmSignHandler::new(false);
 | 
			
		||||
    let request = SignRequest::new("wasm-test-789", "dGVzdA==");
 | 
			
		||||
    
 | 
			
		||||
    let result = handler.handle_sign_request(&request);
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert!(matches!(result.unwrap_err(), SigSocketError::Other(_)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_error_display_wasm() {
 | 
			
		||||
    let error = SigSocketError::NotConnected;
 | 
			
		||||
    assert_eq!(error.to_string(), "Client is not connected");
 | 
			
		||||
    
 | 
			
		||||
    let error = SigSocketError::Connection("wasm test error".to_string());
 | 
			
		||||
    assert_eq!(error.to_string(), "Connection error: wasm test error");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Test that demonstrates the expected WASM usage pattern
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_wasm_usage_pattern() {
 | 
			
		||||
    // 1. Create client
 | 
			
		||||
    let public_key = hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let mut client = SigSocketClient::new("ws://localhost:8080/ws", public_key).unwrap();
 | 
			
		||||
    
 | 
			
		||||
    // 2. Set handler
 | 
			
		||||
    client.set_sign_handler(TestWasmSignHandler::new(true));
 | 
			
		||||
    
 | 
			
		||||
    // 3. Verify state
 | 
			
		||||
    assert!(!client.is_connected());
 | 
			
		||||
    
 | 
			
		||||
    // 4. Create a test request/response cycle
 | 
			
		||||
    let request = SignRequest::new("wasm-test-request", "dGVzdCBtZXNzYWdl");
 | 
			
		||||
    let handler = TestWasmSignHandler::new(true);
 | 
			
		||||
    let signature = handler.handle_sign_request(&request).unwrap();
 | 
			
		||||
    let response = SignResponse::from_request_and_signature(&request, &signature);
 | 
			
		||||
    
 | 
			
		||||
    // 5. Verify the response
 | 
			
		||||
    assert_eq!(response.id, request.id);
 | 
			
		||||
    assert_eq!(response.message, request.message);
 | 
			
		||||
    assert_eq!(response.signature_bytes().unwrap(), signature);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Test WASM-specific console logging (if needed)
 | 
			
		||||
#[wasm_bindgen_test]
 | 
			
		||||
fn test_wasm_console_logging() {
 | 
			
		||||
    // This test verifies that WASM console logging works
 | 
			
		||||
    web_sys::console::log_1(&"SigSocket WASM test logging works!".into());
 | 
			
		||||
    
 | 
			
		||||
    // Test that we can create and log protocol messages
 | 
			
		||||
    let request = SignRequest::new("log-test", "dGVzdA==");
 | 
			
		||||
    let json = serde_json::to_string(&request).unwrap();
 | 
			
		||||
    web_sys::console::log_1(&format!("Sign request JSON: {}", json).into());
 | 
			
		||||
    
 | 
			
		||||
    // This test always passes - it's just for verification that logging works
 | 
			
		||||
    assert!(true);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user