2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE NoImplicitPrelude #-}
5{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TemplateHaskell #-}
9-- For Data.Aeson.Types.camelTo
10{-# OPTIONS_GHC -fno-warn-deprecations #-}
12#if MIN_VERSION_base(4,9,0)
13{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
20 , withEmbeddedJSONTest
25import Control.Monad (forM, forM_)
26import Data.Aeson ((.=), (.:), (.:?), (.:!), FromJSON(..), FromJSONKeyFunction(..), FromJSONKey(..), ToJSON1(..), decode, eitherDecode, encode, fromJSON, genericParseJSON, genericToEncoding, genericToJSON, object, withObject, withEmbeddedJSON)
27import Data.Aeson.Internal (JSONPathElement(..), formatError)
28import Data.Aeson.TH (deriveJSON, deriveToJSON, deriveToJSON1)
29import Data.Aeson.Text (encodeToTextBuilder)
30import Data.Aeson.Types (Options(..), Result(Success), ToJSON(..), Value(Null), camelTo, camelTo2, defaultOptions, omitNothingFields, parse)
31import Data.Char (toUpper)
32import Data.Either.Compat (isLeft, isRight)
33import Data.Hashable (hash)
34import Data.HashMap.Strict (HashMap)
35import Data.List (sort)
36import Data.Maybe (fromMaybe)
37import Data.Sequence (Seq)
38import Data.Scientific (Scientific, scientific)
39import Data.Tagged (Tagged(..))
40import Data.Text (Text)
41import Data.Time (UTCTime)
42import Data.Time.Format (parseTime)
43import Data.Time.Locale.Compat (defaultTimeLocale)
44import GHC.Generics (Generic)
46import Numeric.Natural (Natural)
47import System.Directory (getDirectoryContents)
48import System.FilePath ((</>), takeExtension, takeFileName)
49import Test.Tasty (TestTree, testGroup)
50import Test.Tasty.HUnit (Assertion, assertBool, assertFailure, assertEqual, testCase)
51import Text.Printf (printf)
52import UnitTests.NullaryConstructors (nullaryConstructors)
53import qualified Data.ByteString.Base16.Lazy as LBase16
54import qualified Data.ByteString.Lazy.Char8 as L
55import qualified Data.HashSet as HashSet
56import qualified Data.Text.Lazy as LT
57import qualified Data.Text.Lazy.Builder as TLB
58import qualified Data.Text.Lazy.Encoding as LT
59import qualified Data.Text.Lazy.Encoding as TLE
60import qualified ErrorMessages
61import qualified SerializationFormatSpec
63-- Asserts that we can use both modules at once in the test suite.
64import Data.Aeson.Parser.UnescapeFFI ()
65import Data.Aeson.Parser.UnescapePure ()
68tests = testGroup "unit" [
69 testGroup "SerializationFormatSpec" SerializationFormatSpec.tests
70 , testGroup "ErrorMessages" ErrorMessages.tests
71 , testGroup "camelCase" [
72 testCase "camelTo" $ roundTripCamel "aName"
73 , testCase "camelTo" $ roundTripCamel "another"
74 , testCase "camelTo" $ roundTripCamel "someOtherName"
75 , testCase "camelTo" $
76 assertEqual "" "camel_apicase" (camelTo '_' "CamelAPICase")
77 , testCase "camelTo2" $ roundTripCamel2 "aName"
78 , testCase "camelTo2" $ roundTripCamel2 "another"
79 , testCase "camelTo2" $ roundTripCamel2 "someOtherName"
80 , testCase "camelTo2" $
81 assertEqual "" "camel_api_case" (camelTo2 '_' "CamelAPICase")
83 , testGroup "encoding" [
84 testCase "goodProducer" goodProducer
86 , testGroup "utctime" [
87 testCase "good" utcTimeGood
88 , testCase "bad" utcTimeBad
90 , testGroup "formatError" [
91 testCase "example 1" formatErrorExample
93 , testGroup ".:, .:?, .:!" $ fmap (testCase "-") dotColonMark
94 , testGroup "JSONPath" $ fmap (testCase "-") jsonPath
95 , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws
96 , testGroup "Issue #351" $ fmap (testCase "-") issue351
97 , testGroup "Nullary constructors" $ fmap (testCase "-") nullaryConstructors
98 , testGroup "FromJSONKey" $ fmap (testCase "-") fromJSONKeyAssertions
99 , testCase "PR #455" pr455
100 , testCase "Unescape string (PR #477)" unescapeString
101 , testCase "Show Options" showOptions
102 , testGroup "SingleMaybeField" singleMaybeField
103 , testCase "withEmbeddedJSON" withEmbeddedJSONTest
104 , testCase "SingleFieldCon" singleFieldCon
105 , testCase "Ratio with denominator 0" ratioDenominator0
106 , testCase "Big scientific exponent" bigScientificExponent
107 , testCase "Big integer decoding" bigIntegerDecoding
108 , testCase "Big natural decading" bigNaturalDecoding
109 , testCase "Big integer key decoding" bigIntegerKeyDecoding
112roundTripCamel :: String -> Assertion
113roundTripCamel name = assertEqual "" name (camelFrom '_' $ camelTo '_' name)
115roundTripCamel2 :: String -> Assertion
116roundTripCamel2 name = assertEqual "" name (camelFrom '_' $ camelTo2 '_' name)
118camelFrom :: Char -> String -> String
119camelFrom c s = let (p:ps) = split c s
120 in concat $ p : map capitalize ps
122 split c' s' = map L.unpack $ L.split c' $ L.pack s'
123 capitalize t = toUpper (head t) : tail t
126data Wibble = Wibble {
127 wibbleString :: String
129 } deriving (Generic, Show, Eq)
131instance FromJSON Wibble
133instance ToJSON Wibble where
134 toJSON = genericToJSON defaultOptions
135 toEncoding = genericToEncoding defaultOptions
137-- Test that if we put a bomb in a data structure, but only demand
138-- part of it via lazy encoding, we do not unexpectedly fail.
139goodProducer :: Assertion
140goodProducer = assertEqual "partial encoding should not explode on undefined"
141 '{' (L.head (encode wibble))
144 wibbleString = replicate k 'a'
149 arch32bit = (maxBound :: Int) == 2147483647
151-- Test decoding various UTC time formats
153-- Note: the incomplete pattern matches for UTCTimes are completely
154-- intentional. The test expects these parses to succeed. If the
155-- pattern matches fails, there's a bug in either the test or in aeson
156-- and needs to be investigated.
157utcTimeGood :: Assertion
159 let ts1 = "2015-01-01T12:13:00.00Z" :: LT.Text
160 let ts2 = "2015-01-01T12:13:00Z" :: LT.Text
161 -- 'T' between date and time is not required, can be space
162 let ts3 = "2015-01-03 12:13:00.00Z" :: LT.Text
163 let ts4 = "2015-01-03 12:13:00.125Z" :: LT.Text
164 let (Just (t1 :: UTCTime)) = parseWithAeson ts1
165 let (Just (t2 :: UTCTime)) = parseWithAeson ts2
166 let (Just (t3 :: UTCTime)) = parseWithAeson ts3
167 let (Just (t4 :: UTCTime)) = parseWithAeson ts4
168 assertEqual "utctime" (parseWithRead "%FT%T%QZ" ts1) t1
169 assertEqual "utctime" (parseWithRead "%FT%T%QZ" ts2) t2
170 assertEqual "utctime" (parseWithRead "%F %T%QZ" ts3) t3
171 assertEqual "utctime" (parseWithRead "%F %T%QZ" ts4) t4
172 -- Time zones. Both +HHMM and +HH:MM are allowed for timezone
173 -- offset, and MM may be omitted.
174 let ts5 = "2015-01-01T12:30:00.00+00" :: LT.Text
175 let ts6 = "2015-01-01T12:30:00.00+01:15" :: LT.Text
176 let ts7 = "2015-01-01T12:30:00.00-02" :: LT.Text
177 let ts8 = "2015-01-01T22:00:00.00-03" :: LT.Text
178 let ts9 = "2015-01-01T22:00:00.00-04:30" :: LT.Text
179 let (Just (t5 :: UTCTime)) = parseWithAeson ts5
180 let (Just (t6 :: UTCTime)) = parseWithAeson ts6
181 let (Just (t7 :: UTCTime)) = parseWithAeson ts7
182 let (Just (t8 :: UTCTime)) = parseWithAeson ts8
183 let (Just (t9 :: UTCTime)) = parseWithAeson ts9
184 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T12:30:00.00Z") t5
185 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T11:15:00.00Z") t6
186 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T14:30:00Z") t7
187 -- ts8 wraps around to the next day in UTC
188 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-02T01:00:00Z") t8
189 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-02T02:30:00Z") t9
191 -- Seconds in Time can be omitted
192 let ts10 = "2015-01-03T12:13Z" :: LT.Text
193 let ts11 = "2015-01-03 12:13Z" :: LT.Text
194 let ts12 = "2015-01-01T12:30-02" :: LT.Text
195 let (Just (t10 :: UTCTime)) = parseWithAeson ts10
196 let (Just (t11 :: UTCTime)) = parseWithAeson ts11
197 let (Just (t12 :: UTCTime)) = parseWithAeson ts12
198 assertEqual "utctime" (parseWithRead "%FT%H:%MZ" ts10) t10
199 assertEqual "utctime" (parseWithRead "%F %H:%MZ" ts11) t11
200 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T14:30:00Z") t12
202 -- leap seconds are included correctly
203 let ts13 = "2015-08-23T23:59:60.128+00" :: LT.Text
204 let (Just (t13 :: UTCTime)) = parseWithAeson ts13
205 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-08-23T23:59:60.128Z") t13
206 let ts14 = "2015-08-23T23:59:60.999999999999+00" :: LT.Text
207 let (Just (t14 :: UTCTime)) = parseWithAeson ts14
208 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-08-23T23:59:60.999999999999Z") t14
211 parseWithRead :: String -> LT.Text -> UTCTime
213 fromMaybe (error "parseTime input malformed") . parseTime defaultTimeLocale f . LT.unpack $ s
214 parseWithAeson :: LT.Text -> Maybe UTCTime
215 parseWithAeson s = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""]
217-- Test that a few non-timezone qualified timestamp formats get
218-- rejected if decoding to UTCTime.
219utcTimeBad :: Assertion
221 verifyFailParse "2000-01-01T12:13:00" -- missing Zulu time not allowed (some TZ required)
222 verifyFailParse "2000-01-01 12:13:00" -- missing Zulu time not allowed (some TZ required)
223 verifyFailParse "2000-01-01" -- date only not OK
224 verifyFailParse "2000-01-01Z" -- date only not OK
225 verifyFailParse "2015-01-01T12:30:00.00+00Z" -- no Zulu if offset given
226 verifyFailParse "2015-01-01T12:30:00.00+00:00Z" -- no Zulu if offset given
227 verifyFailParse "2015-01-03 12:13:00.Z" -- decimal at the end but no digits
228 verifyFailParse "2015-01-03 12:13.000Z" -- decimal at the end, but no seconds
229 verifyFailParse "2015-01-03 23:59:61Z" -- exceeds allowed seconds per day
231 verifyFailParse (s :: LT.Text) =
232 let (dec :: Maybe UTCTime) = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""] in
233 assertEqual "verify failure" Nothing dec
235-- Non identifier keys should be escaped & enclosed in brackets
236formatErrorExample :: Assertion
238 let rhs = formatError [Index 0, Key "foo", Key "bar", Key "a.b.c", Key "", Key "'\\", Key "end"] "error msg"
239 lhs = "Error in $[0].foo.bar['a.b.c']['']['\\'\\\\'].end: error msg"
240 in assertEqual "formatError example" lhs rhs
242------------------------------------------------------------------------------
243-- Comparison (.:?) and (.:!)
244------------------------------------------------------------------------------
246newtype T1 = T1 (Maybe Int) deriving (Eq, Show)
247newtype T2 = T2 (Maybe Int) deriving (Eq, Show)
248newtype T3 = T3 (Maybe Int) deriving (Eq, Show)
250instance FromJSON T1 where parseJSON = fmap T1 . withObject "T1" (.: "value")
251instance FromJSON T2 where parseJSON = fmap T2 . withObject "T2" (.:? "value")
252instance FromJSON T3 where parseJSON = fmap T3 . withObject "T3" (.:! "value")
254dotColonMark :: [Assertion]
256 assertEqual ".: not-present" Nothing (decode ex1 :: Maybe T1)
257 , assertEqual ".: 42" (Just (T1 (Just 42))) (decode ex2 :: Maybe T1)
258 , assertEqual ".: null" (Just (T1 Nothing)) (decode ex3 :: Maybe T1)
260 , assertEqual ".:? not-present" (Just (T2 Nothing)) (decode ex1 :: Maybe T2)
261 , assertEqual ".:? 42" (Just (T2 (Just 42))) (decode ex2 :: Maybe T2)
262 , assertEqual ".:? null" (Just (T2 Nothing)) (decode ex3 :: Maybe T2)
264 , assertEqual ".:! not-present" (Just (T3 Nothing)) (decode ex1 :: Maybe T3)
265 , assertEqual ".:! 42" (Just (T3 (Just 42))) (decode ex2 :: Maybe T3)
266 , assertEqual ".:! null" Nothing (decode ex3 :: Maybe T3)
269 ex2 = "{\"value\": 42 }"
270 ex3 = "{\"value\": null }"
272------------------------------------------------------------------------------
273-- These tests check that JSONPath is tracked correctly
274-----------------------------------------------------------------------------
276jsonPath :: [Assertion]
280 (Left "Error in $[1].Left[1]: expected Bool, encountered Number")
281 (eitherDecode "[1,{\"Left\":[2,3]}]"
282 :: Either String (Int, Either (Int, Bool) ()))
284 , assertEqual "Seq a"
285 (Left "Error in $[2]: expected Int, encountered Boolean")
286 (eitherDecode "[0,1,true]" :: Either String (Seq Int))
287 , assertEqual "Wibble"
288 (Left "Error in $.wibbleInt: expected Int, encountered Boolean")
289 (eitherDecode "{\"wibbleString\":\"\",\"wibbleInt\":true}"
290 :: Either String Wibble)
293------------------------------------------------------------------------------
294-- Check that the hashes of two equal Value are the same
295------------------------------------------------------------------------------
297hashableLaws :: [Assertion]
299 assertEqual "Hashable Object" (hash a) (hash b)
302 a = object ["223" .= False, "807882556" .= True]
303 b = object ["807882556" .= True, "223" .= False]
305-------------------------------------------------------------------------------
307-------------------------------------------------------------------------------
309newtype MyText = MyText Text
310 deriving (FromJSONKey)
312newtype MyText' = MyText' Text
314instance FromJSONKey MyText' where
315 fromJSONKey = fmap MyText' fromJSONKey
316 fromJSONKeyList = error "not used"
318fromJSONKeyAssertions :: [Assertion]
319fromJSONKeyAssertions =
320 [ assertIsCoerce "Text" (fromJSONKey :: FromJSONKeyFunction Text)
321 , assertIsCoerce "Tagged Int Text" (fromJSONKey :: FromJSONKeyFunction (Tagged Int Text))
322 , assertIsCoerce "MyText" (fromJSONKey :: FromJSONKeyFunction MyText)
324#if __GLASGOW_HASKELL__ >= 710
325 , assertIsCoerce' "MyText'" (fromJSONKey :: FromJSONKeyFunction MyText')
329 assertIsCoerce _ (FromJSONKeyCoerce _) = pure ()
330 assertIsCoerce n _ = assertFailure n
332#if __GLASGOW_HASKELL__ >= 710
333 assertIsCoerce' _ (FromJSONKeyCoerce _) = pure ()
334 assertIsCoerce' n _ = pickWithRules (assertFailure n) (pure ())
336-- | Pick the first when RULES are enabled, e.g. optimisations are on
338 :: a -- ^ Pick this when RULES are on
339 -> a -- ^ use this otherwise
342{-# NOINLINE pickWithRules #-}
343{-# RULES "pickWithRules/rule" [0] forall x. pickWithRules x = const x #-}
346------------------------------------------------------------------------------
348------------------------------------------------------------------------------
350-- A regression test for: https://github.com/bos/aeson/issues/351
351overlappingRegression :: FromJSON a => L.ByteString -> [a]
352overlappingRegression bs = fromMaybe [] $ decode bs
354issue351 :: [Assertion]
356 assertEqual "Int" ([1, 2, 3] :: [Int]) $ overlappingRegression "[1, 2, 3]"
357 , assertEqual "Char" ("abc" :: String) $ overlappingRegression "\"abc\""
358 , assertEqual "Char" ("" :: String) $ overlappingRegression "[\"a\", \"b\", \"c\"]"
361------------------------------------------------------------------------------
362-- Comparison between bytestring and text encoders
363------------------------------------------------------------------------------
365ioTests :: IO [TestTree]
367 enc <- encoderComparisonTests
371encoderComparisonTests :: IO TestTree
372encoderComparisonTests = do
373 encoderTests <- forM testFiles $ \file0 -> do
374 let file = "benchmarks/json-data/" ++ file0
375 return $ testCase file $ do
376 inp <- L.readFile file
377 case eitherDecode inp of
378 Left err -> assertFailure $ "Decoding failure: " ++ err
379 Right val -> assertEqual "" (encode val) (encodeViaText val)
380 return $ testGroup "encoders" encoderTests
382 encodeViaText :: Value -> L.ByteString
384 TLE.encodeUtf8 . TLB.toLazyText . encodeToTextBuilder . toJSON
401-- A regression test for: https://github.com/bos/aeson/issues/293
402data MyRecord = MyRecord {_field1 :: Maybe Int, _field2 :: Maybe Bool}
404data MyRecord2 = MyRecord2 {_field3 :: Maybe Int, _field4 :: Maybe Bool}
407instance ToJSON MyRecord2
408instance FromJSON MyRecord2
410-- A regression test for: https://github.com/bos/aeson/pull/477
411unescapeString :: Assertion
413 assertEqual "Basic escaping"
414 (Right ("\" / \\ \b \f \n \r \t" :: String))
415 (eitherDecode "\"\\\" \\/ \\\\ \\b \\f \\n \\r \\t\"")
417 forM_ [minBound .. maxBound :: Char] $ \ c ->
418 let s = LT.pack [c] in
419 assertEqual (printf "UTF-16 encoded '\\x%X'" c)
420 (Right s) (eitherDecode $ utf16Char s)
422 utf16Char = formatString . LBase16.encode . LT.encodeUtf16BE
424 | L.length s == 4 = L.concat ["\"\\u", s, "\""]
426 L.concat ["\"\\u", L.take 4 s, "\\u", L.drop 4 s, "\""]
427 | otherwise = error "unescapeString: can't happen"
431jsonTestSuiteTest :: FilePath -> TestTree
432jsonTestSuiteTest path = testCase fileName $ do
433 payload <- L.readFile path
434 let result = eitherDecode payload :: Either String Value
435 assertBool fileName $ case take 2 fileName of
436 "i_" -> isRight result
437 "n_" -> isLeft result
438 "y_" -> isRight result
439 _ -> isRight result -- test_transform tests have inconsistent names
441 fileName = takeFileName path
443-- Build a collection of tests based on the current contents of the
444-- JSONTestSuite test directories.
446jsonTestSuite :: IO TestTree
448 let suitePath = "tests/JSONTestSuite"
449 let suites = ["test_parsing", "test_transform"]
450 testPaths <- fmap (sort . concat) . forM suites $ \suite -> do
451 let dir = suitePath </> suite
452 entries <- getDirectoryContents dir
453 let ok name = takeExtension name == ".json" &&
454 not (name `HashSet.member` blacklist)
455 return . map (dir </>) . filter ok $ entries
456 return $ testGroup "JSONTestSuite" $ map jsonTestSuiteTest testPaths
458-- The set expected-to-be-failing JSONTestSuite tests.
459-- Not all of these failures are genuine bugs.
460-- Of those that are bugs, not all are worth fixing.
462blacklist :: HashSet.HashSet String
463-- blacklist = HashSet.empty
464blacklist = _blacklist
466_blacklist :: HashSet.HashSet String
467_blacklist = HashSet.fromList [
468 "i_object_key_lone_2nd_surrogate.json"
469 , "i_string_1st_surrogate_but_2nd_missing.json"
470 , "i_string_1st_valid_surrogate_2nd_invalid.json"
471 , "i_string_UTF-16LE_with_BOM.json"
472 , "i_string_UTF-16_invalid_lonely_surrogate.json"
473 , "i_string_UTF-16_invalid_surrogate.json"
474 , "i_string_UTF-8_invalid_sequence.json"
475 , "i_string_incomplete_surrogate_and_escape_valid.json"
476 , "i_string_incomplete_surrogate_pair.json"
477 , "i_string_incomplete_surrogates_escape_valid.json"
478 , "i_string_invalid_lonely_surrogate.json"
479 , "i_string_invalid_surrogate.json"
480 , "i_string_inverted_surrogates_U+1D11E.json"
481 , "i_string_lone_second_surrogate.json"
482 , "i_string_not_in_unicode_range.json"
483 , "i_string_truncated-utf-8.json"
484 , "i_structure_UTF-8_BOM_empty_object.json"
485 , "n_string_unescaped_crtl_char.json"
486 , "n_string_unescaped_newline.json"
487 , "n_string_unescaped_tab.json"
488 , "string_1_escaped_invalid_codepoint.json"
489 , "string_1_invalid_codepoint.json"
490 , "string_1_invalid_codepoints.json"
491 , "string_2_escaped_invalid_codepoints.json"
492 , "string_2_invalid_codepoints.json"
493 , "string_3_escaped_invalid_codepoints.json"
494 , "string_3_invalid_codepoints.json"
495 , "y_string_utf16BE_no_BOM.json"
496 , "y_string_utf16LE_no_BOM.json"
499-- A regression test for: https://github.com/bos/aeson/pull/455
500data Foo a = FooNil | FooCons (Foo Int)
503pr455 = assertEqual "FooCons FooNil"
504 (toJSON foo) (liftToJSON undefined undefined foo)
509showOptions :: Assertion
514 ++ "fieldLabelModifier =~ \"exampleField\""
515 ++ ", constructorTagModifier =~ \"ExampleConstructor\""
516 ++ ", allNullaryToStringTag = True"
517 ++ ", omitNothingFields = False"
518 ++ ", sumEncoding = TaggedObject {tagFieldName = \"tag\", contentsFieldName = \"contents\"}"
519 ++ ", unwrapUnaryRecords = False"
520 ++ ", tagSingleConstructors = False"
522 (show defaultOptions)
524newtype SingleMaybeField = SingleMaybeField { smf :: Maybe Int }
525 deriving (Eq, Show, Generic)
527singleMaybeField :: [TestTree]
529 (gName, gToJSON, gToEncoding, gFromJSON) <-
530 [ ("generic", genericToJSON opts, genericToEncoding opts, parse (genericParseJSON opts))
531 , ("th", toJSON, toEncoding, fromJSON) ]
534 assertEqual "toJSON" Null (gToJSON v)
535 assertEqual "toEncoding" (toEncoding (gToJSON v)) (gToEncoding v)
536 assertEqual "fromJSON" (Success v) (gFromJSON Null)
538 v = SingleMaybeField Nothing
539 opts = defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True}
542newtype EmbeddedJSONTest = EmbeddedJSONTest Int
545instance FromJSON EmbeddedJSONTest where
547 withObject "Object" $ \o ->
548 EmbeddedJSONTest <$> (o .: "prop" >>= withEmbeddedJSON "Quoted Int" parseJSON)
550withEmbeddedJSONTest :: Assertion
551withEmbeddedJSONTest =
552 assertEqual "Unquote embedded JSON" (Right $ EmbeddedJSONTest 1) (eitherDecode "{\"prop\":\"1\"}")
554-- Regression test for https://github.com/bos/aeson/issues/627
555newtype SingleFieldCon = SingleFieldCon Int deriving (Eq, Show, Generic)
557instance FromJSON SingleFieldCon where
558 parseJSON = genericParseJSON defaultOptions{unwrapUnaryRecords=True}
559 -- This option should have no effect on this type
561singleFieldCon :: Assertion
563 assertEqual "fromJSON" (Right (SingleFieldCon 0)) (eitherDecode "0")
565ratioDenominator0 :: Assertion
567 assertEqual "Ratio with denominator 0"
568 (Left "Error in $: Ratio denominator was 0")
569 (eitherDecode "{ \"numerator\": 1, \"denominator\": 0 }" :: Either String Rational)
571bigScientificExponent :: Assertion
572bigScientificExponent =
573 assertEqual "Encoding an integral scientific with a large exponent should normalize it"
575 (encode (scientific 1 2000 :: Scientific))
577bigIntegerDecoding :: Assertion
579 assertEqual "Decoding an Integer with a large exponent should fail"
580 (Left "Error in $: expected a number with exponent <= 1024, encountered Number")
581 ((eitherDecode :: L.ByteString -> Either String Integer) "1e2000")
583bigNaturalDecoding :: Assertion
585 assertEqual "Decoding a Natural with a large exponent should fail"
586 (Left "Error in $: expected a number with exponent <= 1024, encountered Number")
587 ((eitherDecode :: L.ByteString -> Either String Integer) "1e2000")
589bigIntegerKeyDecoding :: Assertion
590bigIntegerKeyDecoding =
591 assertEqual "Decoding an Integer key with a large exponent should fail"
592 (Left "Error in $['1e2000']: expected a number with exponent <= 1024, encountered Number")
593 ((eitherDecode :: L.ByteString -> Either String (HashMap Integer Value)) "{ \"1e2000\": null }")
595bigNaturalKeyDecoding :: Assertion
596bigNaturalKeyDecoding =
597 assertEqual "Decoding an Integer key with a large exponent should fail"
598 (Left "Error in $['1e2000']: expected a number with exponent <= 1024, encountered Number")
599 ((eitherDecode :: L.ByteString -> Either String (HashMap Natural Value)) "{ \"1e2000\": null }")
601deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
603deriveToJSON defaultOptions ''Foo
604deriveToJSON1 defaultOptions ''Foo
606deriveJSON defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True} ''SingleMaybeField
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list