Skip to content

Instantly share code, notes, and snippets.

@nikolavojicic
Last active November 29, 2023 09:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nikolavojicic/8af3679f43d1d3f1bf5393a646be60bf to your computer and use it in GitHub Desktop.
Save nikolavojicic/8af3679f43d1d3f1bf5393a646be60bf to your computer and use it in GitHub Desktop.
Various Clojure specs + generators
;; String containing bulk space(s)
(s/def ::bulk-space-string
(s/with-gen
(s/and string? #(re-find #"\t|\n|\r| +" %))
#(gen/fmap
str/join
(gen/vector
(gen/one-of
[(gen/string)
(gen/return "\t")
(gen/return "\n")
(gen/return "\r")
(gen/return (str/join (repeat (rand-int 20) " ")))])))))
;; Email
(defn email?
[s]
(re-matches #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$" s))
(defn gen-email
[]
(gen/fmap
(fn [[name host tld]]
(str name "@" host "." tld))
(gen/tuple
(gen/not-empty (gen/string-alphanumeric))
(gen/not-empty (gen/string-alphanumeric))
(gen/fmap
#(apply str %)
(gen/vector (gen/char-alpha) 2 63)))))
(s/def ::email
(s/with-gen
(s/and string? util/email?)
gen-email))
;; java.time.LocalDateTime
(defn gen-local-date-time
([]
(gen-local-date-time
(Date. Integer/MAX_VALUE)
(Date.)))
([start end]
(gen/fmap
#(-> ^Date % .toInstant
(.atZone (ZoneId/systemDefault))
.toLocalDateTime)
(s/gen (s/inst-in start end)))))
(s/def ::local-date-time
(s/spec
#(instance? LocalDateTime %)
:gen gen-local-date-time))
;; java.time.LocalDate
(defn gen-local-date
([]
(gen-local-date
(Date. Integer/MAX_VALUE)
(Date.)))
([start end]
(gen/fmap
#(-> ^Date % .toInstant
(.atZone (ZoneId/systemDefault))
.toLocalDate)
(s/gen (s/inst-in start end)))))
(s/def ::local-date
(s/spec
#(instance? LocalDate %)
:gen gen-local-date))
;; java.time.LocalTime
(defn gen-local-time
[]
(gen/fmap
(fn [[hh mm ss]]
(LocalTime/of hh mm ss))
(gen/tuple (gen/choose 0 23)
(gen/choose 0 59)
(gen/choose 0 59))))
(s/def ::local-time
(s/spec
#(instance? LocalTime %)
:gen gen-local-time))
(defn numeric?
[s]
(boolean
(try (Double/parseDouble s)
(catch Throwable __))))
(defn gen-numeric-string
([]
(let [min (gen/generate (gen/choose 0 10))
max (gen/generate (gen/choose min 100))]
(gen-numeric-string min max)))
([size]
(gen-numeric-string size size))
([min max]
(gen/fmap
str/join
(gen/vector (gen/choose 0 9) min max))))
(s/def ::numeric-string
(s/with-gen
(s/and string? seq numeric?)
gen-numeric-string))
(s/def ::percent-int
(s/and nat-int?
#(>= % 0)
#(<= % 10000)))
;; BigDecimal percent, > 0M <= 100M, scale 2
(defn gen-percent
[]
(gen/fmap
#(-> (BigDecimal/valueOf ^double %)
(.setScale 2 BigDecimal/ROUND_HALF_EVEN))
(s/gen (s/double-in :min 0.01 :max 100.00))))
(s/def ::percent
(s/with-gen
(s/and decimal?
#(= (.scale ^BigDecimal %) 2)
#(pos? (compare % 0.00M))
#(neg? (compare % 100.01M)))
gen-percent))
(s/def ::pos-int
(s/and nat-int? pos?
#(<= % Integer/MAX_VALUE)))
(s/def ::pos-long
(s/and nat-int? pos?))
(defn trimmed?
[s]
(= (str/trim s) s))
(s/def ::trimmed-not-empty-string
(s/and string? seq trimmed?))
@danodic
Copy link

danodic commented Nov 13, 2022

Thanks for this one, it helped me understand how to use the generators properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment