diff --git a/.commitlintrc.yaml b/.commitlintrc.yaml new file mode 100644 index 0000000..2316995 --- /dev/null +++ b/.commitlintrc.yaml @@ -0,0 +1,7 @@ +rules: + description-empty: # Description shouldn't be empty + level: warning + subject-empty: # Subject line should exist + level: error + type-empty: # Type must not be empty + level: error \ No newline at end of file diff --git a/.samoyed/commit-msg b/.samoyed/commit-msg new file mode 100755 index 0000000..f041635 --- /dev/null +++ b/.samoyed/commit-msg @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +commitlint --edit $1 \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index fee3a16..6f77e84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,7 +610,7 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "easyhttpmock" -version = "0.1.3-beta.1" +version = "0.1.3-beta.7" dependencies = [ "bytes", "http", @@ -1120,7 +1120,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -1743,7 +1743,7 @@ dependencies = [ "rustc-hash", "rustls", "smol", - "socket2 0.6.2", + "socket2 0.6.3", "thiserror 2.0.18", "tokio", "tracing", @@ -1783,7 +1783,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.2", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -2312,12 +2312,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2575,7 +2575,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] diff --git a/Cargo.toml b/Cargo.toml index e4183fc..7263e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "easyhttpmock" -version = "0.1.3-beta.1" +version = "0.1.3-beta.7" edition = "2021" authors = ["Rogerio Araújo "] repository = "https://github.com/ararog/easyhttpmock" @@ -12,14 +12,19 @@ keywords = ["http", "mock", "testing"] publish = true rust-version = "1.75.0" +[package.metadata.docs.rs] +all-features = true + [features] default = ["tokio-rt", "http2", "tokio-rust-tls"] tokio-rt = ["vetis/tokio-rt", "vetis/__deboa_tokio"] smol-rt = ["vetis/smol-rt", "vetis/__deboa_smol"] +#compio-rt = ["vetis/compio-rt"] tokio-rust-tls = ["vetis/tokio-rust-tls"] smol-rust-tls = ["vetis/smol-rust-tls"] +#compio-rust-tls = ["vetis/compio-rust-tls"] http1 = ["vetis/http1"] http2 = ["vetis/http2"] diff --git a/README.md b/README.md index 2a1c91b..5e2f938 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # EasyHttpMock +[![Crates.io downloads](https://img.shields.io/crates/d/easyhttpmock)](https://crates.io/crates/easyhttpmock) [![crates.io](https://img.shields.io/crates/v/easyhttpmock?style=flat-square)](https://crates.io/crates/easyhttpmock) [![Build Status](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml/badge.svg?event=push)](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml) ![Crates.io MSRV](https://img.shields.io/crates/msrv/easyhttpmock) [![Documentation](https://docs.rs/easyhttpmock/badge.svg)](https://docs.rs/easyhttpmock/latest/easyhttpmock) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ararog/easyhttpmock/blob/main/LICENSE.md) [![codecov](https://codecov.io/gh/ararog/easyhttpmock/graph/badge.svg?token=T0HSBAPVSI)](https://codecov.io/gh/ararog/easyhttpmock) + + **The effortless HTTP mock server for seamless API testing** **EasyHttpMock** is a powerful yet simple HTTP mock server designed specifically for testing HTTP clients. Built on top of [VeTiS](https://github.com/ararog/vetis), it provides a clean, intuitive API for creating realistic mock endpoints that simulate real-world API behavior, making your testing workflow faster and more reliable. @@ -26,25 +29,25 @@ easyhttpmock = { version = "0.1.1", features = ["tokio-rt", "http2", "tokio-rust Here's how simple it is to create a mock HTTP server for testing: ```rust -use bytes::Bytes; use http::StatusCode; -use http_body_util::Full; -use hyper::Response; use easyhttpmock::{ - config::EasyHttpMockConfig, - server::{adapters::vetis_adapter::VetisServerAdapter, PortGenerator}, EasyHttpMock, -}; - -use deboa::{cert::ContentEncoding, request::DeboaRequest, Client}; - -use vetis::{ + config::EasyHttpMockConfig, server::{ - config::{SecurityConfig, ServerConfig}, + PortGenerator, + adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, }, }; +use deboa::{ + Client, + cert::{Certificate, ContentEncoding}, + request::DeboaRequest, +}; + +use vetis::Response; + pub const CA_CERT: &[u8] = include_bytes!("../certs/ca.der"); pub const CA_CERT_PEM: &[u8] = include_bytes!("../certs/ca.crt"); @@ -53,25 +56,25 @@ pub const SERVER_KEY: &[u8] = include_bytes!("../certs/server.key.der"); #[tokio::main] async fn main() -> Result<(), Box> { - let tls_config = SecurityConfig::builder() - .cert(SERVER_CERT.to_vec()) - .key(SERVER_KEY.to_vec()) - .build(); - - let vetis_config = ServerConfig::builder() - .security(tls_config) + let vetis_adapter_config = VetisAdapterConfig::builder() + .interface("0.0.0.0") .with_random_port() + .cert(Some(SERVER_CERT.to_vec())) + .key(Some(SERVER_KEY.to_vec())) + .ca(Some(CA_CERT.to_vec())) .build(); - let config = EasyHttpMockConfig::::builder() - .server_config(vetis_config) + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) .build(); - let mut server = EasyHttpMock::new(config); + let mut server = EasyHttpMock::new(config)?; #[allow(unused_must_use)] let result = server .start(|_| async move { - Ok(Response::new(Full::new(Bytes::from("Hello World")))) + Ok(Response::builder() + .status(StatusCode::OK) + .text("Hello World")) }) .await; @@ -80,10 +83,11 @@ async fn main() -> Result<(), Box> { }); let client = Client::builder() - .certificate(deboa::cert::Certificate::from_slice(CA_CERT, ContentEncoding::DER)) + .certificate(Certificate::from_slice(CA_CERT, ContentEncoding::DER)) .build(); - let request = DeboaRequest::get(server.url("/anything"))?.build()?; + let url = server.url("/anything"); + let request = DeboaRequest::get(url)?.build()?; let response = client .execute(request) @@ -96,7 +100,7 @@ async fn main() -> Result<(), Box> { server .stop() .await?; - + Ok(()) } ``` diff --git a/docs/index.md b/docs/index.md index c25cc73..c94c998 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,10 +9,7 @@ permalink: /

EasyHttpMock

-[![crates.io](https://img.shields.io/crates/v/easyhttpmock?style=flat-square)](https://crates.io/crates/easyhttpmock) -[![Build Status](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml/badge.svg?event=push)](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml) -[![Documentation](https://docs.rs/easyhttpmock/badge.svg)](https://docs.rs/easyhttpmock/latest/easyhttpmock) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Crates.io downloads](https://img.shields.io/crates/d/easyhttpmock)](https://crates.io/crates/easyhttpmock) [![crates.io](https://img.shields.io/crates/v/easyhttpmock?style=flat-square)](https://crates.io/crates/easyhttpmock) [![Build Status](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml/badge.svg?event=push)](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml) ![Crates.io MSRV](https://img.shields.io/crates/msrv/easyhttpmock) [![Documentation](https://docs.rs/easyhttpmock/badge.svg)](https://docs.rs/easyhttpmock/latest/easyhttpmock) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ararog/easyhttpmock/blob/main/LICENSE.md) [![codecov](https://codecov.io/gh/ararog/easyhttpmock/graph/badge.svg?token=T0HSBAPVSI)](https://codecov.io/gh/ararog/easyhttpmock) **EasyHttpMock** is a powerful yet simple HTTP mock server designed specifically for testing HTTP clients. Built on top of [VeTiS](https://github.com/ararog/vetis), it provides a clean, intuitive API for creating realistic mock endpoints that simulate real-world API behavior, making your testing workflow faster and more reliable. @@ -37,25 +34,25 @@ easyhttpmock = { version = "0.0.9" } Basic usage: ```rust -use bytes::Bytes; use http::StatusCode; -use http_body_util::Full; -use hyper::Response; use easyhttpmock::{ - config::EasyHttpMockConfig, - server::{adapters::vetis_adapter::VetisServerAdapter, PortGenerator}, EasyHttpMock, -}; - -use deboa::{cert::ContentEncoding, request::DeboaRequest, Client}; - -use vetis::{ + config::EasyHttpMockConfig, server::{ - config::{SecurityConfig, ServerConfig}, + PortGenerator, + adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, }, }; +use deboa::{ + Client, + cert::{Certificate, ContentEncoding}, + request::DeboaRequest, +}; + +use vetis::Response; + pub const CA_CERT: &[u8] = include_bytes!("../certs/ca.der"); pub const CA_CERT_PEM: &[u8] = include_bytes!("../certs/ca.crt"); @@ -64,22 +61,19 @@ pub const SERVER_KEY: &[u8] = include_bytes!("../certs/server.key.der"); #[tokio::main] async fn main() -> Result<(), Box> { - let tls_config = SecurityConfig::builder() - .cert(SERVER_CERT.to_vec()) - .key(SERVER_KEY.to_vec()) - .build(); - - let vetis_config = ServerConfig::builder() - .security(tls_config) + let vetis_adapter_config = VetisAdapterConfig::builder() + .interface("0.0.0.0") .with_random_port() + .cert(Some(SERVER_CERT.to_vec())) + .key(Some(SERVER_KEY.to_vec())) + .ca(Some(CA_CERT.to_vec())) .build(); - let config = EasyHttpMockConfig::::builder() - .server_config(vetis_config) + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) .build(); - let mut server = EasyHttpMock::new(config); - #[allow(unused_must_use)] + let mut server = EasyHttpMock::new(config)?; let result = server .start(|_| async move { Ok(Response::new(Full::new(Bytes::from("Hello World")))) @@ -109,6 +103,7 @@ async fn main() -> Result<(), Box> { .await?; Ok(()) +} ``` ## Examples diff --git a/docs/llms.txt b/docs/llms.txt index 66d74e4..81181de 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -9,28 +9,28 @@ easyhttpmock = { version = "0.0.1", features = ["http1", "tokio-rt", "tokio-rust-tls"] } ``` -## 💡 Usage Example +## Usage Example ```rust -use bytes::Bytes; use http::StatusCode; -use http_body_util::Full; -use hyper::Response; use easyhttpmock::{ - config::EasyHttpMockConfig, - server::{adapters::vetis_adapter::VetisServerAdapter, PortGenerator}, EasyHttpMock, -}; - -use deboa::{cert::ContentEncoding, request::DeboaRequest, Client}; - -use vetis::{ + config::EasyHttpMockConfig, server::{ - config::{SecurityConfig, ServerConfig}, + PortGenerator, + adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, }, }; +use deboa::{ + Client, + cert::{Certificate, ContentEncoding}, + request::DeboaRequest, +}; + +use vetis::Response; + pub const CA_CERT: &[u8] = include_bytes!("../certs/ca.der"); pub const CA_CERT_PEM: &[u8] = include_bytes!("../certs/ca.crt"); @@ -39,22 +39,19 @@ pub const SERVER_KEY: &[u8] = include_bytes!("../certs/server.key.der"); #[tokio::main] async fn main() -> Result<(), Box> { - let tls_config = SecurityConfig::builder() - .cert(SERVER_CERT.to_vec()) - .key(SERVER_KEY.to_vec()) - .build(); - - let vetis_config = ServerConfig::builder() - .security(tls_config) + let vetis_adapter_config = VetisAdapterConfig::builder() + .interface("0.0.0.0") .with_random_port() + .cert(Some(SERVER_CERT.to_vec())) + .key(Some(SERVER_KEY.to_vec())) + .ca(Some(CA_CERT.to_vec())) .build(); - let config = EasyHttpMockConfig::::builder() - .server_config(vetis_config) + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) .build(); - let mut server = EasyHttpMock::new(config); - #[allow(unused_must_use)] + let mut server = EasyHttpMock::new(config)?; let result = server .start(|_| async move { Ok(Response::new(Full::new(Bytes::from("Hello World")))) @@ -84,16 +81,17 @@ async fn main() -> Result<(), Box> { .await?; Ok(()) +} ``` ## Features -- **🎯 Testing-Focused**: Purpose-built for HTTP client testing scenarios -- **⚡ Lightning Fast**: Powered by VeTiS for optimal performance -- **🔧 Flexible Runtime**: Choose between Tokio or Smol async runtimes -- **🌐 Full Protocol Support**: HTTP/1, HTTP/2, and HTTP/3 compatibility -- **🛡️ Secure Testing**: Built-in TLS support for HTTPS endpoint testing -- **📦 Minimal Dependencies**: Lightweight footprint for your test suite +- ** Testing-Focused**: Purpose-built for HTTP client testing scenarios +- ** Lightning Fast**: Powered by VeTiS for optimal performance +- ** Flexible Runtime**: Choose between Tokio or Smol async runtimes +- ** Full Protocol Support**: HTTP/1, HTTP/2, and HTTP/3 compatibility +- ** Secure Testing**: Built-in TLS support for HTTPS endpoint testing +- ** Minimal Dependencies**: Lightweight footprint for your test suite ## Blog Posts diff --git a/samoyed.toml b/samoyed.toml deleted file mode 100644 index e889539..0000000 --- a/samoyed.toml +++ /dev/null @@ -1,3 +0,0 @@ -[hooks] -pre-push = "cargo test --release" -pre-commit = "cargo fmt --check && cargo clippy -- -D warnings" diff --git a/src/errors.rs b/src/errors.rs index 662d4f6..e6380b5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -8,6 +8,8 @@ pub enum EasyHttpMockError { #[derive(Debug, Clone, Error, PartialEq)] pub enum ServerError { + #[error("Server config error: {0}")] + Config(String), #[error("Server start error: {0}")] Start(String), #[error("Server stop error: {0}")] diff --git a/src/lib.rs b/src/lib.rs index 331e078..0ca4bb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,10 +2,11 @@ use std::future::Future; use crate::{config::EasyHttpMockConfig, errors::EasyHttpMockError, server::ServerAdapter}; -use bytes::Bytes; use http::StatusCode; -use http_body_util::{Either, Full}; -use vetis::{errors::VetisError, Request, Response}; +use vetis::{ + errors::VetisError, + server::http::{Request, Response}, +}; pub mod config; pub mod errors; diff --git a/src/server/adapters/vetis_adapter.rs b/src/server/adapters/vetis_adapter.rs index d317ad9..b186498 100644 --- a/src/server/adapters/vetis_adapter.rs +++ b/src/server/adapters/vetis_adapter.rs @@ -1,13 +1,16 @@ use std::future::Future; use vetis::{ - config::{ListenerConfig, SecurityConfig, ServerConfig, VirtualHostConfig}, + config::server::{ + virtual_host::{SecurityConfig, VirtualHostConfig}, + ListenerConfig, Protocol, ServerConfig, + }, errors::VetisError, server::{ - path::HandlerPath, - virtual_host::{handler_fn, VirtualHost}, + http::{Request, Response}, + virtual_host::{handler_fn, path::HandlerPath, VirtualHost}, }, - Request, Response, Vetis, + Vetis, }; use crate::{ @@ -17,8 +20,11 @@ use crate::{ EasyHttpMock, }; +/// Builder for VetisAdapterConfig pub struct VetisAdapterConfigBuilder { + hostname: Option, interface: String, + protocol: Protocol, port: u16, cert: Option>, key: Option>, @@ -26,34 +32,99 @@ pub struct VetisAdapterConfigBuilder { } impl VetisAdapterConfigBuilder { + /// Sets the hostname for the server. + /// + /// # Arguments + /// * `hostname` - The hostname to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the hostname set. + pub fn hostname(mut self, hostname: Option) -> Self { + self.hostname = hostname; + self + } + + /// Sets the interface for the server. + /// + /// # Arguments + /// * `interface` - The interface to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the interface set. pub fn interface(mut self, interface: &str) -> Self { self.interface = interface.to_string(); self } + /// Sets the protocol for the server. + /// + /// # Arguments + /// * `protocol` - The protocol to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the protocol set. + pub fn protocol(mut self, protocol: Protocol) -> Self { + self.protocol = protocol; + self + } + + /// Sets the port for the server. + /// + /// # Arguments + /// * `port` - The port to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the port set. pub fn port(mut self, port: u16) -> Self { self.port = port; self } + /// Sets the certificate for the server. + /// + /// # Arguments + /// * `cert` - The certificate to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the certificate set. pub fn cert(mut self, cert: Option>) -> Self { self.cert = cert; self } + /// Sets the key for the server. + /// + /// # Arguments + /// * `key` - The key to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the key set. pub fn key(mut self, key: Option>) -> Self { self.key = key; self } + /// Sets the CA certificate for the server. + /// + /// # Arguments + /// * `ca` - The CA certificate to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the CA certificate set. pub fn ca(mut self, ca: Option>) -> Self { self.ca = ca; self } + /// Builds the VetisAdapterConfig from the builder. + /// + /// # Returns + /// A new `VetisAdapterConfig` instance. pub fn build(self) -> VetisAdapterConfig { VetisAdapterConfig { + hostname: self.hostname, interface: self.interface, + protocol: self.protocol, port: self.port, cert: self.cert, key: self.key, @@ -62,9 +133,12 @@ impl VetisAdapterConfigBuilder { } } +/// Configuration for the Vetis adapter. #[derive(Clone)] pub struct VetisAdapterConfig { + hostname: Option, interface: String, + protocol: Protocol, port: u16, cert: Option>, key: Option>, @@ -72,15 +146,43 @@ pub struct VetisAdapterConfig { } impl Default for VetisAdapterConfig { + /// Creates a default configuration for the Vetis adapter. + /// + /// This function sets up a basic server configuration with: + /// - Interface: "0.0.0.0" + /// - Port: 80 + /// - No TLS certificates (HTTP only) + /// + /// # Returns + /// A default `VetisAdapterConfig` instance. fn default() -> Self { - Self { interface: "0.0.0.0".into(), port: 80, cert: None, key: None, ca: None } + Self { + hostname: None, + interface: "0.0.0.0".into(), + protocol: Protocol::Http1, + port: 80, + cert: None, + key: None, + ca: None, + } } } impl VetisAdapterConfig { + /// Creates a new builder for the Vetis adapter configuration. + /// + /// This function sets up a basic server configuration with: + /// - Interface: "0.0.0.0" + /// - Port: 80 + /// - No TLS certificates (HTTP only) + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance. pub fn builder() -> VetisAdapterConfigBuilder { VetisAdapterConfigBuilder { + hostname: None, interface: "0.0.0.0".into(), + protocol: Protocol::Http1, port: 80, cert: None, key: None, @@ -88,22 +190,50 @@ impl VetisAdapterConfig { } } + /// Returns the hostname of the server. + /// + /// # Returns + /// The hostname of the server. + pub fn hostname(&self) -> &Option { + &self.hostname + } + + /// Returns the interface of the server. + /// + /// # Returns + /// The interface of the server. pub fn interface(&self) -> &str { &self.interface } + /// Returns the port of the server. + /// + /// # Returns + /// The port of the server. pub fn port(&self) -> u16 { self.port } + /// Returns the certificate of the server. + /// + /// # Returns + /// The certificate of the server. pub fn cert(&self) -> &Option> { &self.cert } + /// Returns the key of the server. + /// + /// # Returns + /// The key of the server. pub fn key(&self) -> &Option> { &self.key } + /// Returns the CA certificate of the server. + /// + /// # Returns + /// The CA certificate of the server. pub fn ca(&self) -> &Option> { &self.ca } @@ -113,11 +243,14 @@ impl From for ServerConfig { fn from(config: VetisAdapterConfig) -> Self { let listener_config = ListenerConfig::builder() .interface(&config.interface) + .protocol(config.protocol) .port(config.port) - .build(); + .build() + .expect("Failed to build listener config"); ServerConfig::builder() .add_listener(listener_config) .build() + .expect("Failed to build server config") } } @@ -134,6 +267,15 @@ impl PortGenerator for VetisAdapterConfigBuilder { } impl Default for EasyHttpMockConfig { + /// Creates a default configuration for the Vetis adapter. + /// + /// This function sets up a basic server configuration with: + /// - Interface: "0.0.0.0" + /// - Port: 80 + /// - No TLS certificates (HTTP only) + /// + /// # Returns + /// A default `EasyHttpMockConfig` configured for the Vetis adapter. fn default() -> Self { let server_config = VetisAdapterConfig::builder() .interface("0.0.0.0") @@ -158,6 +300,13 @@ impl Default for EasyHttpMock { impl ServerAdapter for VetisAdapter { type Config = VetisAdapterConfig; + /// Creates a new VetisAdapter instance. + /// + /// # Arguments + /// * `config` - The configuration for the adapter. + /// + /// # Returns + /// A new `VetisAdapter` instance. fn new(config: Self::Config) -> Result { let vetis_config = config .clone() @@ -168,22 +317,56 @@ impl ServerAdapter for VetisAdapter { Ok(Self { server, config }) } + /// Returns the hostname of the server. + /// + /// # Returns + /// The hostname of the server. + fn hostname(&self) -> String { + self.config + .hostname() + .clone() + .unwrap_or_else(|| "localhost".to_string()) + } + + /// Returns the base URL of the server. + /// + /// # Returns + /// The base URL of the server. fn base_url(&self) -> String { + let hostname = self.hostname(); + if self .config .cert .is_some() { - format!("https://localhost:{}", self.config.port()) + format!("https://{}:{}", hostname, self.config.port()) } else { - format!("http://localhost:{}", self.config.port()) + format!("http://{}:{}", hostname, self.config.port()) } } + /// Returns the configuration of the server. + /// + /// # Returns + /// The configuration of the server. fn config(&self) -> &Self::Config { &self.config } + /// Starts the server with the given handler. + /// + /// # Arguments + /// + /// * `handler` - The handler to use for the server. + /// + /// # Returns + /// + /// A result indicating whether the server started successfully or a `EasyHttpMockError` if it failed. + /// + /// TODO: Make handler a EasyHttpMockHandler and convert from EasyMockRequest + /// and Response to Vetis Request and Response. Maybe have From implementation + /// for EasyMockRequest and Response. async fn start(&mut self, handler: H) -> Result<(), EasyHttpMockError> where H: Fn(Request) -> Fut + Send + Sync + 'static, @@ -195,8 +378,11 @@ impl ServerAdapter for VetisAdapter { .build() .unwrap(); + let hostname = self.hostname(); + let host_config = VirtualHostConfig::builder() - .hostname("localhost") + .hostname(&hostname) + .root_directory("src/tests") .port(self.config.port()); let host_config = if let Some(((cert, key), ca)) = self @@ -218,7 +404,8 @@ impl ServerAdapter for VetisAdapter { .cert_from_bytes(cert.clone()) .key_from_bytes(key.clone()) .ca_cert_from_bytes(ca.clone()) - .build(), + .build() + .map_err(|e| EasyHttpMockError::Server(ServerError::Config(e.to_string())))?, ) } else { host_config @@ -241,6 +428,10 @@ impl ServerAdapter for VetisAdapter { .map_err(|e| EasyHttpMockError::Server(ServerError::Start(e.to_string()))) } + /// Stops the server. + /// + /// # Returns + /// A result indicating whether the server stopped successfully. async fn stop(&mut self) -> Result<(), EasyHttpMockError> { self.server .stop() diff --git a/src/server/mod.rs b/src/server/mod.rs index 3250ba9..236302d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,27 +1,75 @@ use std::future::Future; -use vetis::{errors::VetisError, Request, Response}; +use vetis::{ + errors::VetisError, + server::http::{Request, Response}, +}; use crate::errors::EasyHttpMockError; pub mod adapters; +/// Server adapter trait to allow different http server implementations pub trait ServerAdapter { type Config: Clone; + /// Create a new server adapter + /// + /// # Arguments + /// + /// * `config` - The configuration for the server adapter + /// + /// # Returns + /// + /// * `Result` - The server adapter or an error fn new(config: Self::Config) -> Result where Self: Sized; + /// Get the hostname of the server + /// + /// # Returns + /// + /// * `String` - The hostname of the server + fn hostname(&self) -> String; + + /// Get the base URL of the server + /// + /// # Returns + /// + /// * `String` - The base URL of the server + /// fn base_url(&self) -> String; + /// Get the configuration of the server + /// + /// # Returns + /// + /// * `&Self::Config` - The configuration of the server + /// fn config(&self) -> &Self::Config; + /// Start the server + /// + /// # Arguments + /// + /// * `handler` - The handler function to handle incoming requests + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - The result of the operation + /// fn start(&mut self, handler: H) -> impl Future> where H: Fn(Request) -> Fut + Send + Sync + 'static, Fut: Future> + Send + Sync + 'static; + /// Stop the server + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - The result of the operation + /// fn stop(&mut self) -> impl Future>; } diff --git a/src/tests/config.rs b/src/tests/config.rs index 76021c7..313cc6b 100644 --- a/src/tests/config.rs +++ b/src/tests/config.rs @@ -3,6 +3,7 @@ mod easy_http_mock_config_tests { use crate::{ config::EasyHttpMockConfig, server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, + tests::default_protocol, }; #[test] @@ -97,6 +98,7 @@ mod easy_http_mock_config_tests { fn test_easy_http_mock_config_builder_with_server_config() { let server_config = VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(3000) .build(); @@ -126,6 +128,7 @@ mod easy_http_mock_config_tests { let base_url = "https://test.mock".to_string(); let server_config = VetisAdapterConfig::builder() .interface("0.0.0.0") + .protocol(default_protocol()) .port(8443) .build(); @@ -153,6 +156,7 @@ mod easy_http_mock_config_tests { fn test_easy_http_mock_config_builder_chaining() { let server_config = VetisAdapterConfig::builder() .interface("192.168.1.100") + .protocol(default_protocol()) .port(9090) .build(); @@ -207,12 +211,14 @@ mod integration_tests { use crate::{ config::EasyHttpMockConfig, server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, + tests::default_protocol, }; #[test] fn test_config_with_vetis_server_adapter_integration() { let server_config = VetisAdapterConfig::builder() .interface("0.0.0.0") + .protocol(default_protocol()) .port(443) .build(); @@ -236,6 +242,7 @@ mod integration_tests { .server_config( VetisAdapterConfig::builder() .interface("0.0.0.0") + .protocol(default_protocol()) .port(8080) .build(), ) @@ -246,6 +253,7 @@ mod integration_tests { .server_config( VetisAdapterConfig::builder() .interface("0.0.0.0") + .protocol(default_protocol()) .port(9090) .build(), ) @@ -268,6 +276,7 @@ mod integration_tests { .base_url(Some("https://immutable.mock".to_string())) .server_config( VetisAdapterConfig::builder() + .protocol(default_protocol()) .port(3000) .build(), ) @@ -285,6 +294,7 @@ mod integration_tests { .base_url(Some("https://new.mock".to_string())) .server_config( VetisAdapterConfig::builder() + .protocol(default_protocol()) .port(3000) .build(), ) @@ -303,11 +313,13 @@ mod integration_tests { fn test_config_with_different_server_configs() { let http_config = VetisAdapterConfig::builder() .interface("0.0.0.0") + .protocol(default_protocol()) .port(80) .build(); let https_config = VetisAdapterConfig::builder() .interface("0.0.0.0") + .protocol(default_protocol()) .port(443) .build(); diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 21be1b5..acedeae 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,2 +1,19 @@ +use vetis::config::server::Protocol; + mod config; mod server; + +pub(crate) const fn default_protocol() -> Protocol { + #[cfg(feature = "http1")] + { + Protocol::Http1 + } + #[cfg(feature = "http2")] + { + Protocol::Http2 + } + #[cfg(feature = "http3")] + { + Protocol::Http3 + } +} diff --git a/src/tests/server.rs b/src/tests/server.rs index 0f4d5bc..b315f6b 100644 --- a/src/tests/server.rs +++ b/src/tests/server.rs @@ -3,13 +3,12 @@ mod easy_http_mock_server_tests { use crate::{ config::EasyHttpMockConfig, server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, + tests::default_protocol, EasyHttpMock, }; - use bytes::Bytes; use http::StatusCode; - use http_body_util::{Either, Full}; use std::time::Duration; - use vetis::{Request, Response}; + use vetis::server::http::{Request, Response}; #[tokio::test] async fn test_easy_http_mock_default() { @@ -27,6 +26,7 @@ mod easy_http_mock_server_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(3000) .build(), ) @@ -47,6 +47,7 @@ mod easy_http_mock_server_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(4000) .build(), ) @@ -90,6 +91,7 @@ mod easy_http_mock_server_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(8181) .build(), ) @@ -110,6 +112,7 @@ mod easy_http_mock_server_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(9999) .build(), ) @@ -155,6 +158,7 @@ mod easy_http_mock_server_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(7777) // Use random available port .build(), ) @@ -209,11 +213,12 @@ mod integration_tests { use crate::{ config::EasyHttpMockConfig, server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, + tests::default_protocol, EasyHttpMock, }; use http::StatusCode; use std::time::Duration; - use vetis::{Request, Response}; + use vetis::server::http::{Request, Response}; #[tokio::test] async fn test_multiple_server_instances() -> Result<(), Box> { @@ -221,6 +226,7 @@ mod integration_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(8081) .build(), ) @@ -230,6 +236,7 @@ mod integration_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(8082) .build(), ) @@ -288,6 +295,7 @@ mod integration_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(8888) // Use random available port .build(), ) @@ -328,6 +336,7 @@ mod integration_tests { .server_config( VetisAdapterConfig::builder() .interface("127.0.0.1") + .protocol(default_protocol()) .port(5555) // Use random available port .build(), )