This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 41 kB View raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** JMAP Protocol codec tests using sample JSON files *) 7 8let read_file path = 9 let ic = open_in path in 10 let n = in_channel_length ic in 11 let s = really_input_string ic n in 12 close_in ic; 13 s 14 15let decode jsont json_str = 16 Jsont_bytesrw.decode_string' jsont json_str 17 18let encode jsont value = 19 Jsont_bytesrw.encode_string' jsont value 20 21(* Test helpers *) 22 23let test_decode_success name jsont path () = 24 let json = read_file path in 25 match decode jsont json with 26 | Ok _ -> () 27 | Error e -> 28 Alcotest.failf "%s: expected success but got error: %s" name (Jsont.Error.to_string e) 29 30let test_decode_failure name jsont path () = 31 let json = read_file path in 32 match decode jsont json with 33 | Ok _ -> Alcotest.failf "%s: expected failure but got success" name 34 | Error _ -> () 35 36let test_roundtrip name jsont path () = 37 let json = read_file path in 38 match decode jsont json with 39 | Error e -> 40 Alcotest.failf "%s: decode failed: %s" name (Jsont.Error.to_string e) 41 | Ok value -> 42 match encode jsont value with 43 | Error e -> 44 Alcotest.failf "%s: encode failed: %s" name (Jsont.Error.to_string e) 45 | Ok encoded -> 46 match decode jsont encoded with 47 | Error e -> 48 Alcotest.failf "%s: re-decode failed: %s" name (Jsont.Error.to_string e) 49 | Ok _ -> () 50 51(* Helpers for extracting values from optional fields in tests *) 52let get_id opt = match opt with Some id -> Jmap.Proto.Id.to_string id | None -> Alcotest.fail "expected id" 53let get_string opt = match opt with Some s -> s | None -> Alcotest.fail "expected string" 54let get_int64 opt = match opt with Some n -> n | None -> Alcotest.fail "expected int64" 55let get_bool opt = match opt with Some b -> b | None -> Alcotest.fail "expected bool" 56 57(* ID tests *) 58module Id_tests = struct 59 open Jmap.Proto 60 61 let test_valid_simple () = 62 let json = "\"abc123\"" in 63 match decode Id.jsont json with 64 | Ok id -> Alcotest.(check string) "id value" "abc123" (Id.to_string id) 65 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 66 67 let test_valid_single_char () = 68 let json = "\"a\"" in 69 match decode Id.jsont json with 70 | Ok id -> Alcotest.(check string) "id value" "a" (Id.to_string id) 71 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 72 73 let test_valid_with_hyphen () = 74 let json = "\"msg-2024-01\"" in 75 match decode Id.jsont json with 76 | Ok id -> Alcotest.(check string) "id value" "msg-2024-01" (Id.to_string id) 77 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 78 79 let test_valid_with_underscore () = 80 let json = "\"user_id_123\"" in 81 match decode Id.jsont json with 82 | Ok id -> Alcotest.(check string) "id value" "user_id_123" (Id.to_string id) 83 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 84 85 let test_invalid_empty () = 86 let json = "\"\"" in 87 match decode Id.jsont json with 88 | Ok _ -> Alcotest.fail "expected failure for empty id" 89 | Error _ -> () 90 91 let test_invalid_with_space () = 92 let json = "\"hello world\"" in 93 match decode Id.jsont json with 94 | Ok _ -> Alcotest.fail "expected failure for id with space" 95 | Error _ -> () 96 97 let test_invalid_with_special () = 98 let json = "\"abc@def\"" in 99 match decode Id.jsont json with 100 | Ok _ -> Alcotest.fail "expected failure for id with @" 101 | Error _ -> () 102 103 let test_invalid_not_string () = 104 let json = "12345" in 105 match decode Id.jsont json with 106 | Ok _ -> Alcotest.fail "expected failure for non-string" 107 | Error _ -> () 108 109 let test_edge_max_length () = 110 let id_255 = String.make 255 'a' in 111 let json = Printf.sprintf "\"%s\"" id_255 in 112 match decode Id.jsont json with 113 | Ok id -> Alcotest.(check int) "id length" 255 (String.length (Id.to_string id)) 114 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 115 116 let test_edge_over_max_length () = 117 let id_256 = String.make 256 'a' in 118 let json = Printf.sprintf "\"%s\"" id_256 in 119 match decode Id.jsont json with 120 | Ok _ -> Alcotest.fail "expected failure for 256 char id" 121 | Error _ -> () 122 123 let tests = [ 124 "valid: simple", `Quick, test_valid_simple; 125 "valid: single char", `Quick, test_valid_single_char; 126 "valid: with hyphen", `Quick, test_valid_with_hyphen; 127 "valid: with underscore", `Quick, test_valid_with_underscore; 128 "invalid: empty", `Quick, test_invalid_empty; 129 "invalid: with space", `Quick, test_invalid_with_space; 130 "invalid: with special", `Quick, test_invalid_with_special; 131 "invalid: not string", `Quick, test_invalid_not_string; 132 "edge: max length 255", `Quick, test_edge_max_length; 133 "edge: over max length 256", `Quick, test_edge_over_max_length; 134 ] 135end 136 137(* Int53 tests *) 138module Int53_tests = struct 139 open Jmap.Proto 140 141 let test_zero () = 142 match decode Int53.Signed.jsont "0" with 143 | Ok n -> Alcotest.(check int64) "value" 0L n 144 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 145 146 let test_positive () = 147 match decode Int53.Signed.jsont "12345" with 148 | Ok n -> Alcotest.(check int64) "value" 12345L n 149 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 150 151 let test_negative () = 152 match decode Int53.Signed.jsont "-12345" with 153 | Ok n -> Alcotest.(check int64) "value" (-12345L) n 154 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 155 156 let test_max_safe () = 157 match decode Int53.Signed.jsont "9007199254740991" with 158 | Ok n -> Alcotest.(check int64) "value" 9007199254740991L n 159 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 160 161 let test_min_safe () = 162 match decode Int53.Signed.jsont "-9007199254740991" with 163 | Ok n -> Alcotest.(check int64) "value" (-9007199254740991L) n 164 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 165 166 let test_over_max_safe () = 167 match decode Int53.Signed.jsont "9007199254740992" with 168 | Ok _ -> Alcotest.fail "expected failure for over max safe" 169 | Error _ -> () 170 171 let test_under_min_safe () = 172 match decode Int53.Signed.jsont "-9007199254740992" with 173 | Ok _ -> Alcotest.fail "expected failure for under min safe" 174 | Error _ -> () 175 176 let test_unsigned_zero () = 177 match decode Int53.Unsigned.jsont "0" with 178 | Ok n -> Alcotest.(check int64) "value" 0L n 179 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 180 181 let test_unsigned_max () = 182 match decode Int53.Unsigned.jsont "9007199254740991" with 183 | Ok n -> Alcotest.(check int64) "value" 9007199254740991L n 184 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 185 186 let test_unsigned_negative () = 187 match decode Int53.Unsigned.jsont "-1" with 188 | Ok _ -> Alcotest.fail "expected failure for negative unsigned" 189 | Error _ -> () 190 191 let tests = [ 192 "signed: zero", `Quick, test_zero; 193 "signed: positive", `Quick, test_positive; 194 "signed: negative", `Quick, test_negative; 195 "signed: max safe", `Quick, test_max_safe; 196 "signed: min safe", `Quick, test_min_safe; 197 "signed: over max safe", `Quick, test_over_max_safe; 198 "signed: under min safe", `Quick, test_under_min_safe; 199 "unsigned: zero", `Quick, test_unsigned_zero; 200 "unsigned: max", `Quick, test_unsigned_max; 201 "unsigned: negative fails", `Quick, test_unsigned_negative; 202 ] 203end 204 205(* Date tests *) 206module Date_tests = struct 207 open Jmap.Proto 208 209 let test_utc_z () = 210 match decode Date.Utc.jsont "\"2024-01-15T10:30:00Z\"" with 211 | Ok _ -> () 212 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 213 214 let test_rfc3339_with_offset () = 215 match decode Date.Rfc3339.jsont "\"2024-01-15T10:30:00+05:30\"" with 216 | Ok _ -> () 217 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 218 219 let test_with_milliseconds () = 220 match decode Date.Rfc3339.jsont "\"2024-01-15T10:30:00.123Z\"" with 221 | Ok _ -> () 222 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 223 224 let test_invalid_format () = 225 match decode Date.Rfc3339.jsont "\"January 15, 2024\"" with 226 | Ok _ -> Alcotest.fail "expected failure for invalid format" 227 | Error _ -> () 228 229 let test_not_string () = 230 match decode Date.Rfc3339.jsont "1705315800" with 231 | Ok _ -> Alcotest.fail "expected failure for non-string" 232 | Error _ -> () 233 234 let tests = [ 235 "utc: Z suffix", `Quick, test_utc_z; 236 "rfc3339: with offset", `Quick, test_rfc3339_with_offset; 237 "rfc3339: with milliseconds", `Quick, test_with_milliseconds; 238 "invalid: bad format", `Quick, test_invalid_format; 239 "invalid: not string", `Quick, test_not_string; 240 ] 241end 242 243(* Session tests *) 244module Session_tests = struct 245 open Jmap.Proto 246 247 let test_minimal () = 248 test_decode_success "minimal session" Session.jsont "session/valid/minimal.json" () 249 250 let test_with_mail () = 251 test_decode_success "session with mail" Session.jsont "session/valid/with_mail.json" () 252 253 let test_roundtrip_minimal () = 254 test_roundtrip "minimal session roundtrip" Session.jsont "session/valid/minimal.json" () 255 256 let test_values () = 257 let json = read_file "session/valid/minimal.json" in 258 match decode Session.jsont json with 259 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 260 | Ok session -> 261 Alcotest.(check string) "username" "test@example.com" (Session.username session); 262 Alcotest.(check string) "apiUrl" "https://api.example.com/jmap/" (Session.api_url session); 263 Alcotest.(check string) "state" "abc123" (Session.state session); 264 Alcotest.(check bool) "has core capability" true 265 (Session.has_capability Capability.core session) 266 267 let test_with_accounts () = 268 test_decode_success "with accounts" Session.jsont "session/valid/with_accounts.json" () 269 270 let test_empty_accounts () = 271 test_decode_success "empty accounts" Session.jsont "session/edge/empty_accounts.json" () 272 273 let test_accounts_values () = 274 let json = read_file "session/valid/with_accounts.json" in 275 match decode Session.jsont json with 276 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 277 | Ok session -> 278 Alcotest.(check int) "accounts count" 2 (List.length (Session.accounts session)); 279 Alcotest.(check int) "primary_accounts count" 2 (List.length (Session.primary_accounts session)) 280 281 let tests = [ 282 "valid: minimal", `Quick, test_minimal; 283 "valid: with mail", `Quick, test_with_mail; 284 "valid: with accounts", `Quick, test_with_accounts; 285 "edge: empty accounts", `Quick, test_empty_accounts; 286 "roundtrip: minimal", `Quick, test_roundtrip_minimal; 287 "values: minimal", `Quick, test_values; 288 "values: accounts", `Quick, test_accounts_values; 289 ] 290end 291 292(* Request tests *) 293module Request_tests = struct 294 open Jmap.Proto 295 296 let test_single_method () = 297 test_decode_success "single method" Request.jsont "request/valid/single_method.json" () 298 299 let test_multiple_methods () = 300 test_decode_success "multiple methods" Request.jsont "request/valid/multiple_methods.json" () 301 302 let test_with_created_ids () = 303 test_decode_success "with created ids" Request.jsont "request/valid/with_created_ids.json" () 304 305 let test_empty_methods () = 306 test_decode_success "empty methods" Request.jsont "request/valid/empty_methods.json" () 307 308 let test_values () = 309 let json = read_file "request/valid/single_method.json" in 310 match decode Request.jsont json with 311 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 312 | Ok request -> 313 Alcotest.(check int) "using count" 2 (List.length (Request.using request)); 314 Alcotest.(check int) "method calls count" 1 (List.length (Request.method_calls request)) 315 316 let test_roundtrip () = 317 test_roundtrip "single method roundtrip" Request.jsont "request/valid/single_method.json" () 318 319 let tests = [ 320 "valid: single method", `Quick, test_single_method; 321 "valid: multiple methods", `Quick, test_multiple_methods; 322 "valid: with created ids", `Quick, test_with_created_ids; 323 "valid: empty methods", `Quick, test_empty_methods; 324 "values: single method", `Quick, test_values; 325 "roundtrip: single method", `Quick, test_roundtrip; 326 ] 327end 328 329(* Response tests *) 330module Response_tests = struct 331 open Jmap.Proto 332 333 let test_success () = 334 test_decode_success "success" Response.jsont "response/valid/success.json" () 335 336 let test_with_created_ids () = 337 test_decode_success "with created ids" Response.jsont "response/valid/with_created_ids.json" () 338 339 let test_with_error () = 340 test_decode_success "with error" Response.jsont "response/valid/with_error.json" () 341 342 let test_multiple_responses () = 343 test_decode_success "multiple responses" Response.jsont "response/valid/multiple_responses.json" () 344 345 let test_values () = 346 let json = read_file "response/valid/success.json" in 347 match decode Response.jsont json with 348 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 349 | Ok response -> 350 Alcotest.(check string) "session state" "session123" (Response.session_state response); 351 Alcotest.(check int) "method responses count" 1 (List.length (Response.method_responses response)) 352 353 let test_roundtrip () = 354 test_roundtrip "success roundtrip" Response.jsont "response/valid/success.json" () 355 356 let tests = [ 357 "valid: success", `Quick, test_success; 358 "valid: with created ids", `Quick, test_with_created_ids; 359 "valid: with error", `Quick, test_with_error; 360 "valid: multiple responses", `Quick, test_multiple_responses; 361 "values: success", `Quick, test_values; 362 "roundtrip: success", `Quick, test_roundtrip; 363 ] 364end 365 366(* Invocation tests *) 367module Invocation_tests = struct 368 open Jmap.Proto 369 370 let test_get () = 371 test_decode_success "get" Invocation.jsont "invocation/valid/get.json" () 372 373 let test_set () = 374 test_decode_success "set" Invocation.jsont "invocation/valid/set.json" () 375 376 let test_query () = 377 test_decode_success "query" Invocation.jsont "invocation/valid/query.json" () 378 379 let test_values () = 380 let json = read_file "invocation/valid/get.json" in 381 match decode Invocation.jsont json with 382 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 383 | Ok inv -> 384 Alcotest.(check string) "name" "Email/get" (Invocation.name inv); 385 Alcotest.(check string) "method call id" "call-001" (Invocation.method_call_id inv) 386 387 let test_invalid_not_array () = 388 test_decode_failure "not array" Invocation.jsont "invocation/invalid/not_array.json" () 389 390 let test_invalid_wrong_length () = 391 test_decode_failure "wrong length" Invocation.jsont "invocation/invalid/wrong_length.json" () 392 393 let tests = [ 394 "valid: get", `Quick, test_get; 395 "valid: set", `Quick, test_set; 396 "valid: query", `Quick, test_query; 397 "values: get", `Quick, test_values; 398 "invalid: not array", `Quick, test_invalid_not_array; 399 "invalid: wrong length", `Quick, test_invalid_wrong_length; 400 ] 401end 402 403(* Capability tests *) 404module Capability_tests = struct 405 open Jmap.Proto 406 407 let test_core () = 408 test_decode_success "core" Capability.Core.jsont "capability/valid/core.json" () 409 410 let test_mail () = 411 test_decode_success "mail" Capability.Mail.jsont "capability/valid/mail.json" () 412 413 let test_submission () = 414 test_decode_success "submission" Capability.Submission.jsont "capability/valid/submission.json" () 415 416 let test_core_values () = 417 let json = read_file "capability/valid/core.json" in 418 match decode Capability.Core.jsont json with 419 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 420 | Ok cap -> 421 Alcotest.(check int64) "maxSizeUpload" 50000000L (Capability.Core.max_size_upload cap); 422 Alcotest.(check int) "maxConcurrentUpload" 4 (Capability.Core.max_concurrent_upload cap); 423 Alcotest.(check int) "maxCallsInRequest" 16 (Capability.Core.max_calls_in_request cap) 424 425 let test_mail_values () = 426 let json = read_file "capability/valid/mail.json" in 427 match decode Capability.Mail.jsont json with 428 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 429 | Ok cap -> 430 Alcotest.(check int64) "maxSizeMailboxName" 490L (Capability.Mail.max_size_mailbox_name cap); 431 Alcotest.(check bool) "mayCreateTopLevelMailbox" true (Capability.Mail.may_create_top_level_mailbox cap) 432 433 let tests = [ 434 "valid: core", `Quick, test_core; 435 "valid: mail", `Quick, test_mail; 436 "valid: submission", `Quick, test_submission; 437 "values: core", `Quick, test_core_values; 438 "values: mail", `Quick, test_mail_values; 439 ] 440end 441 442(* Method args/response tests *) 443module Method_tests = struct 444 open Jmap.Proto 445 446 let test_get_args () = 447 test_decode_success "get_args" Method.get_args_jsont "method/valid/get_args.json" () 448 449 let test_get_args_minimal () = 450 test_decode_success "get_args_minimal" Method.get_args_jsont "method/valid/get_args_minimal.json" () 451 452 let test_query_response () = 453 test_decode_success "query_response" Method.query_response_jsont "method/valid/query_response.json" () 454 455 let test_changes_response () = 456 test_decode_success "changes_response" Method.changes_response_jsont "method/valid/changes_response.json" () 457 458 let test_get_args_values () = 459 let json = read_file "method/valid/get_args.json" in 460 match decode Method.get_args_jsont json with 461 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 462 | Ok args -> 463 Alcotest.(check string) "accountId" "acc1" (Id.to_string args.account_id); 464 Alcotest.(check (option (list string))) "properties" (Some ["id"; "name"; "role"]) args.properties 465 466 let test_query_response_values () = 467 let json = read_file "method/valid/query_response.json" in 468 match decode Method.query_response_jsont json with 469 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 470 | Ok resp -> 471 Alcotest.(check int) "ids count" 5 (List.length resp.ids); 472 Alcotest.(check int64) "position" 0L resp.position; 473 Alcotest.(check bool) "canCalculateChanges" true resp.can_calculate_changes; 474 Alcotest.(check (option int64)) "total" (Some 250L) resp.total 475 476 let test_changes_response_values () = 477 let json = read_file "method/valid/changes_response.json" in 478 match decode Method.changes_response_jsont json with 479 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 480 | Ok resp -> 481 Alcotest.(check string) "oldState" "old123" resp.old_state; 482 Alcotest.(check string) "newState" "new456" resp.new_state; 483 Alcotest.(check bool) "hasMoreChanges" false resp.has_more_changes; 484 Alcotest.(check int) "created count" 2 (List.length resp.created); 485 Alcotest.(check int) "destroyed count" 2 (List.length resp.destroyed) 486 487 let tests = [ 488 "valid: get_args", `Quick, test_get_args; 489 "valid: get_args_minimal", `Quick, test_get_args_minimal; 490 "valid: query_response", `Quick, test_query_response; 491 "valid: changes_response", `Quick, test_changes_response; 492 "values: get_args", `Quick, test_get_args_values; 493 "values: query_response", `Quick, test_query_response_values; 494 "values: changes_response", `Quick, test_changes_response_values; 495 ] 496end 497 498(* Error tests *) 499module Error_tests = struct 500 open Jmap.Proto 501 502 let test_method_error () = 503 test_decode_success "method_error" Error.method_error_jsont "error/valid/method_error.json" () 504 505 let test_set_error () = 506 test_decode_success "set_error" Error.set_error_jsont "error/valid/set_error.json" () 507 508 let test_request_error () = 509 test_decode_success "request_error" Error.Request_error.jsont "error/valid/request_error.json" () 510 511 let method_error_type_testable = 512 Alcotest.testable 513 (fun fmt t -> Format.pp_print_string fmt (Error.method_error_type_to_string t)) 514 (=) 515 516 let test_method_error_values () = 517 let json = read_file "error/valid/method_error.json" in 518 match decode Error.method_error_jsont json with 519 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 520 | Ok err -> 521 Alcotest.(check method_error_type_testable) "type" `Unknown_method err.type_ 522 523 (* Additional error type tests *) 524 let test_set_error_forbidden () = 525 test_decode_success "set_error_forbidden" Error.set_error_jsont "error/valid/set_error_forbidden.json" () 526 527 let test_set_error_not_found () = 528 test_decode_success "set_error_not_found" Error.set_error_jsont "error/valid/set_error_not_found.json" () 529 530 let test_set_error_invalid_properties () = 531 test_decode_success "set_error_invalid_properties" Error.set_error_jsont "error/valid/set_error_invalid_properties.json" () 532 533 let test_set_error_singleton () = 534 test_decode_success "set_error_singleton" Error.set_error_jsont "error/valid/set_error_singleton.json" () 535 536 let test_set_error_over_quota () = 537 test_decode_success "set_error_over_quota" Error.set_error_jsont "error/valid/set_error_over_quota.json" () 538 539 let test_method_error_invalid_arguments () = 540 test_decode_success "method_error_invalid_arguments" Error.method_error_jsont "error/valid/method_error_invalid_arguments.json" () 541 542 let test_method_error_server_fail () = 543 test_decode_success "method_error_server_fail" Error.method_error_jsont "error/valid/method_error_server_fail.json" () 544 545 let test_method_error_account_not_found () = 546 test_decode_success "method_error_account_not_found" Error.method_error_jsont "error/valid/method_error_account_not_found.json" () 547 548 let test_method_error_forbidden () = 549 test_decode_success "method_error_forbidden" Error.method_error_jsont "error/valid/method_error_forbidden.json" () 550 551 let test_method_error_account_read_only () = 552 test_decode_success "method_error_account_read_only" Error.method_error_jsont "error/valid/method_error_account_read_only.json" () 553 554 let test_request_error_not_json () = 555 test_decode_success "request_error_not_json" Error.Request_error.jsont "error/valid/request_error_not_json.json" () 556 557 let test_request_error_limit () = 558 test_decode_success "request_error_limit" Error.Request_error.jsont "error/valid/request_error_limit.json" () 559 560 let set_error_type_testable = 561 Alcotest.testable 562 (fun fmt t -> Format.pp_print_string fmt (Error.set_error_type_to_string t)) 563 (=) 564 565 let test_set_error_types () = 566 let json = read_file "error/valid/set_error_invalid_properties.json" in 567 match decode Error.set_error_jsont json with 568 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 569 | Ok err -> 570 Alcotest.(check set_error_type_testable) "type" `Invalid_properties err.Error.type_; 571 match err.Error.properties with 572 | None -> Alcotest.fail "expected properties" 573 | Some props -> Alcotest.(check int) "properties count" 2 (List.length props) 574 575 let tests = [ 576 "valid: method_error", `Quick, test_method_error; 577 "valid: set_error", `Quick, test_set_error; 578 "valid: request_error", `Quick, test_request_error; 579 "valid: set_error forbidden", `Quick, test_set_error_forbidden; 580 "valid: set_error notFound", `Quick, test_set_error_not_found; 581 "valid: set_error invalidProperties", `Quick, test_set_error_invalid_properties; 582 "valid: set_error singleton", `Quick, test_set_error_singleton; 583 "valid: set_error overQuota", `Quick, test_set_error_over_quota; 584 "valid: method_error invalidArguments", `Quick, test_method_error_invalid_arguments; 585 "valid: method_error serverFail", `Quick, test_method_error_server_fail; 586 "valid: method_error accountNotFound", `Quick, test_method_error_account_not_found; 587 "valid: method_error forbidden", `Quick, test_method_error_forbidden; 588 "valid: method_error accountReadOnly", `Quick, test_method_error_account_read_only; 589 "valid: request_error notJSON", `Quick, test_request_error_not_json; 590 "valid: request_error limit", `Quick, test_request_error_limit; 591 "values: method_error", `Quick, test_method_error_values; 592 "values: set_error types", `Quick, test_set_error_types; 593 ] 594end 595 596(* Mailbox tests *) 597module Mailbox_tests = struct 598 open Jmap.Proto 599 600 let role_testable = 601 Alcotest.testable 602 (fun fmt t -> Format.pp_print_string fmt (Mailbox.role_to_string t)) 603 (=) 604 605 let test_simple () = 606 test_decode_success "simple" Mailbox.jsont "mail/mailbox/valid/simple.json" () 607 608 let test_nested () = 609 test_decode_success "nested" Mailbox.jsont "mail/mailbox/valid/nested.json" () 610 611 let test_values () = 612 let json = read_file "mail/mailbox/valid/simple.json" in 613 match decode Mailbox.jsont json with 614 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 615 | Ok mb -> 616 Alcotest.(check string) "id" "mb1" (get_id (Mailbox.id mb)); 617 Alcotest.(check string) "name" "Inbox" (get_string (Mailbox.name mb)); 618 Alcotest.(check (option role_testable)) "role" (Some `Inbox) (Mailbox.role mb); 619 Alcotest.(check int64) "totalEmails" 150L (get_int64 (Mailbox.total_emails mb)); 620 Alcotest.(check int64) "unreadEmails" 5L (get_int64 (Mailbox.unread_emails mb)) 621 622 let test_roundtrip () = 623 test_roundtrip "simple roundtrip" Mailbox.jsont "mail/mailbox/valid/simple.json" () 624 625 let test_with_all_roles () = 626 test_decode_success "with all roles" Mailbox.jsont "mail/mailbox/valid/with_all_roles.json" () 627 628 let test_all_rights_false () = 629 test_decode_success "all rights false" Mailbox.jsont "mail/mailbox/edge/all_rights_false.json" () 630 631 let test_roles_values () = 632 let json = read_file "mail/mailbox/valid/with_all_roles.json" in 633 match decode Mailbox.jsont json with 634 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 635 | Ok mb -> 636 Alcotest.(check (option role_testable)) "role" (Some `Archive) (Mailbox.role mb); 637 Alcotest.(check int64) "totalEmails" 1000L (get_int64 (Mailbox.total_emails mb)) 638 639 let tests = [ 640 "valid: simple", `Quick, test_simple; 641 "valid: nested", `Quick, test_nested; 642 "valid: with all roles", `Quick, test_with_all_roles; 643 "edge: all rights false", `Quick, test_all_rights_false; 644 "values: simple", `Quick, test_values; 645 "values: roles", `Quick, test_roles_values; 646 "roundtrip: simple", `Quick, test_roundtrip; 647 ] 648end 649 650(* Email tests *) 651module Email_tests = struct 652 open Jmap.Proto 653 654 let test_minimal () = 655 test_decode_success "minimal" Email.jsont "mail/email/valid/minimal.json" () 656 657 let test_full () = 658 test_decode_success "full" Email.jsont "mail/email/valid/full.json" () 659 660 let test_with_headers () = 661 test_decode_success "with_headers" Email.jsont "mail/email/valid/with_headers.json" () 662 663 let test_minimal_values () = 664 let json = read_file "mail/email/valid/minimal.json" in 665 match decode Email.jsont json with 666 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 667 | Ok email -> 668 Alcotest.(check string) "id" "e1" (get_id (Email.id email)); 669 Alcotest.(check string) "blobId" "blob1" (get_id (Email.blob_id email)); 670 Alcotest.(check int64) "size" 1024L (get_int64 (Email.size email)) 671 672 let test_full_values () = 673 let json = read_file "mail/email/valid/full.json" in 674 match decode Email.jsont json with 675 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 676 | Ok email -> 677 Alcotest.(check (option string)) "subject" (Some "Re: Important meeting") (Email.subject email); 678 Alcotest.(check bool) "hasAttachment" true (get_bool (Email.has_attachment email)); 679 (* Check from address *) 680 match Email.from email with 681 | None -> Alcotest.fail "expected from address" 682 | Some addrs -> 683 Alcotest.(check int) "from count" 1 (List.length addrs); 684 let addr = List.hd addrs in 685 Alcotest.(check (option string)) "from name" (Some "Alice Smith") (Email_address.name addr); 686 Alcotest.(check string) "from email" "alice@example.com" (Email_address.email addr) 687 688 let test_with_keywords () = 689 test_decode_success "with keywords" Email.jsont "mail/email/valid/with_keywords.json" () 690 691 let test_multiple_mailboxes () = 692 test_decode_success "multiple mailboxes" Email.jsont "mail/email/valid/multiple_mailboxes.json" () 693 694 let test_draft_email () = 695 test_decode_success "draft email" Email.jsont "mail/email/valid/draft_email.json" () 696 697 let test_with_all_system_keywords () = 698 test_decode_success "all system keywords" Email.jsont "mail/email/valid/with_all_system_keywords.json" () 699 700 let test_empty_keywords () = 701 test_decode_success "empty keywords" Email.jsont "mail/email/edge/empty_keywords.json" () 702 703 let test_with_message_ids () = 704 test_decode_success "with message ids" Email.jsont "mail/email/valid/with_message_ids.json" () 705 706 let test_keywords_values () = 707 let json = read_file "mail/email/valid/with_keywords.json" in 708 match decode Email.jsont json with 709 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 710 | Ok email -> 711 let keywords = Option.value ~default:[] (Email.keywords email) in 712 Alcotest.(check int) "keywords count" 3 (List.length keywords); 713 Alcotest.(check bool) "$seen present" true (List.mem_assoc "$seen" keywords); 714 Alcotest.(check bool) "$flagged present" true (List.mem_assoc "$flagged" keywords) 715 716 let test_mailbox_ids_values () = 717 let json = read_file "mail/email/valid/multiple_mailboxes.json" in 718 match decode Email.jsont json with 719 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 720 | Ok email -> 721 let mailbox_ids = Option.value ~default:[] (Email.mailbox_ids email) in 722 Alcotest.(check int) "mailboxIds count" 3 (List.length mailbox_ids) 723 724 let tests = [ 725 "valid: minimal", `Quick, test_minimal; 726 "valid: full", `Quick, test_full; 727 "valid: with_headers", `Quick, test_with_headers; 728 "valid: with keywords", `Quick, test_with_keywords; 729 "valid: multiple mailboxes", `Quick, test_multiple_mailboxes; 730 "valid: draft email", `Quick, test_draft_email; 731 "valid: all system keywords", `Quick, test_with_all_system_keywords; 732 "valid: with message ids", `Quick, test_with_message_ids; 733 "edge: empty keywords", `Quick, test_empty_keywords; 734 "values: minimal", `Quick, test_minimal_values; 735 "values: full", `Quick, test_full_values; 736 "values: keywords", `Quick, test_keywords_values; 737 "values: mailboxIds", `Quick, test_mailbox_ids_values; 738 ] 739end 740 741(* Thread tests *) 742module Thread_tests = struct 743 open Jmap.Proto 744 745 let test_simple () = 746 test_decode_success "simple" Thread.jsont "mail/thread/valid/simple.json" () 747 748 let test_conversation () = 749 test_decode_success "conversation" Thread.jsont "mail/thread/valid/conversation.json" () 750 751 let test_values () = 752 let json = read_file "mail/thread/valid/conversation.json" in 753 match decode Thread.jsont json with 754 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 755 | Ok thread -> 756 Alcotest.(check string) "id" "t2" (get_id (Thread.id thread)); 757 Alcotest.(check int) "emailIds count" 5 (List.length (Option.value ~default:[] (Thread.email_ids thread))) 758 759 let tests = [ 760 "valid: simple", `Quick, test_simple; 761 "valid: conversation", `Quick, test_conversation; 762 "values: conversation", `Quick, test_values; 763 ] 764end 765 766(* Identity tests *) 767module Identity_tests = struct 768 open Jmap.Proto 769 770 let test_simple () = 771 test_decode_success "simple" Identity.jsont "mail/identity/valid/simple.json" () 772 773 let test_values () = 774 let json = read_file "mail/identity/valid/simple.json" in 775 match decode Identity.jsont json with 776 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 777 | Ok ident -> 778 Alcotest.(check string) "name" "Work Identity" (get_string (Identity.name ident)); 779 Alcotest.(check string) "email" "john.doe@company.com" (get_string (Identity.email ident)); 780 Alcotest.(check bool) "mayDelete" true (get_bool (Identity.may_delete ident)) 781 782 let tests = [ 783 "valid: simple", `Quick, test_simple; 784 "values: simple", `Quick, test_values; 785 ] 786end 787 788(* Email address tests *) 789module Email_address_tests = struct 790 open Jmap.Proto 791 792 let test_full () = 793 test_decode_success "full" Email_address.jsont "mail/email_address/valid/full.json" () 794 795 let test_email_only () = 796 test_decode_success "email_only" Email_address.jsont "mail/email_address/valid/email_only.json" () 797 798 let test_full_values () = 799 let json = read_file "mail/email_address/valid/full.json" in 800 match decode Email_address.jsont json with 801 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 802 | Ok addr -> 803 Alcotest.(check (option string)) "name" (Some "John Doe") (Email_address.name addr); 804 Alcotest.(check string) "email" "john.doe@example.com" (Email_address.email addr) 805 806 let test_email_only_values () = 807 let json = read_file "mail/email_address/valid/email_only.json" in 808 match decode Email_address.jsont json with 809 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 810 | Ok addr -> 811 Alcotest.(check (option string)) "name" None (Email_address.name addr); 812 Alcotest.(check string) "email" "anonymous@example.com" (Email_address.email addr) 813 814 let tests = [ 815 "valid: full", `Quick, test_full; 816 "valid: email_only", `Quick, test_email_only; 817 "values: full", `Quick, test_full_values; 818 "values: email_only", `Quick, test_email_only_values; 819 ] 820end 821 822(* Vacation tests *) 823module Vacation_tests = struct 824 open Jmap.Proto 825 826 let test_enabled () = 827 test_decode_success "enabled" Vacation.jsont "mail/vacation/valid/enabled.json" () 828 829 let test_disabled () = 830 test_decode_success "disabled" Vacation.jsont "mail/vacation/valid/disabled.json" () 831 832 let test_enabled_values () = 833 let json = read_file "mail/vacation/valid/enabled.json" in 834 match decode Vacation.jsont json with 835 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 836 | Ok vac -> 837 Alcotest.(check bool) "isEnabled" true (Vacation.is_enabled vac); 838 Alcotest.(check (option string)) "subject" (Some "Out of Office") (Vacation.subject vac) 839 840 let test_disabled_values () = 841 let json = read_file "mail/vacation/valid/disabled.json" in 842 match decode Vacation.jsont json with 843 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 844 | Ok vac -> 845 Alcotest.(check bool) "isEnabled" false (Vacation.is_enabled vac); 846 Alcotest.(check (option string)) "subject" None (Vacation.subject vac) 847 848 let tests = [ 849 "valid: enabled", `Quick, test_enabled; 850 "valid: disabled", `Quick, test_disabled; 851 "values: enabled", `Quick, test_enabled_values; 852 "values: disabled", `Quick, test_disabled_values; 853 ] 854end 855 856(* Comparator tests *) 857module Comparator_tests = struct 858 open Jmap.Proto 859 860 let test_minimal () = 861 test_decode_success "minimal" Filter.comparator_jsont "filter/valid/comparator_minimal.json" () 862 863 let test_descending () = 864 test_decode_success "descending" Filter.comparator_jsont "filter/valid/comparator_descending.json" () 865 866 let test_with_collation () = 867 test_decode_success "with collation" Filter.comparator_jsont "filter/valid/comparator_with_collation.json" () 868 869 let test_minimal_values () = 870 let json = read_file "filter/valid/comparator_minimal.json" in 871 match decode Filter.comparator_jsont json with 872 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 873 | Ok comp -> 874 Alcotest.(check string) "property" "size" (Filter.comparator_property comp); 875 Alcotest.(check bool) "isAscending" true (Filter.comparator_is_ascending comp); 876 Alcotest.(check (option string)) "collation" None (Filter.comparator_collation comp) 877 878 let test_collation_values () = 879 let json = read_file "filter/valid/comparator_with_collation.json" in 880 match decode Filter.comparator_jsont json with 881 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 882 | Ok comp -> 883 Alcotest.(check string) "property" "subject" (Filter.comparator_property comp); 884 Alcotest.(check (option string)) "collation" (Some "i;unicode-casemap") (Filter.comparator_collation comp) 885 886 let tests = [ 887 "valid: minimal", `Quick, test_minimal; 888 "valid: descending", `Quick, test_descending; 889 "valid: with collation", `Quick, test_with_collation; 890 "values: minimal", `Quick, test_minimal_values; 891 "values: with collation", `Quick, test_collation_values; 892 ] 893end 894 895(* EmailBody tests *) 896module EmailBody_tests = struct 897 open Jmap.Proto 898 899 let test_text_part () = 900 test_decode_success "text part" Email_body.Part.jsont "mail/email_body/valid/text_part.json" () 901 902 let test_multipart () = 903 test_decode_success "multipart" Email_body.Part.jsont "mail/email_body/valid/multipart.json" () 904 905 let test_multipart_mixed () = 906 test_decode_success "multipart mixed" Email_body.Part.jsont "mail/email_body/valid/multipart_mixed.json" () 907 908 let test_with_inline_image () = 909 test_decode_success "with inline image" Email_body.Part.jsont "mail/email_body/valid/with_inline_image.json" () 910 911 let test_with_language () = 912 test_decode_success "with language" Email_body.Part.jsont "mail/email_body/valid/with_language.json" () 913 914 let test_deep_nesting () = 915 test_decode_success "deep nesting" Email_body.Part.jsont "mail/email_body/edge/deep_nesting.json" () 916 917 let test_multipart_values () = 918 let json = read_file "mail/email_body/valid/multipart.json" in 919 match decode Email_body.Part.jsont json with 920 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 921 | Ok part -> 922 Alcotest.(check (option string)) "partId" (Some "0") (Email_body.Part.part_id part); 923 Alcotest.(check string) "type" "multipart/alternative" (Email_body.Part.type_ part); 924 match Email_body.Part.sub_parts part with 925 | None -> Alcotest.fail "expected sub_parts" 926 | Some subs -> Alcotest.(check int) "sub_parts count" 2 (List.length subs) 927 928 let tests = [ 929 "valid: text part", `Quick, test_text_part; 930 "valid: multipart", `Quick, test_multipart; 931 "valid: multipart mixed", `Quick, test_multipart_mixed; 932 "valid: with inline image", `Quick, test_with_inline_image; 933 "valid: with language", `Quick, test_with_language; 934 "edge: deep nesting", `Quick, test_deep_nesting; 935 "values: multipart", `Quick, test_multipart_values; 936 ] 937end 938 939(* EmailSubmission tests *) 940module EmailSubmission_tests = struct 941 open Jmap.Proto 942 943 let test_simple () = 944 test_decode_success "simple" Submission.jsont "mail/submission/valid/simple.json" () 945 946 let test_with_envelope () = 947 test_decode_success "with envelope" Submission.jsont "mail/submission/valid/with_envelope.json" () 948 949 let test_final_status () = 950 test_decode_success "final status" Submission.jsont "mail/submission/valid/final_status.json" () 951 952 let test_simple_values () = 953 let json = read_file "mail/submission/valid/simple.json" in 954 match decode Submission.jsont json with 955 | Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e) 956 | Ok sub -> 957 Alcotest.(check string) "id" "sub1" (get_id (Submission.id sub)); 958 (* Check undoStatus is Pending *) 959 match Submission.undo_status sub with 960 | Some `Pending -> () 961 | _ -> Alcotest.fail "expected undoStatus to be pending" 962 963 let tests = [ 964 "valid: simple", `Quick, test_simple; 965 "valid: with envelope", `Quick, test_with_envelope; 966 "valid: final status", `Quick, test_final_status; 967 "values: simple", `Quick, test_simple_values; 968 ] 969end 970 971(* Run all tests *) 972let () = 973 Alcotest.run "JMAP Proto Codecs" [ 974 "Id", Id_tests.tests; 975 "Int53", Int53_tests.tests; 976 "Date", Date_tests.tests; 977 "Session", Session_tests.tests; 978 "Request", Request_tests.tests; 979 "Response", Response_tests.tests; 980 "Invocation", Invocation_tests.tests; 981 "Capability", Capability_tests.tests; 982 "Method", Method_tests.tests; 983 "Error", Error_tests.tests; 984 "Comparator", Comparator_tests.tests; 985 "Mailbox", Mailbox_tests.tests; 986 "Email", Email_tests.tests; 987 "EmailBody", EmailBody_tests.tests; 988 "Thread", Thread_tests.tests; 989 "Identity", Identity_tests.tests; 990 "Email_address", Email_address_tests.tests; 991 "EmailSubmission", EmailSubmission_tests.tests; 992 "Vacation", Vacation_tests.tests; 993 ]