let n_of_s = Domain_name.of_string_exn let raw = let module M = struct type t = [ `raw ] Domain_name.t let pp = Domain_name.pp let equal = Domain_name.equal ~case_sensitive:false end in (module M: Alcotest.TESTABLE with type t = M.t) let host = let module M = struct type t = [ `host ] Domain_name.t let pp = Domain_name.pp let equal = Domain_name.equal ~case_sensitive:false end in (module M: Alcotest.TESTABLE with type t = M.t) let service = let module M = struct type t = [ `service ] Domain_name.t let pp = Domain_name.pp let equal = Domain_name.equal ~case_sensitive:false end in (module M: Alcotest.TESTABLE with type t = M.t) let p_msg = let module M = struct type t = [ `Msg of string ] let pp ppf (`Msg m) = Fmt.string ppf m let equal (`Msg _) (`Msg _) = true end in (module M: Alcotest.TESTABLE with type t = M.t) let is_domain x = match Domain_name.of_string x with | Ok _ -> true | Error _ -> false let is_host x = match Domain_name.host x with | Ok _ -> true | Error _ -> false let is_service x = match Domain_name.service x with | Ok _ -> true | Error _ -> false let longest_label = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk" let longest_prefix = let d a b = a ^ "." ^ b in d longest_label (d longest_label longest_label) let basic_preds () = Alcotest.(check bool "root is_hostname" true (is_host Domain_name.root)) ; Alcotest.(check bool "foo is a hostname" true (is_host (n_of_s "foo"))) ; Alcotest.(check bool ".foo is no domain" false (is_domain ".foo")) ; Alcotest.(check bool "bar is a hostname" true (is_host (n_of_s "bar"))) ; Alcotest.(check bool "foo.bar is a hostname" true (is_host (n_of_s "foo.bar"))) ; Alcotest.(check bool "longest label is domain name" true (is_domain longest_label)) ; Alcotest.(check bool "longest label + a is not domain name" false (is_domain (longest_label ^ "a"))) ; Alcotest.(check bool "ll.ll.ll.ll[:-2] is domain name" true (is_domain (longest_prefix ^ "." ^ (String.sub longest_label 0 61)))) ; Alcotest.(check bool "ll.ll.ll.ll[:-1] is not a domain name" false (is_domain (longest_prefix ^ "." ^ (String.sub longest_label 0 62)))) ; Alcotest.(check bool "foo._bar is not a hostname" false (is_host (n_of_s "foo._bar"))) ; Alcotest.(check bool "2foo.bar is a hostname" true (is_host (n_of_s "2foo.bar"))) ; Alcotest.(check bool "f2.bar is a hostname" true (is_host (n_of_s "f2.bar"))) ; Alcotest.(check bool "-f2.bar is not a hostname" false (is_host (n_of_s "-f2.bar"))) ; Alcotest.(check bool "f2.23 is not a hostname" false (is_host (n_of_s "f2.23"))) ; Alcotest.(check bool "42.23b is a hostname" true (is_host (n_of_s "42.23b"))) ; Alcotest.(check bool "'bar.foo is not a hostname" false (is_host (n_of_s "'bar.foo"))) ; Alcotest.(check bool "-foo.bar is not a hostname" false (is_host (n_of_s "'-foo.bar"))) ; Alcotest.(check bool "foo-.bar is not a hostname" false (is_host (n_of_s "foo-.bar"))) ; Alcotest.(check bool "f-o-o.bar is a hostname" true (is_host (n_of_s "f-o-o.bar"))) ; Alcotest.(check bool "2f.b3 is a hostname" true (is_host (n_of_s "2f.b3"))) ; Alcotest.(check bool "2f3.2b3 is a hostname" true (is_host (n_of_s "2f3.2b3"))) ; Alcotest.(check bool "root is no service" false (is_service Domain_name.root)) ; Alcotest.(check bool "_tcp.foo is no service" false (is_service (n_of_s "_tcp.foo"))) ; Alcotest.(check bool "_._tcp.foo is no service" false (is_service (n_of_s "_._tcp.foo"))) ; Alcotest.(check bool "foo._tcp.foo is no service" false (is_service (n_of_s "foo._tcp.foo"))) ; Alcotest.(check bool "f_oo._tcp.foo is no service" false (is_service (n_of_s "f_oo._tcp.foo"))) ; Alcotest.(check bool "foo_._tcp.foo is no service" false (is_service (n_of_s "foo_._tcp.foo"))) ; Alcotest.(check bool "_xmpp-server._tcp.foo is a service" true (is_service (n_of_s "_xmpp-server._tcp.foo"))) ; Alcotest.(check bool "_xmpp-server._tcp2.foo is no service" false (is_service (n_of_s "_xmpp-server._tcp2.foo"))) ; Alcotest.(check bool "_xmpp_server._tcp.foo is no service" false (is_service (n_of_s "_xmpp_server._tcp.foo"))) ; Alcotest.(check bool "_xmpp-server-server._tcp.foo is no service" false (is_service (n_of_s "_xmpp-server-server._tcp.foo"))) ; Alcotest.(check bool "_443._tcp.foo is a service" true (is_service (n_of_s "_443._tcp.foo"))) ; let foo = n_of_s "foo" in Alcotest.(check bool "foo is no subdomain of foo.bar" false (Domain_name.is_subdomain ~subdomain:foo ~domain:(n_of_s "foo.bar"))) ; Alcotest.(check bool "foo is a subdomain of foo" true (Domain_name.is_subdomain ~subdomain:foo ~domain:foo)) ; Alcotest.(check bool "bar.foo is a subdomain of foo" true (Domain_name.is_subdomain ~subdomain:(n_of_s "bar.foo") ~domain:foo)) let case () = Alcotest.(check bool "foo123.com and Foo123.com are equal" true (Domain_name.equal (n_of_s "foo123.com") (n_of_s "Foo123.com"))) ; Alcotest.(check bool "foo123.com and Foo123.com are not equal if case" false (Domain_name.equal ~case_sensitive:true (n_of_s "foo123.com") (n_of_s "Foo123.com"))) ; Alcotest.(check bool "foo-123.com and com are not equal" false (Domain_name.equal (n_of_s "foo-123.com") (n_of_s "com"))) ; Alcotest.(check bool "foo123.com and Foo123.com are equal if case _and_ canonical used on second" true Domain_name.(equal ~case_sensitive:true (n_of_s "foo123.com") (canonical (n_of_s "Foo123.com")))) ; Alcotest.(check bool "foo123.com and Foo123.com are not equal if case _and_ canonical used on first" false Domain_name.(equal ~case_sensitive:true (canonical (n_of_s "foo123.com")) (n_of_s "Foo123.com"))) ; Alcotest.(check bool "foo123.com and Foo123.com are equal if case _and_ canonical used on both" true Domain_name.(equal ~case_sensitive:true (canonical (n_of_s "foo123.com")) (canonical (n_of_s "Foo123.com")))) let p_name = Alcotest.testable Domain_name.pp Domain_name.equal let basic_name () = let lll = String.sub longest_label 0 61 and llt = String.sub longest_label 0 62 in Alcotest.(check bool "prepend '_foo' to root is not valid hostname" false (is_host (Domain_name.prepend_label_exn Domain_name.root "_foo"))) ; Alcotest.(check bool "host (of_strings [ '_foo' ; 'bar' ]) is not valid" false (is_host (Domain_name.of_strings_exn [ "_foo" ; "bar" ]))) ; Alcotest.(check (result p_name p_msg) "of_string 'foo.bar' is valid" (Ok (n_of_s "foo.bar")) (Domain_name.of_string "foo.bar")) ; Alcotest.(check bool "host (of_string 'foo.bar') is valid" true (is_host (Domain_name.of_string_exn "foo.bar"))) ; Alcotest.(check p_name "of_array 'foo.bar' is good" (n_of_s "foo.bar") (Domain_name.of_array [| "bar" ; "foo" |])) ; Alcotest.(check bool "host (of_array 'foo.bar') is good" true (is_host (Domain_name.of_array [| "bar" ; "foo" |]))) ; Alcotest.(check bool "host (prepend (ll[:-2]) (ll ^ ll ^ ll)) is valid" true (is_host (Domain_name.prepend_label_exn (n_of_s longest_prefix) lll))) ; Alcotest.(check (result p_name p_msg) "prepend '' root is invalid" (Error (`Msg "")) (Domain_name.prepend_label Domain_name.root "")) ; Alcotest.(check (result p_name p_msg) "prepend ll^a root is invalid" (Error (`Msg "")) (Domain_name.prepend_label Domain_name.root (longest_label ^ "a"))) ; Alcotest.(check (result p_name p_msg) "prepend ll (ll ^ ll ^ ll) is invalid" (Error (`Msg "")) (Domain_name.prepend_label (n_of_s longest_prefix) longest_label)) ; Alcotest.(check (result p_name p_msg) "prepend ll[:-1] (ll ^ ll ^ ll) is invalid" (Error (`Msg "")) (Domain_name.prepend_label (n_of_s longest_prefix) llt)) ; Alcotest.(check (result p_name p_msg) "concat 'foo.bar' 'baz.barf' is good" (Ok (n_of_s "foo.bar.baz.barf")) (Domain_name.append (n_of_s "foo.bar") (n_of_s "baz.barf"))) ; let r = Domain_name.prepend_label_exn (n_of_s longest_prefix) lll in Alcotest.(check (result p_name p_msg) "concat ll[:-2] lp is good" (Ok r) (Domain_name.append (n_of_s lll) (n_of_s longest_prefix))) ; Alcotest.(check (result p_name p_msg) "concat ll[:-1] lp is bad" (Error (`Msg "")) (Domain_name.append (n_of_s llt) (n_of_s longest_prefix))) let fqdn () = Alcotest.(check bool "of_string_exn example.com = of_string_exn example.com." true (Domain_name.equal (n_of_s "example.com") (n_of_s "example.com."))) ; Alcotest.(check bool "of_strings_exn ['example' ; 'com'] = of_strings_exn ['example' ; 'com' ; '']" true Domain_name.(equal (of_strings_exn [ "example" ; "com" ]) (of_strings_exn [ "example" ; "com" ; "" ]))); try Alcotest.(check bool {|of_string_exn "" = of_string_exn "."|}) true Domain_name.(equal (n_of_s "") (n_of_s ".")) with Invalid_argument _ -> Alcotest.fail "invalid domain name for root" let fqdn_around () = let d = n_of_s "foo.com." in Alcotest.(check bool "of_string (to_string (of_string 'foo.com.')) works" true Domain_name.(equal d (of_string_exn (to_string d)))) ; Alcotest.(check bool "of_string (to_string ~trailing:true (of_string 'foo.com.')) works" true Domain_name.(equal d (of_string_exn (to_string ~trailing:true d)))); try Alcotest.(check bool "of_string (to_string ~trailing:true (of_string '.')) works") true Domain_name.(equal root (of_string_exn (to_string ~trailing:true root))) with Invalid_argument _ -> Alcotest.fail "invalid domain name for root" let drop_labels () = let res = n_of_s "foo.com" in Alcotest.(check p_name "dropping 1 label from www.foo.com is foo.com" res (Domain_name.drop_label_exn (Domain_name.of_string_exn "www.foo.com"))) ; Alcotest.(check p_name "dropping 2 labels from www.bar.foo.com is foo.com" res (Domain_name.drop_label_exn ~amount:2 (Domain_name.of_string_exn "www.bar.foo.com"))) ; Alcotest.(check p_name "dropping 1 label from the back www.foo.com is www.foo" (Domain_name.of_string_exn "www.foo") (Domain_name.drop_label_exn ~rev:true (Domain_name.of_string_exn "www.foo.com"))) ; Alcotest.(check p_name "prepending 1 and dropping 1 label from foo.com is foo.com" res (Domain_name.drop_label_exn (Domain_name.prepend_label_exn (Domain_name.of_string_exn "foo.com") "www"))) ; Alcotest.(check p_name "prepending 1 and dropping 1 label from foo.com is foo.com" res (Domain_name.drop_label_exn (Domain_name.prepend_label_exn (Domain_name.of_string_exn "foo.com") "www"))) ; Alcotest.(check (result p_name p_msg) "dropping 10 labels from foo.com leads to error" (Error (`Msg "")) (Domain_name.drop_label ~amount:10 (Domain_name.of_string_exn "foo.com"))) let get_and_count_and_find_label () = Alcotest.(check int "count labels of root is 0" 0 Domain_name.(count_labels root)); Alcotest.(check (result string p_msg) "get_label 0 of root is Error" (Error (`Msg "")) Domain_name.(get_label root 0)); Alcotest.(check (result string p_msg) "get_label 1 of root is Error" (Error (`Msg "")) Domain_name.(get_label root 1)); Alcotest.(check (result string p_msg) "get_label 2 of root is Error" (Error (`Msg "")) Domain_name.(get_label root 2)); Alcotest.(check (result string p_msg) "get_label -1 of root is Error" (Error (`Msg "")) Domain_name.(get_label root (-1))); Alcotest.(check (option int) "find_label root '' is none" None Domain_name.(find_label root (fun _ -> true))); Alcotest.(check (option int) "find_label root 'a' is none" None Domain_name.(find_label root (equal_label "a"))); let n = n_of_s "www.example.com" in Alcotest.(check int "count labels of www.example.com is 3" 3 (Domain_name.count_labels n)); Alcotest.(check (result string p_msg) "get_label 0 of n is Ok www" (Ok "www") (Domain_name.get_label n 0)); Alcotest.(check (result string p_msg) "get_label 1 of n is Ok example" (Ok "example") (Domain_name.get_label n 1)); Alcotest.(check (result string p_msg) "get_label 2 of n is Ok com" (Ok "com") (Domain_name.get_label n 2)); Alcotest.(check (result string p_msg) "get_label 3 of n is Error" (Error (`Msg "")) (Domain_name.get_label n 3)); Alcotest.(check (result string p_msg) "get_label ~rev:true 0 of n is Ok com" (Ok "com") (Domain_name.get_label ~rev:true n 0)); Alcotest.(check (result string p_msg) "get_label ~rev:true 1 of n is Ok example" (Ok "example") (Domain_name.get_label ~rev:true n 1)); Alcotest.(check (result string p_msg) "get_label ~rev:true 2 of n is Ok www" (Ok "www") (Domain_name.get_label ~rev:true n 2)); Alcotest.(check (result string p_msg) "get_label ~rev:true 3 of n is Error" (Error (`Msg "")) (Domain_name.get_label ~rev:true n 3)); Alcotest.(check (option int) "find_label www.example.com is Some 0" (Some 0) Domain_name.(find_label n (fun _ -> true))); Alcotest.(check (option int) "find_label www.example.com 'a' is none" None Domain_name.(find_label n (equal_label "a"))); Alcotest.(check (option int) "find_label www.example.com 'w' is none" None Domain_name.(find_label n (equal_label "w"))); Alcotest.(check (option int) "find_label www.example.com 'www' is Some 0" (Some 0) Domain_name.(find_label n (equal_label "www"))); Alcotest.(check (option int) "find_label www.example.com 'WWW' is Some 0" (Some 0) Domain_name.(find_label n (equal_label "WWW"))); Alcotest.(check (option int) "find_label www.example.com 'WWW' is None (case)" None Domain_name.(find_label n (equal_label ~case_sensitive:true "WWW"))); let n' = Domain_name.of_string_exn "www.www.www" in Alcotest.(check (option int) "find_label www.www.www 'www' is 0" (Some 0) Domain_name.(find_label n' (equal_label "www"))); Alcotest.(check (option int) "find_label ~back:true www.www.www 'www' is 2" (Some 2) Domain_name.(find_label ~rev:true n' (equal_label "www"))) let test_compare_canonical () = (* from RFC 4034, 6.1 *) let names = List.map n_of_s [ "example" ; "a.example" ; "yljkjljk.a.example" ; "Z.a.example" ; "zABC.a.EXAMPLE" ; "z.example" ; "\001.z.example" ; "*.z.example" ; "\200.z.example" ] in let sorted_names = List.sort Domain_name.compare names in Alcotest.(check (list raw) "compare fulfills canonical form and order" names sorted_names) let tests = [ "basic predicates", `Quick, basic_preds ; "basic name stuff", `Quick, basic_name ; "case", `Quick, case ; "fqdn", `Quick, fqdn ; "fqdn around", `Quick, fqdn_around ; "drop labels", `Quick, drop_labels ; "get and count and find labels", `Quick, get_and_count_and_find_label ; "sorting", `Quick, test_compare_canonical ; ] let suites = [ "domain names", tests ; ] let () = Alcotest.run "domain name tests" suites