forked from
anil.recoil.org/ocaml-requests
A batteries included HTTP/1.1 client in OCaml
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** Tests for Auth module *)
7
8module Auth = Requests.Auth
9module Headers = Requests.Headers
10
11(** {1 requires_https Tests} *)
12
13let test_requires_https_none () =
14 Alcotest.(check bool)
15 "none does not require https" false
16 (Auth.requires_https Auth.none)
17
18let test_requires_https_basic () =
19 let auth = Auth.basic ~username:"user" ~password:"pass" in
20 Alcotest.(check bool) "basic requires https" true (Auth.requires_https auth)
21
22let test_requires_https_bearer () =
23 let auth = Auth.bearer ~token:"tok" in
24 Alcotest.(check bool) "bearer requires https" true (Auth.requires_https auth)
25
26let test_requires_https_digest () =
27 let auth = Auth.digest ~username:"user" ~password:"pass" in
28 Alcotest.(check bool) "digest requires https" true (Auth.requires_https auth)
29
30(** {1 is_digest Tests} *)
31
32let test_is_digest_true () =
33 let auth = Auth.digest ~username:"user" ~password:"pass" in
34 Alcotest.(check bool) "digest is_digest" true (Auth.is_digest auth)
35
36let test_is_digest_false_none () =
37 Alcotest.(check bool) "none is not digest" false (Auth.is_digest Auth.none)
38
39let test_is_digest_false_basic () =
40 let auth = Auth.basic ~username:"user" ~password:"pass" in
41 Alcotest.(check bool) "basic is not digest" false (Auth.is_digest auth)
42
43(** {1 apply Tests} *)
44
45let test_apply_basic () =
46 let auth = Auth.basic ~username:"user" ~password:"pass" in
47 let headers = Auth.apply auth Headers.empty in
48 let v = Headers.string "authorization" headers in
49 match v with
50 | Some s ->
51 Alcotest.(check bool)
52 "basic auth starts with Basic" true
53 (String.length s > 6 && String.sub s 0 6 = "Basic ")
54 | None -> Alcotest.fail "Expected Authorization header"
55
56let test_apply_bearer () =
57 let auth = Auth.bearer ~token:"mytoken" in
58 let headers = Auth.apply auth Headers.empty in
59 let v = Headers.string "authorization" headers in
60 Alcotest.(check (option string))
61 "bearer auth header" (Some "Bearer mytoken") v
62
63let test_apply_none () =
64 let headers = Auth.apply Auth.none Headers.empty in
65 let v = Headers.string "authorization" headers in
66 Alcotest.(check (option string)) "no auth header" None v
67
68(** {1 digest_credentials Tests} *)
69
70let test_get_digest_credentials_some () =
71 let auth = Auth.digest ~username:"alice" ~password:"secret" in
72 let creds = Auth.digest_credentials auth in
73 Alcotest.(check (option (pair string string)))
74 "digest credentials"
75 (Some ("alice", "secret"))
76 creds
77
78let test_get_digest_credentials_none () =
79 let auth = Auth.basic ~username:"alice" ~password:"secret" in
80 let creds = Auth.digest_credentials auth in
81 Alcotest.(check (option (pair string string)))
82 "non-digest has no credentials" None creds
83
84let test_digest_credentials_none () =
85 let creds = Auth.digest_credentials Auth.none in
86 Alcotest.(check (option (pair string string)))
87 "none has no credentials" None creds
88
89(** {1 parse_www_authenticate Tests} *)
90
91let test_parse_www_authenticate_digest () =
92 let header =
93 "Digest realm=\"example.com\", nonce=\"abc123\", qop=\"auth\", \
94 algorithm=SHA-256"
95 in
96 match Auth.parse_www_authenticate header with
97 | Some challenge ->
98 Alcotest.(check string) "realm" "example.com" challenge.realm;
99 Alcotest.(check string) "nonce" "abc123" challenge.nonce;
100 Alcotest.(check (option string)) "qop" (Some "auth") challenge.qop;
101 Alcotest.(check string) "algorithm" "SHA-256" challenge.algorithm
102 | None -> Alcotest.fail "Expected parsed digest challenge"
103
104let parse_www_auth_not_digest () =
105 let header = "Basic realm=\"example.com\"" in
106 let result = Auth.parse_www_authenticate header in
107 Alcotest.(check bool) "not a digest challenge" true (Option.is_none result)
108
109(** {1 Test Suite} *)
110
111let suite =
112 ( "auth",
113 [
114 Alcotest.test_case "requires_https none" `Quick test_requires_https_none;
115 Alcotest.test_case "requires_https basic" `Quick test_requires_https_basic;
116 Alcotest.test_case "requires_https bearer" `Quick
117 test_requires_https_bearer;
118 Alcotest.test_case "requires_https digest" `Quick
119 test_requires_https_digest;
120 Alcotest.test_case "is_digest true" `Quick test_is_digest_true;
121 Alcotest.test_case "is_digest false none" `Quick test_is_digest_false_none;
122 Alcotest.test_case "is_digest false basic" `Quick
123 test_is_digest_false_basic;
124 Alcotest.test_case "apply basic" `Quick test_apply_basic;
125 Alcotest.test_case "apply bearer" `Quick test_apply_bearer;
126 Alcotest.test_case "apply none" `Quick test_apply_none;
127 Alcotest.test_case "digest credentials some" `Quick
128 test_get_digest_credentials_some;
129 Alcotest.test_case "digest credentials none" `Quick
130 test_get_digest_credentials_none;
131 Alcotest.test_case "digest credentials none auth" `Quick
132 test_digest_credentials_none;
133 Alcotest.test_case "parse www-authenticate" `Quick
134 test_parse_www_authenticate_digest;
135 Alcotest.test_case "parse non-digest" `Quick parse_www_auth_not_digest;
136 ] )