use wip_https::http::{Method, Request, StatusCode}; use wip_https::{Endpoint, RequestMetadataError, Route, validate_http_request}; #[test] fn endpoint_is_https_absolute_queryless_and_canonical() { let endpoint = Endpoint::parse("https://example.test/root/prefix").unwrap(); assert_eq!(endpoint.to_string(), "https://example.test/root/prefix/"); let expected = [ (Route::Fetch, "https://example.test/root/prefix/v1/fetch"), ( Route::FetchTree, "https://example.test/root/prefix/v1/fetch_tree", ), ( Route::FetchInterface, "https://example.test/root/prefix/v1/fetch_interface", ), ( Route::CallOperation, "https://example.test/root/prefix/v1/call_operation", ), (Route::Batch, "https://example.test/root/prefix/v1/batch"), ]; for (route, url) in expected { assert_eq!(endpoint.route(route).as_str(), url); assert_eq!( endpoint.recognize(&wip_https::url::Url::parse(url).unwrap()), Some(route) ); } assert_eq!( endpoint.recognize( &wip_https::url::Url::parse("https://example.test/root/prefix/v1/fetch?path=/items") .unwrap() ), None ); } #[test] fn routes_never_mix_worldspace_paths_into_endpoint_prefixes() { let endpoint = Endpoint::parse("https://example.test/wip/%E4%B8%96%E7%95%8C/").unwrap(); let route = endpoint.route(Route::Fetch); assert_eq!(route.path(), "/wip/%E4%B8%96%E7%95%8C/v1/fetch"); assert!(!route.as_str().contains("items")); } #[test] fn inbound_http_metadata_enforces_post_route_and_json() { let endpoint = Endpoint::parse("https://example.test/wip").unwrap(); let valid = Request::builder() .method(Method::POST) .uri("/wip/v1/fetch") .header("content-type", "application/json; charset=utf-8") .body(Vec::::new()) .unwrap(); assert!(validate_http_request(&endpoint, Route::Fetch, &valid).is_ok()); let wrong_method = Request::builder() .method(Method::GET) .uri("/wip/v1/fetch") .header("content-type", "application/json") .body(Vec::::new()) .unwrap(); assert!(validate_http_request(&endpoint, Route::Fetch, &wrong_method).is_err()); let wrong_route = Request::builder() .method(Method::POST) .uri("/wip/v1/fetch_tree") .header("content-type", "application/json") .body(Vec::::new()) .unwrap(); assert!(validate_http_request(&endpoint, Route::Fetch, &wrong_route).is_err()); for accept in [ "text/plain, */*;q=1, application/json;q=0", "application/json; charset=utf-16", "application/json; profile=future", ] { let incompatible_accept = Request::builder() .method(Method::POST) .uri("/wip/v1/fetch") .header("content-type", "application/json") .header("accept", accept) .body(Vec::::new()) .unwrap(); let error = validate_http_request(&endpoint, Route::Fetch, &incompatible_accept).unwrap_err(); assert!(matches!(&error, RequestMetadataError::NotAcceptable)); assert_eq!(error.status(), StatusCode::NOT_ACCEPTABLE); } for accept in [ "application/json; charset=UTF-8", "application/json; q=1; profile=accept-extension", ] { let compatible_accept = Request::builder() .method(Method::POST) .uri("/wip/v1/fetch") .header("content-type", "application/json") .header("accept", accept) .body(Vec::::new()) .unwrap(); assert!(validate_http_request(&endpoint, Route::Fetch, &compatible_accept).is_ok()); } let unsupported_media_type = Request::builder() .method(Method::POST) .uri("/wip/v1/fetch") .header("content-type", "text/plain") .header("accept", "application/json") .body(Vec::::new()) .unwrap(); let error = validate_http_request(&endpoint, Route::Fetch, &unsupported_media_type).unwrap_err(); assert!(matches!(&error, RequestMetadataError::UnsupportedMediaType)); assert_eq!(error.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE); } #[test] fn invalid_endpoint_components_are_rejected() { assert!(Endpoint::parse("http://example.test/wip").is_err()); assert!(Endpoint::parse("/relative").is_err()); assert!(Endpoint::parse("https://example.test/wip?q=1").is_err()); assert!(Endpoint::parse("https://example.test/wip#fragment").is_err()); }