vorma 0.86.0-pre.3

Vorma framework.
Documentation
use super::*;

#[test]
fn router_defaults_mount_root_and_best_match() {
	let router: Router = Router::default();
	assert_eq!(router.dynamic_param_prefix(), ':');
	assert_eq!(router.splat_segment_identifier(), '*');
	assert_eq!(router.mount_root(), "/api/");

	let cases = [
		("api", "/api/"),
		("/api", "/api/"),
		("/api/", "/api/"),
		("api/v1", "/api/v1/"),
	];
	for (input, expected) in cases {
		let router: Router = Router::new(crate::mux::Options {
			mount_root: input.to_string(),
			..crate::mux::Options::default()
		})
		.unwrap();
		assert_eq!(router.mount_root(), expected);
	}
	for input in ["", "/"] {
		let error =
			match Router::<(), Box<dyn std::error::Error + Send + Sync>>::new(crate::mux::Options {
				mount_root: input.to_owned(),
				..crate::mux::Options::default()
			}) {
				Ok(_) => panic!("invalid mount root {input:?} should fail"),
				Err(error) => error.to_string(),
			};
		assert!(error.contains("api_base must be a non-root path prefix"));
	}

	let mut router: Router = Router::new(crate::mux::Options {
		mount_root: "/api".to_string(),
		..crate::mux::Options::default()
	})
	.unwrap();
	router
		.add_handler(Method::GET, "/", no_input(), |_| async { Ok(()) })
		.unwrap();
	router
		.add_handler(Method::GET, "/users/new", no_input(), |_| async { Ok(()) })
		.unwrap();
	router
		.add_handler(Method::GET, "/users/:id", no_input(), |_| async { Ok(()) })
		.unwrap();
	router
		.add_handler(Method::POST, "/users", no_input(), |_| async { Ok(()) })
		.unwrap();

	let found = router.find_best(&Method::GET, "/api").unwrap();
	assert_eq!(found.original_pattern(), "/");

	let found = router.find_best(&Method::GET, "/api/").unwrap();
	assert_eq!(found.original_pattern(), "/");

	let found = router.find_best(&Method::GET, "/api/users/new").unwrap();
	assert_eq!(found.original_pattern(), "/users/new");
	assert!(found.params().is_empty());

	let found = router.find_best(&Method::GET, "/api/users/123").unwrap();
	assert_eq!(found.original_pattern(), "/users/:id");
	assert_eq!(found.params().get("id").map(String::as_str), Some("123"));

	let found = router.find_best(&Method::HEAD, "/api/users/123").unwrap();
	assert_eq!(found.method(), &Method::GET);
	assert!(found.is_head_fallback());

	let methods = router
		.allowed_methods_for_path("/api/users/123")
		.into_iter()
		.collect::<Vec<_>>();
	assert_eq!(methods, ["GET", "HEAD"]);

	router
		.add_handler(Method::HEAD, "/users/:id", no_input(), |_| async { Ok(()) })
		.unwrap();
	let found = router.find_best(&Method::HEAD, "/api/users/123").unwrap();
	assert_eq!(found.method(), &Method::HEAD);
	assert!(!found.is_head_fallback());
	assert!(router.find_best(&Method::GET, "/users/123").is_none());

	let methods = router
		.allowed_methods_for_path("/api/users")
		.into_iter()
		.collect::<Vec<_>>();
	assert_eq!(methods, ["POST"]);
}

#[tokio::test]
async fn route_executes_with_typed_input_and_request_context() {
	let mut router: Router<TestEnv, &'static str> = Router::default();
	router
		.add_handler(
			Method::GET,
			"/orgs/:org_id/files/*",
			InputParser::callback(|request| {
				Ok(GreetingInput {
					name: request.query().unwrap_or("").to_string(),
				})
			}),
			|ctx| async move {
				assert_eq!(ctx.matched_pattern(), "/orgs/:org_id/files/*");
				assert_eq!(ctx.param("org_id"), Some("acme"));
				assert_eq!(ctx.splat_values(), ["reports", "q1"]);
				assert_eq!(ctx.request().path(), "/api/orgs/acme/files/reports/q1");
				assert_eq!(ctx.state().shared_runs.load(Ordering::SeqCst), 0);
				Ok(format!("hello {}", ctx.input().name))
			},
		)
		.unwrap();

	let (state, exec_ctx) = default_runtime();
	let result = router
		.execute_route(
			api_request("/orgs/acme/files/reports/q1?Jeff"),
			state,
			exec_ctx,
			empty_public_filemap(),
		)
		.await
		.unwrap()
		.unwrap();

	assert_eq!(
		result.route_match().original_pattern(),
		"/orgs/:org_id/files/*"
	);
	assert_eq!(result.data().and_then(Value::as_str), Some("hello Jeff"));
	assert!(result.error().is_none());
}