/
test.cljc
128 lines (111 loc) · 3.77 KB
/
test.cljc
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
(ns com.wsscode.pathom.test
(:require
[clojure.spec.alpha :as s]
[clojure.string :as str]
[com.fulcrologic.guardrails.core :refer [>def >defn >fdef => | <- ?]]
[#?(:clj com.wsscode.async.async-clj
:cljs com.wsscode.async.async-cljs) :refer [go-catch]]
[com.wsscode.pathom.core :as p]
[com.wsscode.pathom.misc :as p.misc]
[edn-query-language.core :as eql]))
(>def ::throw-errors? boolean?)
(>def ::include-nils? boolean?)
(>def ::depth-limit int?)
(defn hash-mod?
"Check if the mod of the hash of x is zero. This is useful to call against some random value.
It will have a chance of 1/n to be true."
[x n]
(-> x hash (mod n) zero?))
(defn key-ex-value
"Generate a random value for a key, uses hash-mod to pick what type of value will be returned."
[key {::keys [throw-errors? include-nils?]
:or {include-nils? true}
:as env}]
(cond
(hash-mod? key 11)
false
(and include-nils? (hash-mod? key 9))
nil
(hash-mod? key 8)
true
(hash-mod? key 7)
(p/cached env key (str key))
(and throw-errors? (hash-mod? key 5))
(throw (ex-info "Demo error" {:x key}))
(hash-mod? key 3)
(hash key)
:else
(str key)))
(defn union-test-path
"Picks a union path based on the hash-mod of key."
[env]
(if-let [entries (-> env :ast :children first :children seq)]
(-> entries (nth (-> env :ast :key hash (mod (count entries)))) :union-key)))
(defn reader
"Reader suited for random testing."
[{:keys [ast query]
::keys [depth-limit]
:or {depth-limit 5}
:as env}]
(if query
(if (> depth-limit 0)
(if (-> ast :key (hash-mod? 5))
(p/join-seq (assoc env ::depth-limit (- depth-limit 3)) (-> ast :key hash (mod 4) (repeat {}) vec))
(p/join (assoc env ::depth-limit (dec depth-limit))))
(if (-> ast :key (hash-mod? 5))
[]
{}))
(-> ast :key (key-ex-value env))))
(defn async-reader
"Like reader, but have a chance to return channels."
[{:keys [ast query]
::keys [depth-limit]
:or {depth-limit 5}
:as env}]
(if query
(if (> depth-limit 0)
(if (-> ast :key (hash-mod? 5))
(p/join-seq (assoc env ::depth-limit (- depth-limit 3)) (-> ast :key hash (mod 4) (repeat {}) vec))
(p/join (assoc env ::depth-limit (dec depth-limit))))
(if (-> ast :key (hash-mod? 5))
[]
{}))
(if (-> ast :key (hash-mod? 2))
(go-catch (-> ast :key (key-ex-value env)))
(-> ast :key (key-ex-value env)))))
(defn mutate-fn
"Mutation function suited for random testing."
[{::keys [throw-errors?]} k _]
{:action
(fn []
(if (and throw-errors? (hash-mod? k 5))
(throw (ex-info "Demo error" {:x k}))
(str k)))})
#?(:clj
(defn sleep-reader
"This reader looks for ident queries starting with :sleep., and sleep for the ident value amount of time."
[{:keys [ast query] :as env}]
(if-let [key (p/ident-key env)]
(if (some-> key name (str/starts-with? "sleep."))
(do
(Thread/sleep (p/ident-value env))
(if query
(p/join env)
(name (first (:key ast)))))
::p/continue)
::p/continue)))
(defn repeat-reader
"This reader looks for ident queries starting with :repeat., and repeat for the ident value amount of times."
[env]
(if-let [key (p/ident-key env)]
(if (some-> key name (str/starts-with? "repeat."))
(p/join-seq env (map (constantly {}) (range (p/ident-value env))))
::p/continue)
::p/continue))
(when p.misc/INCLUDE_SPECS
(s/fdef hash-mod?
:args (s/cat :x any? :n pos-int?)
:ret nat-int?)
(s/fdef key-ex-value
:args (s/cat :key ::eql/join-key :env (s/keys :opt [::throw-errors?
::include-nils?]))))