* add cheshire.factory namespace This will allow bb users to change jackson factory options when using cheshire. Took inspiration from other bb lib impls that dealt with dynamic vars. Brought over cheshire tests, disabling tests for cheshire/jvm features not included in bb. Closes #1806 * turf debug stuff * turf commented code accidentally left in
793 lines
34 KiB
Clojure
793 lines
34 KiB
Clojure
(ns cheshire.test.core
|
|
(:require [clojure.test :refer [deftest testing is are]]
|
|
[clojure.java.io :as io]
|
|
[clojure.string :as str]
|
|
[cheshire.core :as json]
|
|
;; BB-TEST-PATCH: bb does not include cheshire.exact
|
|
#_[cheshire.exact :as json-exact]
|
|
;; BB-TEST-PATCH: bb does not include cheshire.gen
|
|
#_[cheshire.generate :as gen]
|
|
[cheshire.factory :as fact]
|
|
;; BB-TEST-PATCH: bb does not include cheshire.parse
|
|
#_[cheshire.parse :as parse])
|
|
(:import ;; BB-TEST-PATCH: tests adjusted to check for general Exception instead of specific jackson exceptions
|
|
#_(com.fasterxml.jackson.core JsonGenerationException
|
|
JsonParseException)
|
|
#_(com.fasterxml.jackson.core.exc StreamConstraintsException)
|
|
(java.io StringReader StringWriter
|
|
BufferedReader BufferedWriter
|
|
IOException)
|
|
;; BB-TEST-PATCH: bb does not support creating java.sql.Timestamps
|
|
#_(java.sql Timestamp)
|
|
(java.util Date UUID)))
|
|
|
|
(defn- str-of-len
|
|
([len]
|
|
(str-of-len len "x"))
|
|
([len val]
|
|
(apply str (repeat len val))))
|
|
|
|
(defn- nested-map [depth]
|
|
(reduce (fn [acc n] {(str n) acc})
|
|
{"0" "foo"}
|
|
(range 1 depth)))
|
|
|
|
(defn- encode-stream->str [obj opts]
|
|
(let [sw (StringWriter.)
|
|
bw (BufferedWriter. sw)]
|
|
(json/generate-stream obj bw opts)
|
|
(.toString sw)))
|
|
|
|
(def test-obj {"int" 3 "long" (long -2147483647) "boolean" true
|
|
"LongObj" (Long/parseLong "2147483647") "double" 1.23
|
|
"nil" nil "string" "string" "vec" [1 2 3] "map" {"a" "b"}
|
|
"list" (list "a" "b") "short" (short 21) "byte" (byte 3)})
|
|
|
|
(deftest t-ratio
|
|
(let [n 1/2]
|
|
(is (= (double n) (:num (json/decode (json/encode {:num n}) true))))))
|
|
|
|
(deftest t-long-wrap-around
|
|
(is (= 2147483648 (json/decode (json/encode 2147483648)))))
|
|
|
|
(deftest t-bigint
|
|
(let [n 9223372036854775808]
|
|
(is (= n (:num (json/decode (json/encode {:num n}) true))))))
|
|
|
|
(deftest t-biginteger
|
|
(let [n (BigInteger. "42")]
|
|
(is (= n (:num (json/decode (json/encode {:num n}) true))))))
|
|
|
|
(deftest t-bigdecimal
|
|
(let [n (BigDecimal. "42.5")]
|
|
(is (= (.doubleValue n) (:num (json/decode (json/encode {:num n}) true))))
|
|
;; BB-TEST-PATCH:
|
|
#_(binding [parse/*use-bigdecimals?* true]
|
|
(is (= n (:num (json/decode (json/encode {:num n}) true)))))))
|
|
|
|
(deftest test-string-round-trip
|
|
(is (= test-obj (json/decode (json/encode test-obj)))))
|
|
|
|
(deftest test-generate-accepts-float
|
|
(is (= "3.14" (json/encode 3.14))))
|
|
|
|
(deftest test-keyword-encode
|
|
(is (= {"key" "val"}
|
|
(json/decode (json/encode {:key "val"})))))
|
|
|
|
(deftest test-generate-set
|
|
(is (= {"set" ["a" "b"]}
|
|
(json/decode (json/encode {"set" #{"a" "b"}})))))
|
|
|
|
(deftest test-generate-empty-set
|
|
(is (= {"set" []}
|
|
(json/decode (json/encode {"set" #{}})))))
|
|
|
|
(deftest test-generate-empty-array
|
|
(is (= {"array" []}
|
|
(json/decode (json/encode {"array" []})))))
|
|
|
|
(deftest test-key-coercion
|
|
(is (= {"foo" "bar" "1" "bat" "2" "bang" "3" "biz"}
|
|
(json/decode
|
|
(json/encode
|
|
{:foo "bar" 1 "bat" (long 2) "bang" (bigint 3) "biz"})))))
|
|
|
|
(deftest test-keywords
|
|
(is (= {:foo "bar" :bat 1}
|
|
(json/decode (json/encode {:foo "bar" :bat 1}) true))))
|
|
|
|
(deftest test-symbols
|
|
(is (= {"foo" "clojure.core/map"}
|
|
(json/decode (json/encode {"foo" 'clojure.core/map})))))
|
|
|
|
(deftest test-accepts-java-map
|
|
(is (= {"foo" 1}
|
|
(json/decode
|
|
(json/encode (doto (java.util.HashMap.) (.put "foo" 1)))))))
|
|
|
|
(deftest test-accepts-java-list
|
|
(is (= [1 2 3]
|
|
(json/decode (json/encode (doto (java.util.ArrayList. 3)
|
|
(.add 1)
|
|
(.add 2)
|
|
(.add 3)))))))
|
|
|
|
(deftest test-accepts-java-set
|
|
(is (= {"set" [1 2 3]}
|
|
(json/decode (json/encode {"set" (doto (java.util.HashSet. 3)
|
|
(.add 1)
|
|
(.add 2)
|
|
(.add 3))})))))
|
|
|
|
(deftest test-accepts-empty-java-set
|
|
(is (= {"set" []}
|
|
(json/decode (json/encode {"set" (java.util.HashSet. 3)})))))
|
|
|
|
(deftest test-nil
|
|
(is (nil? (json/decode nil true))))
|
|
|
|
(deftest test-parsed-seq
|
|
(let [br (BufferedReader. (StringReader. "1\n2\n3\n"))]
|
|
(is (= (list 1 2 3) (json/parsed-seq br)))))
|
|
|
|
;; BB-TEST-PATCH: bb does not support smile
|
|
#_(deftest test-smile-round-trip
|
|
(is (= test-obj (json/parse-smile (json/generate-smile test-obj)))))
|
|
|
|
(def bin-obj {"byte-array" (byte-array (map byte [1 2 3]))})
|
|
|
|
;; BB-TEST-PATCH: bb does not support smile/cbor
|
|
#_(deftest test-round-trip-binary
|
|
(doseq [[p g] {json/parse-string json/generate-string
|
|
json/parse-smile json/generate-smile
|
|
json/parse-cbor json/generate-cbor}]
|
|
(is (let [roundtripped (p (g bin-obj))]
|
|
;; test value equality
|
|
(is (= (->> bin-obj (get "byte-array") seq)
|
|
(->> roundtripped (get "byte-array") seq)))))))
|
|
|
|
;; BB-TEST-PATCH: bb does not support smile
|
|
#_(deftest test-smile-factory
|
|
(binding [fact/*smile-factory* (fact/make-smile-factory {})]
|
|
(is (= {"a" 1} (-> {:a 1}
|
|
json/generate-smile
|
|
json/parse-smile)))))
|
|
|
|
;; BB-TEST-PATCH: bb does not support smile/cbor
|
|
#_(deftest test-smile-duplicate-detection
|
|
(let [smile-data (byte-array [0x3a 0x29 0x0a 0x01 ;; smile header
|
|
0xFa ;; object start
|
|
0x80 0x61 ;; key a
|
|
0xC2 ;; value 1
|
|
0x80 0x61 ;; key a (again)
|
|
0xC4 ;; value 2
|
|
0xFB ;; object end
|
|
])]
|
|
(binding [fact/*smile-factory* (fact/make-smile-factory {:strict-duplicate-detection false})]
|
|
(is (= {"a" 2} (json/parse-smile smile-data))))
|
|
(binding [fact/*smile-factory* (fact/make-smile-factory {:strict-duplicate-detection true})]
|
|
(is (thrown? JsonParseException (json/parse-smile smile-data))))))
|
|
|
|
;; BB-TEST-PATCH: bb does not support cbor
|
|
#_(deftest test-cbor-factory
|
|
(binding [fact/*cbor-factory* (fact/make-cbor-factory {})]
|
|
(is (= {"a" 1} (-> {:a 1}
|
|
json/generate-cbor
|
|
json/parse-cbor)))))
|
|
|
|
;; BB-TEST-PATCH: bb does not support cbor
|
|
#_(deftest test-cbor-duplicate-detection
|
|
(let [cbor-data (byte-array [0xbf ;; object begin
|
|
0x61 0x61 ;; key a
|
|
0x01 ;; value 1
|
|
0x61 0x61 ;; key a (again)
|
|
0x02 ;; value 2
|
|
0xff ;; object end
|
|
])]
|
|
(binding [fact/*cbor-factory* (fact/make-cbor-factory {:strict-duplicate-detection false})]
|
|
(is (= {"a" 2} (json/parse-cbor cbor-data))))
|
|
(binding [fact/*cbor-factory* (fact/make-cbor-factory {:strict-duplicate-detection true})]
|
|
(is (thrown? JsonParseException (json/parse-cbor cbor-data))))))
|
|
|
|
(deftest test-aliases
|
|
(is (= {"foo" "bar" "1" "bat" "2" "bang" "3" "biz"}
|
|
(json/decode
|
|
(json/encode
|
|
{:foo "bar" 1 "bat" (long 2) "bang" (bigint 3) "biz"})))))
|
|
|
|
(deftest test-date
|
|
(is (= {"foo" "1970-01-01T00:00:00Z"}
|
|
(json/decode (json/encode {:foo (Date. (long 0))}))))
|
|
(is (= {"foo" "1970-01-01"}
|
|
(json/decode (json/encode {:foo (Date. (long 0))}
|
|
{:date-format "yyyy-MM-dd"})))
|
|
"encode with given date format"))
|
|
|
|
;; BB-TEST-PATCH: bb does not support creating java.sql.Timestamps
|
|
#_(deftest test-sql-timestamp
|
|
(is (= {"foo" "1970-01-01T00:00:00Z"}
|
|
(json/decode (json/encode {:foo (Timestamp. (long 0))}))))
|
|
(is (= {"foo" "1970-01-01"}
|
|
(json/decode (json/encode {:foo (Timestamp. (long 0))}
|
|
{:date-format "yyyy-MM-dd"})))
|
|
"encode with given date format"))
|
|
|
|
(deftest test-uuid
|
|
(let [id (UUID/randomUUID)
|
|
id-str (str id)]
|
|
(is (= {"foo" id-str} (json/decode (json/encode {:foo id}))))))
|
|
|
|
(deftest test-char-literal
|
|
(is (= "{\"foo\":\"a\"}" (json/encode {:foo \a}))))
|
|
|
|
(deftest test-streams
|
|
(testing "parse-stream"
|
|
(are [parsed parse parsee] (= parsed
|
|
(parse (BufferedReader. (StringReader. parsee))))
|
|
{"foo" "bar"} json/parse-stream "{\"foo\":\"bar\"}\n"
|
|
{"foo" "bar"} json/parse-stream-strict "{\"foo\":\"bar\"}\n")
|
|
|
|
(are [parsed parse parsee] (= parsed
|
|
(with-open [rdr (StringReader. parsee)]
|
|
(parse rdr true)))
|
|
{(keyword "foo baz") "bar"} json/parse-stream "{\"foo baz\":\"bar\"}\n"
|
|
{(keyword "foo baz") "bar"} json/parse-stream-strict "{\"foo baz\":\"bar\"}\n"))
|
|
|
|
(testing "generate-stream"
|
|
(let [sw (StringWriter.)
|
|
bw (BufferedWriter. sw)]
|
|
(json/generate-stream {"foo" "bar"} bw)
|
|
(is (= "{\"foo\":\"bar\"}" (.toString sw))))))
|
|
|
|
;; BB-TEST-PATCH: bb does not include with-writer
|
|
#_(deftest serial-writing
|
|
(is (= "[\"foo\",\"bar\"]"
|
|
(.toString
|
|
(json/with-writer [(StringWriter.) nil]
|
|
(json/write [] :start)
|
|
(json/write "foo")
|
|
(json/write "bar")
|
|
(json/write [] :end)))))
|
|
(is (= "[1,[2,3],4]"
|
|
(.toString
|
|
(json/with-writer [(StringWriter.) nil]
|
|
(json/write [1 [2]] :start-inner)
|
|
(json/write 3)
|
|
(json/write [] :end)
|
|
(json/write 4)
|
|
(json/write [] :end)))))
|
|
(is (= "{\"a\":1,\"b\":2,\"c\":3}"
|
|
(.toString
|
|
(json/with-writer [(StringWriter.) nil]
|
|
(json/write {:a 1} :start)
|
|
(json/write {:b 2} :bare)
|
|
(json/write {:c 3} :end)))))
|
|
(is (= (str "[\"start\",\"continue\",[\"implicitly-nested\"],"
|
|
"[\"explicitly-nested\"],\"flatten\",\"end\"]")
|
|
(.toString
|
|
(json/with-writer [(StringWriter.) nil]
|
|
(json/write ["start"] :start)
|
|
(json/write "continue")
|
|
(json/write ["implicitly-nested"])
|
|
(json/write ["explicitly-nested"] :all)
|
|
(json/write ["flatten"] :bare)
|
|
(json/write ["end"] :end)))))
|
|
(is (= "{\"head\":\"head info\",\"data\":[1,2,3],\"tail\":\"tail info\"}"
|
|
(.toString
|
|
(json/with-writer [(StringWriter.) nil]
|
|
(json/write {:head "head info" :data []} :start-inner)
|
|
(json/write 1)
|
|
(json/write 2)
|
|
(json/write 3)
|
|
(json/write [] :end)
|
|
(json/write {:tail "tail info"} :end))))))
|
|
|
|
;; BB-TEST-PATCH: modified so that json files could be found
|
|
(deftest test-multiple-objs-in-file
|
|
(is (= {"one" 1, "foo" "bar"}
|
|
(first (json/parsed-seq (io/reader (io/resource "cheshire/test/multi.json"))))))
|
|
(is (= {"two" 2, "foo" "bar"}
|
|
(second (json/parsed-seq (io/reader (io/resource "cheshire/test/multi.json"))))))
|
|
(with-open [r (io/reader (io/resource "cheshire/test/multi.json"))]
|
|
(is (= [{"one" 1, "foo" "bar"} {"two" 2, "foo" "bar"}]
|
|
(json/parsed-seq r)))))
|
|
|
|
(deftest test-jsondotorg-pass1
|
|
(let [;; BB-TEST-PATCH: modified so that json files could be found
|
|
string (slurp (io/resource "cheshire/test/pass1.json"))
|
|
decoded-json (json/decode string)
|
|
encoded-json (json/encode decoded-json)
|
|
re-decoded-json (json/decode encoded-json)]
|
|
(is (= decoded-json re-decoded-json))))
|
|
|
|
(deftest test-namespaced-keywords
|
|
(is (= "{\"foo\":\"user/bar\"}"
|
|
(json/encode {:foo :user/bar})))
|
|
(is (= {:foo/bar "baz/eggplant"}
|
|
(json/decode (json/encode {:foo/bar :baz/eggplant}) true))))
|
|
|
|
(deftest test-array-coerce-fn
|
|
(is (= {"set" #{"a" "b"} "array" ["a" "b"] "map" {"a" 1}}
|
|
(json/decode
|
|
(json/encode {"set" #{"a" "b"} "array" ["a" "b"] "map" {"a" 1}}) false
|
|
(fn [field-name] (if (= "set" field-name) #{} []))))))
|
|
|
|
(deftest t-symbol-encoding-for-non-resolvable-symbols
|
|
(is (= "{\"bar\":\"clojure.core/pam\",\"foo\":\"clojure.core/map\"}"
|
|
(json/encode (sorted-map :foo 'clojure.core/map :bar 'clojure.core/pam))))
|
|
(is (= "{\"bar\":\"clojure.core/pam\",\"foo\":\"foo.bar/baz\"}"
|
|
(json/encode (sorted-map :foo 'foo.bar/baz :bar 'clojure.core/pam)))))
|
|
|
|
(deftest t-bindable-factories-auto-close-source
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:auto-close-source false})]
|
|
(let [br (BufferedReader. (StringReader. "123"))]
|
|
(is (= 123 (json/parse-stream br)))
|
|
(is (= -1 (.read br)))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:auto-close-source true})]
|
|
(let [br (BufferedReader. (StringReader. "123"))]
|
|
(is (= 123 (json/parse-stream br)))
|
|
(is (thrown? IOException (.read br))))))
|
|
|
|
(deftest t-bindable-factories-allow-comments
|
|
(let [s "{\"a\": /* comment */ 1, // comment\n \"b\": 2}"]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-comments true})]
|
|
(is (= {"a" 1 "b" 2} (json/decode s))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-comments false})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_JsonParseException Exception (json/decode s))))))
|
|
|
|
(deftest t-bindable-factories-allow-unquoted-field-names
|
|
(let [s "{a: 1, b: 2}"]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-unquoted-field-names true})]
|
|
(is (= {"a" 1 "b" 2} (json/decode s))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-unquoted-field-names false})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_JsonParseException Exception (json/decode s))))))
|
|
|
|
(deftest t-bindable-factories-allow-single-quotes
|
|
(doseq [s ["{'a': \"one\", 'b': \"two\"}"
|
|
"{\"a\": 'one', \"b\": 'two'}"
|
|
"{'a': 'one', 'b': 'two'}"]]
|
|
(testing s
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-single-quotes true})]
|
|
(is (= {"a" "one" "b" "two"} (json/decode s))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-single-quotes false})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_JsonParseException Exception (json/decode s)))))))
|
|
|
|
(deftest t-bindable-factories-allow-unquoted-control-chars
|
|
(let [s "{\"a\": \"one\ntwo\"}"]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-unquoted-control-chars true})]
|
|
(is (= {"a" "one\ntwo"} (json/decode s))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-unquoted-control-chars false})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_JsonParseException Exception (json/decode s))))))
|
|
|
|
(deftest t-bindable-factories-allow-backslash-escaping-any-char
|
|
(let [s "{\"a\": 00000000001}"]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-numeric-leading-zeros true})]
|
|
(is (= {"a" 1} (json/decode s))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-numeric-leading-zeros false})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_JsonParseException Exception (json/decode s))))))
|
|
|
|
(deftest t-bindable-factories-allow-numeric-leading-zeros
|
|
(let [s "{\"a\": \"\\o\\n\\e\"}"]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-backslash-escaping true})]
|
|
(is (= {"a" "o\ne"} (json/decode s))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-backslash-escaping false})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_JsonParseException Exception (json/decode s))))))
|
|
|
|
(deftest t-bindable-factories-non-numeric-numbers
|
|
(let [s "{\"foo\":NaN}"]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-non-numeric-numbers true})]
|
|
(is (= (type Double/NaN)
|
|
(type (:foo (json/decode s true))))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:allow-non-numeric-numbers false})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_JsonParseException Exception (json/decode s true))))))
|
|
|
|
(deftest t-bindable-factories-optimization-opts
|
|
(let [s "{\"a\": \"foo\"}"]
|
|
(doseq [opts [{:intern-field-names true}
|
|
{:intern-field-names false}
|
|
{:canonicalize-field-names true}
|
|
{:canonicalize-field-names false}]]
|
|
(binding [fact/*json-factory* (fact/make-json-factory opts)]
|
|
(is (= {"a" "foo"} (json/decode s)))))))
|
|
|
|
(deftest t-bindable-factories-escape-non-ascii
|
|
;; includes testing legacy fn opt of same name can override factory
|
|
(let [edn {:foo "It costs £100"}
|
|
expected-esc "{\"foo\":\"It costs \\u00A3100\"}"
|
|
expected-no-esc "{\"foo\":\"It costs £100\"}"
|
|
opt-esc {:escape-non-ascii true}
|
|
opt-no-esc {:escape-non-ascii false}]
|
|
(testing "default factory"
|
|
(doseq [[fn-opts expected]
|
|
[[{} expected-no-esc]
|
|
[opt-esc expected-esc]
|
|
[opt-no-esc expected-no-esc]]]
|
|
(testing fn-opts
|
|
(is (= expected (json/encode edn fn-opts) (encode-stream->str edn fn-opts))))))
|
|
(testing (str "factory: " opt-esc)
|
|
(binding [fact/*json-factory* (fact/make-json-factory opt-esc)]
|
|
(doseq [[fn-opts expected]
|
|
[[{} expected-esc]
|
|
[opt-esc expected-esc]
|
|
[opt-no-esc expected-no-esc]]]
|
|
(testing (str "fn: " fn-opts)
|
|
(is (= expected (json/encode edn fn-opts) (encode-stream->str edn fn-opts)))))))
|
|
(testing (str "factory: " opt-no-esc)
|
|
(binding [fact/*json-factory* (fact/make-json-factory opt-no-esc)]
|
|
(doseq [[fn-opts expected]
|
|
[[{} expected-no-esc]
|
|
[opt-esc expected-esc]
|
|
[opt-no-esc expected-no-esc]]]
|
|
(testing (str "fn: " fn-opts)
|
|
(is (= expected (json/encode edn fn-opts) (encode-stream->str edn fn-opts)))))))))
|
|
|
|
(deftest t-bindable-factories-quoteless
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:quote-field-names true})]
|
|
(is (= "{\"a\":\"foo\"}" (json/encode {:a "foo"}))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:quote-field-names false})]
|
|
(is (= "{a:\"foo\"}" (json/encode {:a "foo"})))))
|
|
|
|
(deftest t-bindable-factories-strict-duplicate-detection
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:strict-duplicate-detection true})]
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
(is (thrown? #_ JsonParseException Exception
|
|
(json/decode "{\"a\": 1, \"b\": 2, \"a\": 3}"))))
|
|
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:strict-duplicate-detection false})]
|
|
(is (= {"a" 3 "b" 2}
|
|
(json/decode "{\"a\": 1, \"b\": 2, \"a\": 3}")))))
|
|
|
|
(deftest t-bindable-factories-max-input-document-length
|
|
(let [edn {"a" (apply str (repeat 10000 "x"))}
|
|
sample-data (json/encode edn)]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-document-length (count sample-data)})]
|
|
(is (= edn (json/decode sample-data))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
;; as per Jackson docs, limit is inexact, so dividing input length by 2 should do the trick
|
|
{:max-input-document-length (/ (count sample-data) 2)})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)document length .* exceeds"
|
|
(json/decode sample-data))))))
|
|
|
|
(deftest t-bindable-factories-max-input-token-count
|
|
;; A token is a single unit of input, such as a number, a string, an object start or end, or an array start or end.
|
|
(let [edn {"1" 2 "3" 4}
|
|
sample-data (json/encode edn)]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-token-count 6})]
|
|
(is (= edn (json/decode sample-data))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-token-count 5})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)token count .* exceeds"
|
|
(json/decode sample-data))))))
|
|
|
|
(deftest t-bindable-factories-max-input-name-length
|
|
(let [k "somekey"
|
|
edn {k 1}
|
|
sample-data (json/encode edn)]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-name-length (count k)})]
|
|
(is (= edn (json/decode sample-data))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-name-length (dec (count k))})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)name .* exceeds"
|
|
(json/decode sample-data)))))
|
|
(let [default-limit (:max-input-name-length fact/default-factory-options)]
|
|
(let [k (str-of-len default-limit)
|
|
edn {k 1}
|
|
sample-data (json/encode edn)]
|
|
(is (= edn (json/decode sample-data))))
|
|
(let [k (str-of-len (inc default-limit))
|
|
sample-data (json/encode {k 1})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)name .* exceeds"
|
|
(json/decode sample-data))))))
|
|
|
|
(deftest t-bindable-factories-input-nesting-depth
|
|
(let [edn (nested-map 100)
|
|
sample-data (json/encode edn)]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-nesting-depth 100})]
|
|
(is (= edn (json/decode sample-data))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-nesting-depth 99})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)nesting depth .* exceeds"
|
|
(json/decode sample-data))))))
|
|
|
|
(deftest t-bindable-factories-max-input-number-length
|
|
(let [num 123456789
|
|
edn {"foo" num}
|
|
sample-data (json/encode edn)]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-number-length (-> num str count)})]
|
|
(is (= edn (json/decode sample-data))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-number-length (-> num str count dec)})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)number value length .* exceeds"
|
|
(json/decode sample-data)))))
|
|
(let [default-limit (:max-input-number-length fact/default-factory-options)]
|
|
(let [num (bigint (str-of-len default-limit 2))
|
|
edn {"foo" num}
|
|
sample-data (json/encode edn)]
|
|
(is (= edn (json/decode sample-data))))
|
|
(let [num (bigint (str-of-len (inc default-limit) 2))
|
|
sample-data (json/encode {"foo" num})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)number value length .* exceeds"
|
|
(json/decode sample-data))))))
|
|
|
|
(deftest t-bindable-factories-max-input-string-length
|
|
(let [big-string (str-of-len 40000000)
|
|
edn {"big-string" big-string}
|
|
sample-data (json/encode edn)]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-string-length (count big-string)})]
|
|
(is (= edn (json/decode sample-data))))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-input-string-length (dec (count big-string))})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)string value length .* exceeds"
|
|
(json/decode sample-data)))))
|
|
(let [default-limit (:max-input-string-length fact/default-factory-options)]
|
|
(let [big-string (str-of-len default-limit)
|
|
edn {"big-string" big-string}
|
|
sample-data (json/encode edn)]
|
|
(is (= edn (json/decode sample-data))))
|
|
(let [big-string (str-of-len (inc default-limit))
|
|
sample-data (json/encode {"big-string" big-string})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)string value length .* exceeds"
|
|
(json/decode sample-data))))))
|
|
|
|
(deftest t-bindable-factories-max-output-nesting-depth
|
|
(let [edn (nested-map 100)]
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-output-nesting-depth 100})]
|
|
(is (.contains (json/encode edn) "\"99\"")))
|
|
(binding [fact/*json-factory* (fact/make-json-factory
|
|
{:max-output-nesting-depth 99})]
|
|
(is (thrown-with-msg?
|
|
;; BB-TEST-PATCH: Generalized exception check
|
|
#_StreamConstraintsException Exception #"(?i)nesting depth .* exceeds"
|
|
(json/encode edn))))))
|
|
|
|
(deftest t-persistent-queue
|
|
(let [q (conj clojure.lang.PersistentQueue/EMPTY 1 2 3)]
|
|
(is (= q (json/decode (json/encode q))))))
|
|
|
|
(deftest t-pretty-print
|
|
(is (= (str/join (System/lineSeparator)
|
|
["{"
|
|
" \"bar\" : [ {"
|
|
" \"baz\" : 2"
|
|
" }, \"quux\", [ 1, 2, 3 ] ],"
|
|
" \"foo\" : 1"
|
|
"}"])
|
|
(json/encode (sorted-map :foo 1 :bar [{:baz 2} :quux [1 2 3]])
|
|
{:pretty true}))))
|
|
|
|
(deftest t-pretty-print-custom-linebreak
|
|
(is (= (str/join "foo"
|
|
["{"
|
|
" \"bar\" : [ {"
|
|
" \"baz\" : 2"
|
|
" }, \"quux\", [ 1, 2, 3 ] ],"
|
|
" \"foo\" : 1"
|
|
"}"])
|
|
(json/encode (sorted-map :foo 1 :bar [{:baz 2} :quux [1 2 3]])
|
|
{:pretty {:line-break "foo"}}))))
|
|
|
|
(deftest t-pretty-print-illegal-argument
|
|
; just expecting this not to throw
|
|
(json/encode {:foo "bar"}
|
|
{:pretty []})
|
|
(json/encode {:foo "bar"}
|
|
{:pretty nil}))
|
|
|
|
(deftest t-custom-pretty-print-with-defaults
|
|
(let [test-obj (sorted-map :foo 1 :bar {:baz [{:ulu "mulu"} {:moot "foo"} 3]} :quux :blub)
|
|
pretty-str-default (json/encode test-obj {:pretty true})
|
|
pretty-str-custom (json/encode test-obj {:pretty {}})]
|
|
(is (= pretty-str-default pretty-str-custom))
|
|
(when-not (= pretty-str-default pretty-str-custom)
|
|
; print for easy comparison
|
|
(println "; default pretty print")
|
|
(println pretty-str-default)
|
|
(println "; custom pretty print with default options")
|
|
(println pretty-str-custom))))
|
|
|
|
(deftest t-custom-pretty-print-with-non-defaults
|
|
(let [test-obj (sorted-map :foo 1 :bar {:baz [{:ulu "mulu"} {:moot "foo"} 3]} :quux :blub)
|
|
test-opts {:pretty {:indentation 4
|
|
:indent-arrays? false
|
|
:before-array-values ""
|
|
:after-array-values ""
|
|
:object-field-value-separator ": "}}
|
|
expected (str/join (System/lineSeparator)
|
|
["{"
|
|
" \"bar\": {"
|
|
" \"baz\": [{"
|
|
" \"ulu\": \"mulu\""
|
|
" }, {"
|
|
" \"moot\": \"foo\""
|
|
" }, 3]"
|
|
" },"
|
|
" \"foo\": 1,"
|
|
" \"quux\": \"blub\""
|
|
"}"])
|
|
pretty-str (json/encode test-obj test-opts)]
|
|
|
|
; just to be easy on the eyes in case of error
|
|
(when-not (= expected pretty-str)
|
|
(println "; pretty print with options - actual")
|
|
(println pretty-str)
|
|
(println "; pretty print with options - expected")
|
|
(println expected))
|
|
(is (= expected pretty-str))))
|
|
|
|
(deftest t-custom-pretty-print-with-noident-objects
|
|
(let [test-obj [{:foo 1 :bar 2} {:foo 3 :bar 4}]
|
|
test-opts {:pretty {:indent-objects? false}}
|
|
expected (str "[ { \"foo\" : 1, \"bar\" : 2 }, "
|
|
"{ \"foo\" : 3, \"bar\" : 4 } ]")
|
|
pretty-str (json/encode test-obj test-opts)]
|
|
; just to be easy on the eyes in case of error
|
|
(when-not (= expected pretty-str)
|
|
(println "; pretty print with options - actual")
|
|
(println pretty-str)
|
|
(println "; pretty print with options - expected")
|
|
(println expected))
|
|
(is (= expected pretty-str))))
|
|
|
|
(deftest t-custom-keyword-fn
|
|
(is (= {:FOO "bar"} (json/decode "{\"foo\": \"bar\"}"
|
|
(fn [k] (keyword (.toUpperCase k))))))
|
|
(is (= {"foo" "bar"} (json/decode "{\"foo\": \"bar\"}" nil)))
|
|
(is (= {"foo" "bar"} (json/decode "{\"foo\": \"bar\"}" false)))
|
|
(is (= {:foo "bar"} (json/decode "{\"foo\": \"bar\"}" true))))
|
|
|
|
(deftest t-custom-encode-key-fn
|
|
(is (= "{\"FOO\":\"bar\"}"
|
|
(json/encode {:foo :bar}
|
|
{:key-fn (fn [k] (.toUpperCase (name k)))}))))
|
|
|
|
;; BB-TEST-PATCH: bb does nto include cheshire.generate ns
|
|
#_(deftest test-add-remove-encoder
|
|
(gen/remove-encoder java.net.URL)
|
|
(gen/add-encoder java.net.URL gen/encode-str)
|
|
(is (= "\"http://foo.com\""
|
|
(json/encode (java.net.URL. "http://foo.com"))))
|
|
(gen/remove-encoder java.net.URL)
|
|
(is (thrown? JsonGenerationException
|
|
(json/encode (java.net.URL. "http://foo.com")))))
|
|
|
|
#_(defprotocol TestP
|
|
(foo [this] "foo method"))
|
|
|
|
#_(defrecord TestR [state])
|
|
|
|
#_(extend TestR
|
|
TestP
|
|
{:foo (constantly "bar")})
|
|
|
|
#_(deftest t-custom-protocol-encoder
|
|
(let [rec (TestR. :quux)]
|
|
(is (= {:state "quux"} (json/decode (json/encode rec) true)))
|
|
(gen/add-encoder cheshire.test.core.TestR
|
|
(fn [obj jg]
|
|
(.writeString jg (foo obj))))
|
|
(is (= "bar" (json/decode (json/encode rec))))
|
|
(gen/remove-encoder cheshire.test.core.TestR)
|
|
(is (= {:state "quux"} (json/decode (json/encode rec) true)))))
|
|
|
|
#_(defprotocol CTestP
|
|
(thing [this] "thing method"))
|
|
#_(defrecord CTestR [state])
|
|
#_(extend CTestR
|
|
CTestP
|
|
{:thing (constantly "thing")})
|
|
|
|
#_(deftest t-custom-helpers
|
|
(let [thing (CTestR. :state)
|
|
remove #(gen/remove-encoder CTestR)]
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-nil nil jg)))
|
|
(is (= nil (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-str "foo" jg)))
|
|
(is (= "foo" (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-number 5 jg)))
|
|
(is (= 5 (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-long 4 jg)))
|
|
(is (= 4 (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-int 3 jg)))
|
|
(is (= 3 (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-ratio 1/2 jg)))
|
|
(is (= 0.5 (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-seq [:foo :bar] jg)))
|
|
(is (= ["foo" "bar"] (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-date (Date. (long 0)) jg)))
|
|
(binding [gen/*date-format* "yyyy-MM-dd'T'HH:mm:ss'Z'"]
|
|
(is (= "1970-01-01T00:00:00Z" (json/decode (json/encode thing) true))))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-bool true jg)))
|
|
(is (= true (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-named :foo jg)))
|
|
(is (= "foo" (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-map {:foo "bar"} jg)))
|
|
(is (= {:foo "bar"} (json/decode (json/encode thing) true)))
|
|
(remove)
|
|
(gen/add-encoder CTestR (fn [_obj jg] (gen/encode-symbol 'foo jg)))
|
|
(is (= "foo" (json/decode (json/encode thing) true)))
|
|
(remove)))
|
|
|
|
(deftest t-float-encoding
|
|
(is (= "{\"foo\":0.01}" (json/encode {:foo (float 0.01)}))))
|
|
|
|
(deftest t-non-const-bools
|
|
(is (= {:a 1} (json/decode "{\"a\": 1}" (Boolean. true)))))
|
|
|
|
;; BB-TEST-PATCH: bb does not include cheshire.exact ns
|
|
#_(deftest t-invalid-json
|
|
(let [invalid-json-message "Invalid JSON, expected exactly one parseable object but multiple objects were found"]
|
|
(are [x y] (= x (try
|
|
y
|
|
(catch Exception e
|
|
(.getMessage e))))
|
|
invalid-json-message (json-exact/decode "{\"foo\": 1}asdf")
|
|
invalid-json-message (json-exact/decode "{\"foo\": 123}null")
|
|
invalid-json-message (json-exact/decode "\"hello\" : 123}")
|
|
{"foo" 1} (json/decode "{\"foo\": 1}")
|
|
invalid-json-message (json-exact/decode-strict "{\"foo\": 1}asdf")
|
|
invalid-json-message (json-exact/decode-strict "{\"foo\": 123}null")
|
|
invalid-json-message (json-exact/decode-strict "\"hello\" : 123}")
|
|
{"foo" 1} (json/decode-strict "{\"foo\": 1}"))))
|