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 Cache_control module - RFC 9111 compliance *)
7
8module Cache_control = Requests.Cache_control
9
10(** {1 Response Parsing Tests} *)
11
12let test_parse_max_age () =
13 let cc = Cache_control.parse_response "max-age=3600" in
14 Alcotest.(check (option int)) "max_age" (Some 3600) cc.max_age
15
16let test_no_cache_no_store () =
17 let cc = Cache_control.parse_response "no-cache, no-store" in
18 Alcotest.(check bool) "no_cache present" true (Option.is_some cc.no_cache);
19 Alcotest.(check bool) "no_store" true cc.no_store
20
21let test_public_max_age_immutable () =
22 let cc = Cache_control.parse_response "public, max-age=604800, immutable" in
23 Alcotest.(check bool) "public" true cc.public;
24 Alcotest.(check (option int)) "max_age" (Some 604800) cc.max_age;
25 Alcotest.(check bool) "immutable" true cc.immutable
26
27let test_parse_private_must_revalidate () =
28 let cc = Cache_control.parse_response "private, must-revalidate" in
29 Alcotest.(check bool) "private_ present" true (Option.is_some cc.private_);
30 Alcotest.(check bool) "must_revalidate" true cc.must_revalidate
31
32let test_parse_s_maxage () =
33 let cc = Cache_control.parse_response "s-maxage=600" in
34 Alcotest.(check (option int)) "s_maxage" (Some 600) cc.s_maxage
35
36let test_parse_no_transform () =
37 let cc = Cache_control.parse_response "no-transform" in
38 Alcotest.(check bool) "no_transform" true cc.no_transform
39
40let test_parse_proxy_revalidate () =
41 let cc = Cache_control.parse_response "proxy-revalidate" in
42 Alcotest.(check bool) "proxy_revalidate" true cc.proxy_revalidate
43
44let test_parse_stale_while_revalidate () =
45 let cc = Cache_control.parse_response "stale-while-revalidate=60" in
46 Alcotest.(check (option int))
47 "stale_while_revalidate" (Some 60) cc.stale_while_revalidate
48
49let test_parse_stale_if_error () =
50 let cc = Cache_control.parse_response "stale-if-error=300" in
51 Alcotest.(check (option int)) "stale_if_error" (Some 300) cc.stale_if_error
52
53let test_parse_must_understand () =
54 let cc = Cache_control.parse_response "must-understand" in
55 Alcotest.(check bool) "must_understand" true cc.must_understand
56
57(** {1 Request Parsing Tests} *)
58
59let test_request_max_age_zero () =
60 let cc = Cache_control.parse_request "max-age=0" in
61 Alcotest.(check (option int)) "req_max_age" (Some 0) cc.req_max_age
62
63let test_request_no_cache () =
64 let cc = Cache_control.parse_request "no-cache" in
65 Alcotest.(check bool) "req_no_cache" true cc.req_no_cache
66
67let test_request_no_store () =
68 let cc = Cache_control.parse_request "no-store" in
69 Alcotest.(check bool) "req_no_store" true cc.req_no_store
70
71let test_request_no_transform () =
72 let cc = Cache_control.parse_request "no-transform" in
73 Alcotest.(check bool) "req_no_transform" true cc.req_no_transform
74
75let test_request_only_if_cached () =
76 let cc = Cache_control.parse_request "only-if-cached" in
77 Alcotest.(check bool) "req_only_if_cached" true cc.req_only_if_cached
78
79let test_request_min_fresh () =
80 let cc = Cache_control.parse_request "min-fresh=120" in
81 Alcotest.(check (option int)) "req_min_fresh" (Some 120) cc.req_min_fresh
82
83(** {1 Cacheability Tests} *)
84
85let test_cacheable_200_max_age () =
86 let cc = Cache_control.parse_response "max-age=3600" in
87 Alcotest.(check bool)
88 "cacheable 200 with max-age" true
89 (Cache_control.is_cacheable ~response_cc:cc ~status:200)
90
91let test_is_cacheable_no_store () =
92 let cc = Cache_control.parse_response "no-store" in
93 Alcotest.(check bool)
94 "not cacheable with no-store" false
95 (Cache_control.is_cacheable ~response_cc:cc ~status:200)
96
97let test_is_cacheable_default_status () =
98 let cc = Cache_control.empty_response in
99 Alcotest.(check bool)
100 "cacheable 200 default" true
101 (Cache_control.is_cacheable ~response_cc:cc ~status:200)
102
103let test_cacheable_non_default_status () =
104 let cc = Cache_control.empty_response in
105 Alcotest.(check bool)
106 "non-cacheable 500 without directives" false
107 (Cache_control.is_cacheable ~response_cc:cc ~status:500)
108
109let test_is_cacheable_301 () =
110 let cc = Cache_control.empty_response in
111 Alcotest.(check bool)
112 "cacheable 301 by default" true
113 (Cache_control.is_cacheable ~response_cc:cc ~status:301)
114
115let test_is_cacheable_404 () =
116 let cc = Cache_control.empty_response in
117 Alcotest.(check bool)
118 "cacheable 404 by default" true
119 (Cache_control.is_cacheable ~response_cc:cc ~status:404)
120
121(** {1 Predicate Tests} *)
122
123let test_must_revalidate_flag () =
124 let cc = Cache_control.parse_response "must-revalidate" in
125 Alcotest.(check bool)
126 "must_revalidate" true
127 (Cache_control.must_revalidate ~response_cc:cc)
128
129let test_revalidate_with_no_cache () =
130 let cc = Cache_control.parse_response "no-cache" in
131 Alcotest.(check bool)
132 "must_revalidate via no-cache" true
133 (Cache_control.must_revalidate ~response_cc:cc)
134
135let test_revalidate_with_proxy () =
136 let cc = Cache_control.parse_response "proxy-revalidate" in
137 Alcotest.(check bool)
138 "must_revalidate via proxy-revalidate" true
139 (Cache_control.must_revalidate ~response_cc:cc)
140
141let test_must_revalidate_false () =
142 let cc = Cache_control.parse_response "max-age=3600" in
143 Alcotest.(check bool)
144 "must_revalidate false" false
145 (Cache_control.must_revalidate ~response_cc:cc)
146
147let test_is_public () =
148 let cc = Cache_control.parse_response "public" in
149 Alcotest.(check bool)
150 "is_public" true
151 (Cache_control.is_public ~response_cc:cc)
152
153let test_is_public_false () =
154 let cc = Cache_control.parse_response "private" in
155 Alcotest.(check bool)
156 "is_public false" false
157 (Cache_control.is_public ~response_cc:cc)
158
159let test_is_private () =
160 let cc = Cache_control.parse_response "private" in
161 Alcotest.(check bool)
162 "is_private" true
163 (Cache_control.is_private ~response_cc:cc)
164
165let test_is_private_false () =
166 let cc = Cache_control.parse_response "public" in
167 Alcotest.(check bool)
168 "is_private false" false
169 (Cache_control.is_private ~response_cc:cc)
170
171(** {1 Freshness Lifetime Tests} *)
172
173let test_freshness_lifetime_max_age () =
174 let cc = Cache_control.parse_response "max-age=3600" in
175 let fl = Cache_control.freshness_lifetime ~response_cc:cc () in
176 Alcotest.(check (option int)) "freshness_lifetime from max-age" (Some 3600) fl
177
178let test_freshness_lifetime_no_directives () =
179 let cc = Cache_control.empty_response in
180 let fl = Cache_control.freshness_lifetime ~response_cc:cc () in
181 Alcotest.(check (option int)) "freshness_lifetime none" None fl
182
183(** {1 Empty Values Tests} *)
184
185let test_empty_response () =
186 let cc = Cache_control.empty_response in
187 Alcotest.(check (option int)) "max_age" None cc.max_age;
188 Alcotest.(check (option int)) "s_maxage" None cc.s_maxage;
189 Alcotest.(check bool) "no_store" false cc.no_store;
190 Alcotest.(check bool) "must_revalidate" false cc.must_revalidate;
191 Alcotest.(check bool) "public" false cc.public;
192 Alcotest.(check bool) "immutable" false cc.immutable
193
194let test_empty_request () =
195 let cc = Cache_control.empty_request in
196 Alcotest.(check (option int)) "req_max_age" None cc.req_max_age;
197 Alcotest.(check bool) "req_no_cache" false cc.req_no_cache;
198 Alcotest.(check bool) "req_no_store" false cc.req_no_store
199
200(** {1 Test Suite} *)
201
202let suite =
203 ( "cache_control",
204 [
205 Alcotest.test_case "max-age=3600" `Quick test_parse_max_age;
206 Alcotest.test_case "no-cache, no-store" `Quick test_no_cache_no_store;
207 Alcotest.test_case "public, max-age, immutable" `Quick
208 test_public_max_age_immutable;
209 Alcotest.test_case "private, must-revalidate" `Quick
210 test_parse_private_must_revalidate;
211 Alcotest.test_case "s-maxage=600" `Quick test_parse_s_maxage;
212 Alcotest.test_case "no-transform" `Quick test_parse_no_transform;
213 Alcotest.test_case "proxy-revalidate" `Quick test_parse_proxy_revalidate;
214 Alcotest.test_case "stale-while-revalidate" `Quick
215 test_parse_stale_while_revalidate;
216 Alcotest.test_case "stale-if-error" `Quick test_parse_stale_if_error;
217 Alcotest.test_case "must-understand" `Quick test_parse_must_understand;
218 Alcotest.test_case "max-age=0" `Quick test_request_max_age_zero;
219 Alcotest.test_case "no-cache" `Quick test_request_no_cache;
220 Alcotest.test_case "no-store" `Quick test_request_no_store;
221 Alcotest.test_case "no-transform" `Quick test_request_no_transform;
222 Alcotest.test_case "only-if-cached" `Quick test_request_only_if_cached;
223 Alcotest.test_case "min-fresh=120" `Quick test_request_min_fresh;
224 Alcotest.test_case "200 with max-age" `Quick test_cacheable_200_max_age;
225 Alcotest.test_case "no-store not cacheable" `Quick
226 test_is_cacheable_no_store;
227 Alcotest.test_case "200 default status" `Quick
228 test_is_cacheable_default_status;
229 Alcotest.test_case "500 non-default" `Quick
230 test_cacheable_non_default_status;
231 Alcotest.test_case "301 cacheable by default" `Quick test_is_cacheable_301;
232 Alcotest.test_case "404 cacheable by default" `Quick test_is_cacheable_404;
233 Alcotest.test_case "must-revalidate" `Quick test_must_revalidate_flag;
234 Alcotest.test_case "must-revalidate via no-cache" `Quick
235 test_revalidate_with_no_cache;
236 Alcotest.test_case "must-revalidate via proxy-revalidate" `Quick
237 test_revalidate_with_proxy;
238 Alcotest.test_case "must-revalidate false" `Quick
239 test_must_revalidate_false;
240 Alcotest.test_case "is_public" `Quick test_is_public;
241 Alcotest.test_case "is_public false" `Quick test_is_public_false;
242 Alcotest.test_case "is_private" `Quick test_is_private;
243 Alcotest.test_case "is_private false" `Quick test_is_private_false;
244 Alcotest.test_case "from max-age" `Quick test_freshness_lifetime_max_age;
245 Alcotest.test_case "no directives" `Quick
246 test_freshness_lifetime_no_directives;
247 Alcotest.test_case "empty_response" `Quick test_empty_response;
248 Alcotest.test_case "empty_request" `Quick test_empty_request;
249 ] )