@@ -1,6 +1,7 @@
11{-# LANGUAGE DeriveGeneric #-}
22{-# LANGUAGE OverloadedStrings #-}
33{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE TemplateHaskell #-}
45{-# OPTIONS_GHC -fno-warn-orphans #-}
56module Main where
67
@@ -9,21 +10,25 @@ import Control.Exception
910import Control.Monad
1011import Control.Monad.Reader
1112import Data.Aeson
13+import Data.Aeson.Types (parseEither)
14+import Data.DeriveTH
1215import qualified Data.HashMap.Strict as HM
1316import Data.List (nub)
1417import Data.List.NonEmpty (NonEmpty (..))
1518import qualified Data.List.NonEmpty as NE
1619import qualified Data.Map.Strict as M
1720import Data.Monoid
21+import Data.Proxy
1822import Data.Text (Text)
1923import qualified Data.Text as T
2024import Data.Time.Calendar (Day (..))
2125import Data.Time.Clock (UTCTime (..),
2226 secondsToDiffTime)
27+import Data.Typeable
2328import qualified Data.Vector as V
2429import Database.Bloodhound
2530import GHC.Generics (Generic)
26−import Network.HTTP.Client
31+import Network.HTTP.Client hiding (Proxy)
2732import qualified Network.HTTP.Types.Status as NHTS
2833import Prelude hiding (filter)
2934import Test.Hspec
@@ -31,6 +36,7 @@ import Test.QuickCheck.Property.Monoid (T (..), eq, prop_Monoid)
3136
3237import Test.Hspec.QuickCheck (prop)
3338import Test.QuickCheck
39+import Test.QuickCheck.Instances ()
3440
3541testServer :: Server
3642testServer = Server "http://localhost:9200"
@@ -101,6 +107,12 @@ is v = testServerBranch >>= \x -> return $ x == Just (serverBranch v)
101107when' :: Monad m => m Bool -> m () -> m ()
102108when' b f = b >>= \x -> when x f
103109
110+propJSON :: forall a. (Arbitrary a, ToJSON a, FromJSON a, Show a, Eq a, Typeable a) => Proxy a -> Spec
111+propJSON _ = prop testName $ \(a :: a) ->
112+ parseEither parseJSON (toJSON a) === Right a
113+ where testName = show ty <> " FromJSON/ToJSON roundtrips"
114+ ty = typeOf (undefined :: a)
115+
104116data Location = Location { lat :: Double
105117 , lon :: Double } deriving (Eq, Generic, Show)
106118
@@ -265,42 +277,12 @@ instance ToJSON BulkTest where
265277noDuplicates :: Eq a => [a] -> Bool
266278noDuplicates xs = nub xs == xs
267279
268−instance Arbitrary RegexpFlags where
269− arbitrary = oneof [ pure AllRegexpFlags
270− , pure NoRegexpFlags
271− , SomeRegexpFlags <$> arbitrary
272− ]
273−
274280instance Arbitrary a => Arbitrary (NonEmpty a) where
275281 arbitrary = liftA2 (:|) arbitrary arbitrary
276282
277−instance Arbitrary RegexpFlag where
278− arbitrary = oneof [ pure AnyString
279− , pure Automaton
280− , pure Complement
281− , pure Empty
282− , pure Intersection
283− , pure Interval
284− ]
285−
286283arbitraryScore :: Gen Score
287284arbitraryScore = fmap getPositive <$> arbitrary
288285
289−instance Arbitrary Text where
290− arbitrary = T.pack <$> arbitrary
291−
292−instance (Arbitrary k, Arbitrary v, Ord k) => Arbitrary (M.Map k v) where
293− arbitrary = M.fromList <$> arbitrary
294−
295−instance Arbitrary IndexName where
296− arbitrary = IndexName <$> arbitrary
297−
298−instance Arbitrary MappingName where
299− arbitrary = MappingName <$> arbitrary
300−
301−instance Arbitrary DocId where
302− arbitrary = DocId <$> arbitrary
303−
304286instance Arbitrary a => Arbitrary (Hit a) where
305287 arbitrary = Hit <$> arbitrary
306288 <*> arbitrary
@@ -327,6 +309,135 @@ grabFirst r =
327309 (Right Nothing) -> Left "Source was missing"
328310 (Right (Just x)) -> Right x
329311
312+-------------------------------------------------------------------------------
313+arbitraryAlphaNum :: Gen Char
314+arbitraryAlphaNum = oneof [choose ('a', 'z')
315+ ,choose ('A','Z')
316+ , choose ('0', '9')]
317+
318+instance Arbitrary RoutingValue where
319+ arbitrary = RoutingValue . T.pack <$> listOf1 arbitraryAlphaNum
320+
321+instance Arbitrary AliasRouting where
322+ arbitrary = oneof [allAlias
323+ ,one
324+ ,theOther
325+ ,both]
326+ where one = GranularAliasRouting
327+ <$> (Just <$> arbitrary)
328+ <*> pure Nothing
329+ theOther = GranularAliasRouting Nothing
330+ <$> (Just <$> arbitrary)
331+ both = GranularAliasRouting
332+ <$> (Just <$> arbitrary)
333+ <*> (Just <$> arbitrary)
334+ allAlias = AllAliasRouting <$> arbitrary
335+
336+$(derive makeArbitrary ''IndexName)
337+$(derive makeArbitrary ''FieldName)
338+$(derive makeArbitrary ''MappingName)
339+$(derive makeArbitrary ''DocId)
340+$(derive makeArbitrary ''Version)
341+$(derive makeArbitrary ''IndexAliasRouting)
342+$(derive makeArbitrary ''ShardCount)
343+$(derive makeArbitrary ''ReplicaCount)
344+$(derive makeArbitrary ''TemplateName)
345+$(derive makeArbitrary ''TemplatePattern)
346+$(derive makeArbitrary ''QueryString)
347+$(derive makeArbitrary ''CacheName)
348+$(derive makeArbitrary ''CacheKey)
349+$(derive makeArbitrary ''Existence)
350+$(derive makeArbitrary ''CutoffFrequency)
351+$(derive makeArbitrary ''Analyzer)
352+$(derive makeArbitrary ''MaxExpansions)
353+$(derive makeArbitrary ''Lenient)
354+$(derive makeArbitrary ''Tiebreaker)
355+$(derive makeArbitrary ''Boost)
356+$(derive makeArbitrary ''BoostTerms)
357+$(derive makeArbitrary ''MinimumMatch)
358+$(derive makeArbitrary ''DisableCoord)
359+$(derive makeArbitrary ''IgnoreTermFrequency)
360+$(derive makeArbitrary ''MinimumTermFrequency)
361+$(derive makeArbitrary ''MaxQueryTerms)
362+$(derive makeArbitrary ''Fuzziness)
363+$(derive makeArbitrary ''PrefixLength)
364+$(derive makeArbitrary ''TypeName)
365+$(derive makeArbitrary ''PercentMatch)
366+$(derive makeArbitrary ''StopWord)
367+$(derive makeArbitrary ''QueryPath)
368+$(derive makeArbitrary ''AllowLeadingWildcard)
369+$(derive makeArbitrary ''LowercaseExpanded)
370+$(derive makeArbitrary ''EnablePositionIncrements)
371+$(derive makeArbitrary ''AnalyzeWildcard)
372+$(derive makeArbitrary ''GeneratePhraseQueries)
373+$(derive makeArbitrary ''Locale)
374+$(derive makeArbitrary ''MaxWordLength)
375+$(derive makeArbitrary ''MinWordLength)
376+$(derive makeArbitrary ''PhraseSlop)
377+$(derive makeArbitrary ''MinDocFrequency)
378+$(derive makeArbitrary ''MaxDocFrequency)
379+$(derive makeArbitrary ''Regexp)
380+$(derive makeArbitrary ''Filter)
381+$(derive makeArbitrary ''Query)
382+$(derive makeArbitrary ''SimpleQueryStringQuery)
383+$(derive makeArbitrary ''FieldOrFields)
384+$(derive makeArbitrary ''SimpleQueryFlag)
385+$(derive makeArbitrary ''RegexpQuery)
386+$(derive makeArbitrary ''QueryStringQuery)
387+$(derive makeArbitrary ''RangeQuery)
388+$(derive makeArbitrary ''RangeValue)
389+$(derive makeArbitrary ''PrefixQuery)
390+$(derive makeArbitrary ''NestedQuery)
391+$(derive makeArbitrary ''MoreLikeThisFieldQuery)
392+$(derive makeArbitrary ''MoreLikeThisQuery)
393+$(derive makeArbitrary ''IndicesQuery)
394+$(derive makeArbitrary ''HasParentQuery)
395+$(derive makeArbitrary ''HasChildQuery)
396+$(derive makeArbitrary ''FuzzyQuery)
397+$(derive makeArbitrary ''FuzzyLikeFieldQuery)
398+$(derive makeArbitrary ''FuzzyLikeThisQuery)
399+$(derive makeArbitrary ''FilteredQuery)
400+$(derive makeArbitrary ''DisMaxQuery)
401+$(derive makeArbitrary ''CommonTermsQuery)
402+$(derive makeArbitrary ''DistanceRange)
403+$(derive makeArbitrary ''MultiMatchQuery)
404+$(derive makeArbitrary ''LessThanD)
405+$(derive makeArbitrary ''LessThanEqD)
406+$(derive makeArbitrary ''GreaterThanD)
407+$(derive makeArbitrary ''GreaterThanEqD)
408+$(derive makeArbitrary ''LessThan)
409+$(derive makeArbitrary ''LessThanEq)
410+$(derive makeArbitrary ''GreaterThan)
411+$(derive makeArbitrary ''GreaterThanEq)
412+$(derive makeArbitrary ''GeoPoint)
413+$(derive makeArbitrary ''NullValue)
414+$(derive makeArbitrary ''MinimumMatchHighLow)
415+$(derive makeArbitrary ''CommonMinimumMatch)
416+$(derive makeArbitrary ''BoostingQuery)
417+$(derive makeArbitrary ''BoolQuery)
418+$(derive makeArbitrary ''MatchQuery)
419+$(derive makeArbitrary ''MultiMatchQueryType)
420+$(derive makeArbitrary ''BooleanOperator)
421+$(derive makeArbitrary ''ZeroTermsQuery)
422+$(derive makeArbitrary ''MatchQueryType)
423+$(derive makeArbitrary ''IndexAliasCreate)
424+$(derive makeArbitrary ''SearchAliasRouting)
425+$(derive makeArbitrary ''ScoreType)
426+$(derive makeArbitrary ''Distance)
427+$(derive makeArbitrary ''DistanceUnit)
428+$(derive makeArbitrary ''DistanceType)
429+$(derive makeArbitrary ''OptimizeBbox)
430+$(derive makeArbitrary ''GeoBoundingBoxConstraint)
431+$(derive makeArbitrary ''GeoFilterType)
432+$(derive makeArbitrary ''GeoBoundingBox)
433+$(derive makeArbitrary ''LatLon)
434+$(derive makeArbitrary ''RangeExecution)
435+$(derive makeArbitrary ''RegexpFlags)
436+$(derive makeArbitrary ''RegexpFlag)
437+$(derive makeArbitrary ''BoolMatch)
438+$(derive makeArbitrary ''Term)
439+
440+
330441main :: IO ()
331442main = hspec $ do
332443
@@ -869,3 +980,100 @@ main = hspec $ do
869980 regular_search `shouldBe` Right exampleTweet -- Check that the size restrtiction is being honored
870981 liftIO $
871982 scan_search `shouldMatchList` [Just exampleTweet, Just otherTweet]
983+
984+ describe "JSON instances" $ do
985+ propJSON (Proxy :: Proxy Version)
986+ propJSON (Proxy :: Proxy IndexName)
987+ propJSON (Proxy :: Proxy MappingName)
988+ propJSON (Proxy :: Proxy DocId)
989+ propJSON (Proxy :: Proxy IndexAliasRouting)
990+ propJSON (Proxy :: Proxy RoutingValue)
991+ propJSON (Proxy :: Proxy ShardCount)
992+ propJSON (Proxy :: Proxy ReplicaCount)
993+ propJSON (Proxy :: Proxy TemplateName)
994+ propJSON (Proxy :: Proxy TemplatePattern)
995+ propJSON (Proxy :: Proxy QueryString)
996+ propJSON (Proxy :: Proxy FieldName)
997+ propJSON (Proxy :: Proxy CacheName)
998+ propJSON (Proxy :: Proxy CacheKey)
999+ propJSON (Proxy :: Proxy Existence)
1000+ propJSON (Proxy :: Proxy CutoffFrequency)
1001+ propJSON (Proxy :: Proxy Analyzer)
1002+ propJSON (Proxy :: Proxy MaxExpansions)
1003+ propJSON (Proxy :: Proxy Lenient)
1004+ propJSON (Proxy :: Proxy Tiebreaker)
1005+ propJSON (Proxy :: Proxy Boost)
1006+ propJSON (Proxy :: Proxy BoostTerms)
1007+ propJSON (Proxy :: Proxy MaxExpansions)
1008+ propJSON (Proxy :: Proxy MinimumMatch)
1009+ propJSON (Proxy :: Proxy DisableCoord)
1010+ propJSON (Proxy :: Proxy IgnoreTermFrequency)
1011+ propJSON (Proxy :: Proxy MinimumTermFrequency)
1012+ propJSON (Proxy :: Proxy MaxQueryTerms)
1013+ propJSON (Proxy :: Proxy Fuzziness)
1014+ propJSON (Proxy :: Proxy PrefixLength)
1015+ propJSON (Proxy :: Proxy TypeName)
1016+ propJSON (Proxy :: Proxy PercentMatch)
1017+ propJSON (Proxy :: Proxy StopWord)
1018+ propJSON (Proxy :: Proxy QueryPath)
1019+ propJSON (Proxy :: Proxy AllowLeadingWildcard)
1020+ propJSON (Proxy :: Proxy LowercaseExpanded)
1021+ propJSON (Proxy :: Proxy EnablePositionIncrements)
1022+ propJSON (Proxy :: Proxy AnalyzeWildcard)
1023+ propJSON (Proxy :: Proxy GeneratePhraseQueries)
1024+ propJSON (Proxy :: Proxy Locale)
1025+ propJSON (Proxy :: Proxy MaxWordLength)
1026+ propJSON (Proxy :: Proxy MinWordLength)
1027+ propJSON (Proxy :: Proxy PhraseSlop)
1028+ propJSON (Proxy :: Proxy MinDocFrequency)
1029+ propJSON (Proxy :: Proxy MaxDocFrequency)
1030+ propJSON (Proxy :: Proxy Filter)
1031+ propJSON (Proxy :: Proxy Query)
1032+ propJSON (Proxy :: Proxy SimpleQueryStringQuery)
1033+ propJSON (Proxy :: Proxy FieldOrFields)
1034+ propJSON (Proxy :: Proxy SimpleQueryFlag)
1035+ propJSON (Proxy :: Proxy RegexpQuery)
1036+ propJSON (Proxy :: Proxy QueryStringQuery)
1037+ propJSON (Proxy :: Proxy RangeQuery)
1038+ propJSON (Proxy :: Proxy PrefixQuery)
1039+ propJSON (Proxy :: Proxy NestedQuery)
1040+ propJSON (Proxy :: Proxy MoreLikeThisFieldQuery)
1041+ propJSON (Proxy :: Proxy MoreLikeThisQuery)
1042+ propJSON (Proxy :: Proxy IndicesQuery)
1043+ propJSON (Proxy :: Proxy HasParentQuery)
1044+ propJSON (Proxy :: Proxy HasChildQuery)
1045+ propJSON (Proxy :: Proxy FuzzyQuery)
1046+ propJSON (Proxy :: Proxy FuzzyLikeFieldQuery)
1047+ propJSON (Proxy :: Proxy FuzzyLikeThisQuery)
1048+ propJSON (Proxy :: Proxy FilteredQuery)
1049+ propJSON (Proxy :: Proxy DisMaxQuery)
1050+ propJSON (Proxy :: Proxy CommonTermsQuery)
1051+ propJSON (Proxy :: Proxy CommonMinimumMatch)
1052+ propJSON (Proxy :: Proxy BoostingQuery)
1053+ propJSON (Proxy :: Proxy BoolQuery)
1054+ propJSON (Proxy :: Proxy MatchQuery)
1055+ propJSON (Proxy :: Proxy MultiMatchQueryType)
1056+ propJSON (Proxy :: Proxy BooleanOperator)
1057+ propJSON (Proxy :: Proxy ZeroTermsQuery)
1058+ propJSON (Proxy :: Proxy MatchQueryType)
1059+ propJSON (Proxy :: Proxy AliasRouting)
1060+ propJSON (Proxy :: Proxy IndexAliasCreate)
1061+ propJSON (Proxy :: Proxy SearchAliasRouting)
1062+ propJSON (Proxy :: Proxy ScoreType)
1063+ propJSON (Proxy :: Proxy Distance)
1064+ propJSON (Proxy :: Proxy DistanceUnit)
1065+ propJSON (Proxy :: Proxy DistanceType)
1066+ propJSON (Proxy :: Proxy OptimizeBbox)
1067+ propJSON (Proxy :: Proxy GeoBoundingBoxConstraint)
1068+ propJSON (Proxy :: Proxy GeoFilterType)
1069+ propJSON (Proxy :: Proxy GeoBoundingBox)
1070+ propJSON (Proxy :: Proxy LatLon)
1071+ propJSON (Proxy :: Proxy RangeExecution)
1072+ prop "RegexpFlags FromJSON/ToJSON roundtrips, removing dups " $ \rfs ->
1073+ let expected = case rfs of
1074+ SomeRegexpFlags fs -> SomeRegexpFlags (NE.fromList (nub (NE.toList fs)))
1075+ x -> x
1076+ in parseEither parseJSON (toJSON rfs) === Right expected
1077+ propJSON (Proxy :: Proxy BoolMatch)
1078+ propJSON (Proxy :: Proxy Term)
1079+ propJSON (Proxy :: Proxy MultiMatchQuery)