(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}"))))