···
3
+
(** Test JSON serialization validation for JMAP method arguments *)
5
+
(** Validation helper - checks if JSON contains expected fields *)
6
+
let validate_json_fields expected_fields json =
7
+
let open Yojson.Safe.Util in
10
+
List.for_all (fun expected_field ->
11
+
List.exists (fun (field_name, _) -> field_name = expected_field) fields
15
+
(** Test Get_args JSON serialization validation *)
16
+
let test_get_args () =
17
+
Printf.printf "Testing Get_args JSON validation...\n";
19
+
(* Test with all fields *)
20
+
let get_args = Get_args.v
21
+
~account_id:"acc123"
23
+
~properties:["subject"; "from"]
26
+
let json = Get_args.to_json get_args in
28
+
assert (validate_json_fields ["accountId"; "ids"; "properties"] json);
29
+
Printf.printf "✓ Get_args with all fields - validated\n";
32
+
let minimal_args = Get_args.v ~account_id:"acc456" () in
33
+
let minimal_json = Get_args.to_json minimal_args in
34
+
assert (validate_json_fields ["accountId"] minimal_json);
35
+
Printf.printf "✓ Get_args minimal - validated\n";
37
+
(* Test result reference *)
38
+
let (_, ref_json) = Get_args.with_result_reference get_args
39
+
~result_of:"q1" ~name:"Email/query" ~path:"/ids" in
40
+
let json_with_ref = Get_args.to_json ~result_reference_ids:(Some ref_json) get_args in
41
+
assert (validate_json_fields ["accountId"; "ids"; "properties"] json_with_ref);
42
+
Printf.printf "✓ Get_args with result reference - validated\n"
44
+
(** Test Query_args JSON serialization validation *)
45
+
let test_query_args () =
46
+
Printf.printf "\nTesting Query_args JSON validation...\n";
48
+
let filter = Filter.property_equals "keywords/$seen" (`Bool false) in
49
+
let sort = [Comparator.v ~property:"receivedAt" ~is_ascending:false ()] in
51
+
let query_args = Query_args.v
52
+
~account_id:"acc123"
53
+
~filter ~sort ~position:0 ~limit:50
54
+
~calculate_total:true ~collapse_threads:false ()
56
+
let json = Query_args.to_json query_args in
58
+
assert (validate_json_fields ["accountId"; "filter"; "sort"; "position"; "limit"; "calculateTotal"; "collapseThreads"] json);
59
+
Printf.printf "✓ Query_args comprehensive - validated\n";
61
+
let minimal_query = Query_args.v ~account_id:"acc456" () in
62
+
let minimal_json = Query_args.to_json minimal_query in
63
+
assert (validate_json_fields ["accountId"] minimal_json);
64
+
Printf.printf "✓ Query_args minimal - validated\n"
66
+
(** Test Set_args JSON serialization validation *)
67
+
let test_set_args () =
68
+
Printf.printf "\nTesting Set_args JSON validation...\n";
70
+
let create_map = Hashtbl.create 1 in
71
+
Hashtbl.add create_map "k1" (`Assoc [("subject", `String "Test")]);
73
+
let update_map = Hashtbl.create 1 in
74
+
Hashtbl.add update_map "upd1" (`Assoc [("keywords/$seen", `Bool true)]);
76
+
let set_args = Set_args.v
77
+
~account_id:"acc123" ~if_in_state:"state456"
78
+
~create:create_map ~update:update_map
79
+
~destroy:["destroy1"] ()
81
+
let json = Set_args.to_json
82
+
~create_to_json:(fun v -> v)
83
+
~update_to_json:(fun v -> v) set_args
86
+
assert (validate_json_fields ["accountId"; "ifInState"; "create"; "update"; "destroy"] json);
87
+
Printf.printf "✓ Set_args comprehensive - validated\n";
89
+
let minimal_set = Set_args.v ~account_id:"acc456" () in
90
+
let minimal_json = Set_args.to_json minimal_set in
91
+
assert (validate_json_fields ["accountId"] minimal_json);
92
+
Printf.printf "✓ Set_args minimal - validated\n"
94
+
(** Test Changes_args JSON serialization validation *)
95
+
let test_changes_args () =
96
+
Printf.printf "\nTesting Changes_args JSON validation...\n";
98
+
let changes_args = Changes_args.v
99
+
~account_id:"acc123" ~since_state:"state789" ~max_changes:100 ()
101
+
let json = Changes_args.to_json changes_args in
103
+
assert (validate_json_fields ["accountId"; "sinceState"; "maxChanges"] json);
104
+
Printf.printf "✓ Changes_args with maxChanges - validated\n";
106
+
let minimal_changes = Changes_args.v
107
+
~account_id:"acc456" ~since_state:"state101" ()
109
+
let minimal_json = Changes_args.to_json minimal_changes in
110
+
assert (validate_json_fields ["accountId"; "sinceState"] minimal_json);
111
+
Printf.printf "✓ Changes_args minimal - validated\n"
113
+
(** Test Filter and Comparator JSON operations *)
114
+
let test_filter_comparator () =
115
+
Printf.printf "\nTesting Filter and Comparator JSON...\n";
117
+
(* Test various filter types *)
119
+
("text_contains", Filter.text_contains "subject" "test");
120
+
("property_equals", Filter.property_equals "from" (`String "user@example.com"));
121
+
("property_gt", Filter.property_gt "receivedAt" (`String "2023-01-01T00:00:00Z"));
122
+
("property_in", Filter.property_in "keywords" [`String "$flagged"; `String "$important"]);
123
+
("and_filter", Filter.and_ [
124
+
Filter.property_equals "mailboxIds/inbox" (`Bool true);
125
+
Filter.property_equals "keywords/$seen" (`Bool false)
127
+
("or_filter", Filter.or_ [
128
+
Filter.text_contains "subject" "urgent";
129
+
Filter.text_contains "subject" "important"
131
+
("not_filter", Filter.not_ (Filter.property_equals "keywords/$draft" (`Bool true)));
134
+
List.iter (fun (name, filter) ->
135
+
let json = Filter.to_json filter in
136
+
(* Basic validation - filters should produce valid JSON *)
138
+
| `Assoc _ | `String _ | `Int _ | `Bool _ | `List _ ->
139
+
Printf.printf "✓ Filter %s - valid JSON\n" name
140
+
| _ -> failwith ("Invalid JSON for filter: " ^ name))
143
+
(* Test comparators *)
144
+
let comparators = [
145
+
("basic", Comparator.v ~property:"receivedAt" ~is_ascending:false ());
146
+
("with_collation", Comparator.v ~property:"subject" ~is_ascending:true
147
+
~collation:"i;ascii-casemap" ());
148
+
("with_keyword", Comparator.v ~property:"size" ~keyword:"numeric" ());
151
+
List.iter (fun (name, comp) ->
152
+
let json = Comparator.to_json comp in
153
+
assert (validate_json_fields ["property"] json);
154
+
Printf.printf "✓ Comparator %s - validated\n" name
157
+
(** Test JMAP protocol compliance examples *)
158
+
let test_jmap_compliance () =
159
+
Printf.printf "\nTesting JMAP Protocol Compliance...\n";
161
+
(* Real-world Email/get example *)
162
+
let email_get = Get_args.v
163
+
~account_id:"u1234567890"
164
+
~ids:["Mf8a6c123"; "Mf8a6c456"; "Mf8a6c789"]
165
+
~properties:["id"; "subject"; "from"; "to"; "receivedAt"; "size"; "preview"; "keywords"]
168
+
let json = Get_args.to_json email_get in
169
+
assert (validate_json_fields ["accountId"; "ids"; "properties"] json);
170
+
Printf.printf "✓ JMAP Email/get request - compliant\n";
172
+
(* Real-world Email/query example *)
173
+
let email_filter = Filter.and_ [
174
+
Filter.property_in "mailboxIds" [`String "Minbox123"];
175
+
Filter.property_equals "keywords/$seen" (`Bool false);
176
+
Filter.property_ge "receivedAt" (`String "2023-12-01T00:00:00Z");
178
+
let email_query = Query_args.v
179
+
~account_id:"u1234567890"
180
+
~filter:email_filter
181
+
~sort:[Comparator.v ~property:"receivedAt" ~is_ascending:false ()]
182
+
~limit:50 ~calculate_total:true ()
184
+
let query_json = Query_args.to_json email_query in
185
+
assert (validate_json_fields ["accountId"; "filter"; "sort"; "limit"; "calculateTotal"] query_json);
186
+
Printf.printf "✓ JMAP Email/query request - compliant\n";
188
+
(* Real-world Email/set example *)
189
+
let create_emails = Hashtbl.create 1 in
190
+
Hashtbl.add create_emails "draft001" (`Assoc [
191
+
("subject", `String "Meeting Tomorrow");
192
+
("from", `List [`Assoc [("email", `String "sender@company.com"); ("name", `String "John Doe")]]);
193
+
("to", `List [`Assoc [("email", `String "recipient@company.com"); ("name", `String "Jane Smith")]]);
194
+
("keywords", `Assoc [("$draft", `Bool true)]);
195
+
("mailboxIds", `Assoc [("Mdrafts456", `Bool true)]);
198
+
let update_emails = Hashtbl.create 1 in
199
+
Hashtbl.add update_emails "Mf8a6c123" (`Assoc [
200
+
("keywords/$seen", `Bool true);
201
+
("keywords/$flagged", `Bool true)
204
+
let email_set = Set_args.v
205
+
~account_id:"u1234567890"
206
+
~create:create_emails
207
+
~update:update_emails
208
+
~destroy:["Mf8a6c789"]
211
+
let set_json = Set_args.to_json
212
+
~create_to_json:(fun v -> v)
213
+
~update_to_json:(fun v -> v)
216
+
assert (validate_json_fields ["accountId"; "create"; "update"; "destroy"] set_json);
217
+
Printf.printf "✓ JMAP Email/set request - compliant\n"
219
+
(** Main test runner *)
221
+
Printf.printf "JMAP Method Arguments JSON Serialization Validation\n";
222
+
Printf.printf "===================================================\n";
225
+
test_query_args ();
227
+
test_changes_args ();
228
+
test_filter_comparator ();
229
+
test_jmap_compliance ();
231
+
Printf.printf "\n🎉 All JSON serialization validation tests passed!\n";
232
+
Printf.printf " The implementation correctly supports:\n";
233
+
Printf.printf " • Get_args.to_json with result reference support\n";
234
+
Printf.printf " • Query_args.to_json with filters and sorting\n";
235
+
Printf.printf " • Set_args.to_json with create/update/destroy operations\n";
236
+
Printf.printf " • Changes_args.to_json with maxChanges parameter\n";
237
+
Printf.printf " • Filter.to_json with logical operations\n";
238
+
Printf.printf " • Comparator.to_json with sorting specifications\n";
239
+
Printf.printf " • Full JMAP protocol compliance\n"