module Status = struct type t = Exactly of int | One_of of int Nonempty.t let exactly status = Exactly status let matches expected actual = match expected with | Exactly status -> status = actual | One_of statuses -> Nonempty.exists (( = ) actual) statuses end module Body = struct type t = Exact of string | Prefix of string | Any let matches expected actual = match expected with | Any -> true | Exact body -> String.equal body actual | Prefix prefix -> let prefix_length = String.length prefix in String.length actual >= prefix_length && String.equal prefix (String.sub actual 0 prefix_length) end module Field = struct type t = | Present of Fields.name | Absent of Fields.name | Contains of Fields.name * string | First of Fields.name * string | Exactly of Fields.name * string list let matches expected fields = match expected with | Present name -> Fields.values name fields <> [] | Absent name -> Fields.values name fields = [] | Contains (name, expected) -> List.exists (String.equal expected) (Fields.values name fields) | First (name, expected) -> ( match Fields.values name fields with | actual :: _ -> String.equal expected actual | [] -> false) | Exactly (name, expected) -> Fields.values name fields = expected end type t = { status : Status.t; body : Body.t; headers : Field.t list; trailers : Field.t list; } type message = { status : int; body : string; headers : Fields.t; trailers : Fields.t; } let matches (expected : t) (actual : message) = Status.matches expected.status actual.status && Body.matches expected.body actual.body && List.for_all (fun field -> Field.matches field actual.headers) expected.headers && List.for_all (fun field -> Field.matches field actual.trailers) expected.trailers