use creditservice_api::{CreditServiceImpl, InMemoryStorage}; use creditservice_proto::credit_service_server::CreditServiceServer; use creditservice_client::{Client, TlsConfig}; use rcgen::generate_simple_self_signed; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::oneshot; use tonic::transport::{Identity, Server, ServerTlsConfig}; #[tokio::test] async fn mtls_connects_and_allows_rpc() { // --- Generate self-signed server and client certs --- let server = generate_simple_self_signed(vec!["creditservice.local".into()]).unwrap(); let server_cert_pem = server.cert.pem(); let server_key_pem = server.key_pair.serialize_pem(); let client = generate_simple_self_signed(vec!["creditservice-client".into()]).unwrap(); let client_cert_pem = client.cert.pem(); let client_key_pem = client.key_pair.serialize_pem(); // --- Start CreditService server with mTLS --- let addr: SocketAddr = "127.0.0.1:50057".parse().unwrap(); let storage: Arc = InMemoryStorage::new(); let svc = Arc::new(CreditServiceImpl::new(storage)); let identity = Identity::from_pem(server_cert_pem.clone(), server_key_pem.clone()); let client_ca = tonic::transport::Certificate::from_pem(client_cert_pem.clone()); let (tx, rx) = oneshot::channel::<()>(); let server = Server::builder() .tls_config( ServerTlsConfig::new() .identity(identity) .client_ca_root(client_ca), ) .unwrap() .add_service(CreditServiceServer::new(svc.as_ref().clone())) .serve_with_shutdown(addr, async { let _ = rx.await; }); let server_handle = tokio::spawn(server); // Give the server a moment to start tokio::time::sleep(std::time::Duration::from_millis(200)).await; // --- Client with mTLS --- let mut client = Client::builder(format!("https://127.0.0.1:{}", addr.port())) .tls(TlsConfig { domain: Some("creditservice.local".into()), ca_cert_pem: Some(server_cert_pem.clone()), client_cert_pem: Some(client_cert_pem.clone()), client_key_pem: Some(client_key_pem.clone()), }) .build() .await .expect("client build"); // Simple RPC: create wallet then get wallet let wallet = client .create_wallet("proj-mtls", "org-mtls", 1000) .await .expect("create_wallet"); assert_eq!(wallet.project_id, "proj-mtls"); assert_eq!(wallet.org_id, "org-mtls"); let fetched = client .get_wallet("proj-mtls", "org-mtls") .await .expect("get_wallet"); assert_eq!(fetched.balance, 1000); // Shutdown server let _ = tx.send(()); let _ = server_handle.await; }