<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" href="../../coverage.css"/> <title> dog_and_duck/quack/picky.clj </title> </head> <body> <span class="covered" title="1 out of 1 forms covered"> 001 (ns dog-and-duck.quack.picky "Fault-finder for ActivityPub documents. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 002 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 003 Generally, each `-faults` function will return: </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 004 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 005 1. `nil` if no faults were found; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 006 2. a sequence of fault objects if faults were found. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 007 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 008 Each fault object shall have the properties: </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 009 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 010 1. `:@context` whose value shall be the URL of a </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 011 document specifying this vocabulary; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 012 2. `:type` whose value shall be `Fault`; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 013 3. `:severity` whose value shall be one of </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 014 `info`, `minor`, `should`, `must` or `critical`; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 015 4. `:fault` whose value shall be a unique token </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 016 representing the particular fault type; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 017 5. `:narrative` whose value shall be a natural </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 018 language description of the fault type. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 019 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 020 Note that the reason for the `:fault` property is </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 021 to be able to have a well known place, linked to </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 022 from the @context URL, which allows narratives </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 023 for each fault type to be served in as many </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 024 natural languages as possible. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 025 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 026 The idea further is that it should ultimately be </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 027 possible to serialise a fault report as a </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 028 document which in its own right conforms to the </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 029 ActivityStreams spec." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 030 (:require [dog-and-duck.quack.picky.collections :refer [collection-page-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 031 paged-collection-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 032 simple-collection-faults]] </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 033 [dog-and-duck.quack.picky.constants :refer [actor-types]] </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 034 [dog-and-duck.quack.picky.utils :refer [any-or-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 035 coll-object-reference-or-fault </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 036 concat-non-empty </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 037 has-activity-type? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 038 has-actor-type? has-type? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 039 has-type-or-fault </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 040 make-fault-object </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 041 object-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 042 object-reference-or-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 043 string-or-fault]]) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 044 (:import [java.net URI URISyntaxException])) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 045 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 046 ;;; Copyright (C) Simon Brooke, 2022 </span><br/> <span class="blank" title="0 out of 0 forms covered"> 047 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 048 ;;; This program is free software; you can redistribute it and/or </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 049 ;;; modify it under the terms of the GNU General Public License </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 050 ;;; as published by the Free Software Foundation; either version 2 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 051 ;;; of the License, or (at your option) any later version. </span><br/> <span class="blank" title="0 out of 0 forms covered"> 052 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 053 ;;; This program is distributed in the hope that it will be useful, </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 054 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 055 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 056 ;;; GNU General Public License for more details. </span><br/> <span class="blank" title="0 out of 0 forms covered"> 057 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 058 ;;; You should have received a copy of the GNU General Public License </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 059 ;;; along with this program; if not, write to the Free Software </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 060 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. </span><br/> <span class="blank" title="0 out of 0 forms covered"> 061 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 062 (defn uri-or-fault </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 063 "If `u` is not a valid URI, return a fault object with this `severity` and </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 064 `if-invalid-token`. If it's `nil`, return a fault object with this `severity` </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 065 and `if-missing-token`. Otherwise return nil." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 066 ([u severity if-missing-token] </span><br/> <span class="not-covered" title="0 out of 6 forms covered"> 067 (uri-or-fault u severity if-missing-token if-missing-token)) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 068 ([u severity if-missing-token if-invalid-token] </span><br/> <span class="covered" title="1 out of 1 forms covered"> 069 (try </span><br/> <span class="covered" title="6 out of 6 forms covered"> 070 (if (uri? (URI. u)) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 071 nil </span><br/> <span class="not-covered" title="0 out of 4 forms covered"> 072 (make-fault-object severity if-invalid-token)) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 073 (catch URISyntaxException _ </span><br/> <span class="not-covered" title="0 out of 4 forms covered"> 074 (make-fault-object severity if-invalid-token)) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 075 (catch NullPointerException _ </span><br/> <span class="covered" title="4 out of 4 forms covered"> 076 (make-fault-object severity if-missing-token))))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 077 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 078 (defn persistent-object-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 079 "Return a list of faults found in persistent object `x`, or `nil` if none are." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 080 ([x] </span><br/> <span class="covered" title="2 out of 2 forms covered"> 081 (concat-non-empty </span><br/> <span class="covered" title="3 out of 3 forms covered"> 082 (object-faults x) </span><br/> <span class="covered" title="2 out of 2 forms covered"> 083 (list </span><br/> <span class="covered" title="5 out of 5 forms covered"> 084 (if (contains? x :id) </span><br/> <span class="covered" title="6 out of 6 forms covered"> 085 (try (let [id (URI. (:id x))] </span><br/> <span class="covered" title="6 out of 6 forms covered"> 086 (when-not (= (.getScheme id) "https") </span><br/> <span class="covered" title="4 out of 4 forms covered"> 087 (make-fault-object :should :id-not-https))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 088 (catch URISyntaxException _ </span><br/> <span class="covered" title="4 out of 4 forms covered"> 089 (make-fault-object :must :id-not-uri)) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 090 (catch NullPointerException _ </span><br/> <span class="not-covered" title="0 out of 4 forms covered"> 091 (make-fault-object :must :null-id-persistent))) </span><br/> <span class="covered" title="4 out of 4 forms covered"> 092 (make-fault-object :must :no-id-persistent))))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 093 ([x types severity token] </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 094 (concat-non-empty </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 095 (persistent-object-faults x) </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 096 (list </span><br/> <span class="not-covered" title="0 out of 6 forms covered"> 097 (has-type-or-fault x types severity token))))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 098 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 099 (defn actor-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 100 "Return a list of faults found in actor `x`, or `nil` if none are." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 101 [x] </span><br/> <span class="covered" title="2 out of 2 forms covered"> 102 (concat-non-empty </span><br/> <span class="covered" title="3 out of 3 forms covered"> 103 (persistent-object-faults x) </span><br/> <span class="covered" title="2 out of 2 forms covered"> 104 (list </span><br/> <span class="covered" title="6 out of 6 forms covered"> 105 (when-not (has-actor-type? x) </span><br/> <span class="covered" title="4 out of 4 forms covered"> 106 (make-fault-object :must :not-actor-type)) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 107 (uri-or-fault </span><br/> <span class="covered" title="3 out of 3 forms covered"> 108 (:inbox x) :must :no-inbox :invalid-inbox-uri) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 109 (uri-or-fault </span><br/> <span class="covered" title="3 out of 3 forms covered"> 110 (:outbox x) :must :no-outbox :invalid-outbox-uri)))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 111 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 112 (defn link-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 113 "A link object is required to have an `href` property. It may have all of </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 114 `rel` | `mediaType` | `name` | `hreflang` | `height` | `width` | `preview` </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 115 but I *think* they're all optional." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 116 [x] </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 117 (concat-non-empty </span><br/> <span class="not-covered" title="0 out of 6 forms covered"> 118 (object-reference-or-faults x "Link" :critical :expected-link) </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 119 (list </span><br/> <span class="not-covered" title="0 out of 5 forms covered"> 120 (uri-or-fault </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 121 (:href x) :must :no-href-uri :invalid-href-uri) </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 122 (string-or-fault (:mediaType x) :minor :no-media-type #"\w+\/[-+.\w]+") </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 123 ;; TODO: possibly more here. Audit against the specs </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 124 ))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 125 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 126 (def ^:const base-activity-required-properties </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 127 "Properties most activities should have. Values are validating functions, each. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 128 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 129 See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-activity" </span><br/> <span class="partial" title="5 out of 11 forms covered"> 130 {:summary (fn [v] (when-not (string? v) </span><br/> <span class="not-covered" title="0 out of 6 forms covered"> 131 (list (make-fault-object :should :no-summary)))) </span><br/> <span class="partial" title="1 out of 7 forms covered"> 132 :actor (fn [v] (object-reference-or-faults v actor-types :must :no-actor)) </span><br/> <span class="partial" title="1 out of 7 forms covered"> 133 :object (fn [v] (object-reference-or-faults v nil :must :no-object))}) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 134 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 135 (def ^:const intransitive-activity-required-properties </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 136 "Properties intransitive activities should have. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 137 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 138 See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-intransitiveactivity" </span><br/> <span class="covered" title="4 out of 4 forms covered"> 139 (dissoc base-activity-required-properties :object)) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 140 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 141 (def ^:const accept-required-properties </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 142 "As base-activity-required-properties, except that the type of the object </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 143 is restricted." </span><br/> <span class="covered" title="4 out of 4 forms covered"> 144 (assoc base-activity-required-properties </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 145 :object </span><br/> <span class="covered" title="1 out of 1 forms covered"> 146 (fn [v] </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 147 (object-reference-or-faults v #{"Invite" "Person"} </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 148 :must </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 149 :bad-accept-target)))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 150 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 151 (def ^:const activity-required-properties </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 152 "Properties activities should have, keyed by activity type. Values are maps </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 153 of the format of `base-activity-required-properties`, q.v." </span><br/> <span class="covered" title="30 out of 30 forms covered"> 154 {"Accept" accept-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 155 "Add" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 156 "Announce" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 157 "Arrive" intransitive-activity-required-properties </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 158 ;; TODO: is `:location` required for arrive? </span><br/> <span class="covered" title="1 out of 1 forms covered"> 159 "Block" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 160 "Create" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 161 "Delete" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 162 "Dislike" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 163 "Flag" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 164 "Follow" base-activity-required-properties </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 165 ;; TODO: is `:object` required to be an actor? </span><br/> <span class="covered" title="1 out of 1 forms covered"> 166 "Ignore" base-activity-required-properties </span><br/> <span class="covered" title="4 out of 4 forms covered"> 167 "Invite" (assoc base-activity-required-properties :target </span><br/> <span class="covered" title="1 out of 1 forms covered"> 168 (fn [v] </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 169 (coll-object-reference-or-fault v #{"Event" "Group"} </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 170 :must </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 171 :bad-accept-target))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 172 ;; TODO: are here other things one could meaningfully be invited to? </span><br/> <span class="covered" title="1 out of 1 forms covered"> 173 "Join" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 174 "Leave" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 175 "Like" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 176 "Listen" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 177 "Move" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 178 "Offer" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 179 "Question" intransitive-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 180 "Reject" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 181 "Read" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 182 "Remove" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 183 "TentativeReject" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 184 "TentativeAccept" accept-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 185 "Travel" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 186 "Undo" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 187 "Update" base-activity-required-properties </span><br/> <span class="covered" title="1 out of 1 forms covered"> 188 "View" base-activity-required-properties}) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 189 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 190 (defn activity-type-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 191 "Return a list of faults found in the activity `x`; if `type` is also </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 192 specified, it should be a string naming a specific activity type for </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 193 which checks should be performed. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 194 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 195 Some specific activity types have specific requirements which are not </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 196 requirements." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 197 ([x] </span><br/> <span class="not-covered" title="0 out of 6 forms covered"> 198 (if (coll? (:type x)) </span><br/> <span class="not-covered" title="0 out of 10 forms covered"> 199 (map #(activity-type-faults x %) (:type x)) </span><br/> <span class="not-covered" title="0 out of 6 forms covered"> 200 (activity-type-faults x (:type x)))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 201 ([x type] </span><br/> <span class="not-covered" title="0 out of 4 forms covered"> 202 (let [checks (activity-required-properties type)] </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 203 (map </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 204 #(apply (checks %) (x %)) </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 205 (keys checks))))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 206 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 207 (defn activity-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 208 [x] </span><br/> <span class="not-covered" title="0 out of 5 forms covered"> 209 (concat-non-empty (persistent-object-faults x) </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 210 (activity-type-faults x) </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 211 (list </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 212 (when-not </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 213 (has-activity-type? x) </span><br/> <span class="not-covered" title="0 out of 4 forms covered"> 214 (make-fault-object :must :not-activity-type)) </span><br/> <span class="not-covered" title="0 out of 12 forms covered"> 215 (when-not (string? (:summary x)) (make-fault-object :should :no-summary))))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 216 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 217 (defn collection-faults </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 218 "Return a list of faults found in the collection `x`; if `type` is also </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 219 specified, it should be a string naming a specific collection type for </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 220 which checks should be performed. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 221 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 222 Every collection *should*(?) have a `totalItems` field (an integer). </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 223 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 224 Beyond that, collections are either 'just collections' (in which case </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 225 they *should* have an `items` field (a sequence)), or else they're paged </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 226 collections, in which case they *must*(?) have a `first` field which is </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 227 a collection page or a URI pointing to a collection page, and *should* </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 228 have a `last` field which is similar. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 229 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 230 The pages of collections *should* be collection pages; the pages of </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 231 ordered collections *should* be ordered collection pages." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 232 ([x] </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 233 (collection-faults </span><br/> <span class="not-covered" title="0 out of 1 forms covered"> 234 x </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 235 (first </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 236 (remove nil? </span><br/> <span class="not-covered" title="0 out of 10 forms covered"> 237 (map #(when (has-type? x %) %) </span><br/> <span class="not-covered" title="0 out of 5 forms covered"> 238 ["Collection" </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 239 "OrderedCollection" </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 240 "CollectionPage" </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 241 "OrderedCollectionPage"]))))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 242 ([x type] </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 243 ;; (log/info "collection-faults called with argumens " x ", " type) </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 244 (case type </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 245 ("Collection" "OrderedCollection") (any-or-faults </span><br/> <span class="not-covered" title="0 out of 12 forms covered"> 246 (list (simple-collection-faults x type) </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 247 (paged-collection-faults x type)) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 248 :must </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 249 :no-items) </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 250 ("CollectionPage" "OrderedCollectionPage") (collection-page-faults x type) </span><br/> <span class="not-covered" title="0 out of 6 forms covered"> 251 (list (make-fault-object :critical :expected-collection))))) </span><br/> </body> </html>