cljs / test / ird / spec_check_test.cljs
449 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
(ns ird.spec-check-test
  "Offline tests for the conformance validator itself. The live conformance
  sweep only proves the API agrees with the spec if this detector actually
  detects — a checker that always returns [] would pass that sweep silently
  forever, so each class of drift it is meant to catch is pinned here."
  (:require [cljs.test :refer [deftest is testing]]
            [ird.spec-check :as spec-check]))

(defn- check [schema value] (spec-check/check schema value ""))

(def ^:private pin-row
  {"type" "object"
   "additionalProperties" false
   "required" ["id" "cid" "state"]
   "properties" {"id" {"type" "string"}
                 "cid" {"oneOf" [{"type" "string"} {"type" "null"}]}
                 "state" {"type" "string" "enum" ["queued" "pinned" "failed"]}
                 "size_bytes" {"type" "integer"}}})

(deftest conforming-responses-produce-no-problems
  (is (= [] (check pin-row {:id "p1" :cid "bafy1" :state "pinned" :size_bytes 56})))
  (testing "an optional field may be absent, and a oneOf null branch is fine"
    (is (= [] (check pin-row {:id "p1" :cid nil :state "queued"}))))
  (testing "an unknown schema passes rather than guessing"
    (is (= [] (check nil {:anything true})))
    (is (= [] (check {} {:anything true})))))

(deftest detects-every-class-of-drift
  (testing "a required key the API did not return"
    (is (re-find #"\.state is required by the spec but absent"
                 (first (check pin-row {:id "p1" :cid "bafy1"})))))
  (testing "a field the API returns that the spec does not declare"
    (is (re-find #"\.cid_v1 is returned by the API but absent from the spec"
                 (first (check pin-row {:id "p1" :cid "b" :state "pinned" :cid_v1 "bafy2"})))))
  (testing "a value outside a declared enum"
    (is (re-find #"not in the declared enum"
                 (first (check pin-row {:id "p1" :cid "b" :state "deleting"})))))
  (testing "a declared type the API contradicts"
    (is (re-find #"declared integer but the API returned string"
                 (first (check pin-row {:id "p1" :cid "b" :state "pinned" :size_bytes "56"}))))
    (is (re-find #"declared integer but the API returned number"
                 (first (check pin-row {:id "p1" :cid "b" :state "pinned" :size_bytes 56.5})))))
  (testing "a oneOf none of whose branches match"
    (is (re-find #"matches none of oneOf"
                 (first (check pin-row {:id "p1" :cid 42 :state "pinned"}))))))

(deftest walks-nested-bodies-and-every-array-row
  (let [page {"type" "object"
              "properties" {"pins" {"type" "array" "items" pin-row}}}]
    (testing "problems are reported with the path that locates them"
      (is (= [".pins[1].state is required by the spec but absent from the response"]
             (check page {:pins [{:id "a" :cid "x" :state "pinned"}
                                 {:id "b" :cid "y"}]}))))
    (testing "a late row is checked, not just the first few"
      (let [rows (vec (concat (repeat 20 {:id "a" :cid "x" :state "pinned"})
                              [{:id "z" :cid "x" :state "not-a-state"}]))]
        (is (= 1 (count (check page {:pins rows}))))
        (is (re-find #"\.pins\[20\]\.state" (first (check page {:pins rows}))))))))

(deftest response-schema-lookup
  (let [spec {"paths" {"/x" {"get" {"responses" {"200" {"content" {"application/json" {"schema" {"type" "object"}}}}}}}}}]
    (is (= {"type" "object"} (spec-check/response-schema spec :get "/x" 200)))
    (is (nil? (spec-check/response-schema spec :get "/x" 404)))
    (is (nil? (spec-check/response-schema spec :post "/x" 200)))))

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/cljs.git