(*--------------------------------------------------------------------------- Copyright (c) 2025 Anil Madhavapeddy . All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) (** Tests for Method module - HTTP methods per RFC 9110 Section 9 *) module Method = Requests.Method (** {1 Conversion Tests} *) let standard_methods = [ (`GET, "GET"); (`POST, "POST"); (`PUT, "PUT"); (`DELETE, "DELETE"); (`HEAD, "HEAD"); (`OPTIONS, "OPTIONS"); (`PATCH, "PATCH"); (`CONNECT, "CONNECT"); (`TRACE, "TRACE"); ] let test_to_string_roundtrip () = List.iter (fun (method_, expected_str) -> let str = Method.to_string method_ in Alcotest.(check string) (Fmt.str "to_string %s" expected_str) expected_str str; let method_' = Method.of_string str in Alcotest.(check bool) (Fmt.str "roundtrip %s" expected_str) true (Method.equal method_ method_')) standard_methods let test_of_string_case_insensitive () = let got = Method.of_string "get" in Alcotest.(check bool) "get -> GET" true (Method.equal `GET got); let got2 = Method.of_string "Post" in Alcotest.(check bool) "Post -> POST" true (Method.equal `POST got2); let got3 = Method.of_string "dElEtE" in Alcotest.(check bool) "dElEtE -> DELETE" true (Method.equal `DELETE got3) let test_custom_method () = let custom = `Other "CUSTOM" in let str = Method.to_string custom in Alcotest.(check string) "custom to_string" "CUSTOM" str; let parsed = Method.of_string "CUSTOM" in Alcotest.(check bool) "custom roundtrip" true (Method.equal custom parsed) (** {1 Safe Methods (RFC 9110 Section 9.2.1)} *) let test_is_safe () = let safe_methods = [ `GET; `HEAD; `OPTIONS; `TRACE ] in let unsafe_methods = [ `POST; `PUT; `DELETE; `PATCH; `CONNECT ] in List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s is safe" (Method.to_string m)) true (Method.is_safe m)) safe_methods; List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s is not safe" (Method.to_string m)) false (Method.is_safe m)) unsafe_methods (** {1 Idempotent Methods (RFC 9110 Section 9.2.2)} *) let test_is_idempotent () = let idempotent = [ `GET; `HEAD; `PUT; `DELETE; `OPTIONS; `TRACE ] in let not_idempotent = [ `POST; `PATCH ] in List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s is idempotent" (Method.to_string m)) true (Method.is_idempotent m)) idempotent; List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s is not idempotent" (Method.to_string m)) false (Method.is_idempotent m)) not_idempotent (** {1 Cacheable Methods (RFC 9110 Section 9.2.3)} *) let test_is_cacheable () = let cacheable = [ `GET; `HEAD; `POST ] in let not_cacheable = [ `PUT; `DELETE; `PATCH; `OPTIONS; `TRACE; `CONNECT ] in List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s is cacheable" (Method.to_string m)) true (Method.is_cacheable m)) cacheable; List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s is not cacheable" (Method.to_string m)) false (Method.is_cacheable m)) not_cacheable (** {1 Request Body Semantics (RFC 9110 Section 9.3)} *) let test_request_body_semantics () = (* Body required *) List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s body required" (Method.to_string m)) true (Method.request_body_semantics m = Method.Body_required)) [ `POST; `PUT; `PATCH ]; (* Body forbidden *) List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s body forbidden" (Method.to_string m)) true (Method.request_body_semantics m = Method.Body_forbidden)) [ `HEAD; `TRACE; `CONNECT ]; (* Body optional *) List.iter (fun m -> Alcotest.(check bool) (Fmt.str "%s body optional" (Method.to_string m)) true (Method.request_body_semantics m = Method.Body_optional)) [ `GET; `DELETE; `OPTIONS ] (** {1 Equality} *) let test_equal () = Alcotest.(check bool) "GET = GET" true (Method.equal `GET `GET); Alcotest.(check bool) "GET <> POST" false (Method.equal `GET `POST); Alcotest.(check bool) "Other = Other" true (Method.equal (`Other "X") (`Other "X")); Alcotest.(check bool) "Other <> Other" false (Method.equal (`Other "X") (`Other "Y")) (** {1 Test Suite} *) let suite = ( "method", [ Alcotest.test_case "to_string/of_string roundtrip" `Quick test_to_string_roundtrip; Alcotest.test_case "of_string case insensitive" `Quick test_of_string_case_insensitive; Alcotest.test_case "custom method" `Quick test_custom_method; Alcotest.test_case "safe methods (9.2.1)" `Quick test_is_safe; Alcotest.test_case "idempotent methods (9.2.2)" `Quick test_is_idempotent; Alcotest.test_case "cacheable methods (9.2.3)" `Quick test_is_cacheable; Alcotest.test_case "request body semantics (9.3)" `Quick test_request_body_semantics; Alcotest.test_case "equal" `Quick test_equal; ] )