1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE TemplateHaskell #-}
4module Test.Common where
8import qualified Data.Map as M
9import qualified Data.SemVer as SemVer
10import qualified Network.HTTP.Types.Status as NHTS
13testServer = Server "http://localhost:9200"
15testIndex = IndexName "bloodhound-tests-twitter-1"
16testMapping :: MappingName
17testMapping = MappingName "tweet"
19withTestEnv :: BH IO a -> IO a
20withTestEnv = withBH defaultManagerSettings testServer
22data Location = Location { lat :: Double
23 , lon :: Double } deriving (Eq, Show)
25data Tweet = Tweet { user :: Text
29 , location :: Location
30 , extra :: Maybe Text }
33$(deriveJSON defaultOptions ''Location)
34$(deriveJSON defaultOptions ''Tweet)
36data ParentMapping = ParentMapping deriving (Eq, Show)
38instance ToJSON ParentMapping where
39 toJSON ParentMapping =
40 object ["properties" .=
41 object [ "user" .= object ["type" .= ("string" :: Text)
44 -- Serializing the date as a date is breaking other tests, mysteriously.
45 -- , "postDate" .= object [ "type" .= ("date" :: Text)
46 -- , "format" .= ("YYYY-MM-dd`T`HH:mm:ss.SSSZZ" :: Text)]
47 , "message" .= object ["type" .= ("string" :: Text)]
48 , "age" .= object ["type" .= ("integer" :: Text)]
49 , "location" .= object ["type" .= ("geo_point" :: Text)]
50 , "extra" .= object ["type" .= ("keyword" :: Text)]
54es11 = SemVer.version 1 1 0 [] []
57es13 = SemVer.version 1 3 0 [] []
60es12 = SemVer.version 1 2 0 [] []
63es14 = SemVer.version 1 4 0 [] []
66es15 = SemVer.version 1 5 0 [] []
69es16 = SemVer.version 1 6 0 [] []
72es20 = SemVer.version 2 0 0 [] []
75es50 = SemVer.version 5 0 0 [] []
77getServerVersion :: IO (Maybe SemVer.Version)
78getServerVersion = fmap extractVersion <$> withTestEnv getStatus
80 extractVersion = versionNumber . number . version
82createExampleIndex :: (MonadBH m) => m Reply
84 createIndex (IndexSettings (ShardCount 1) (ReplicaCount 0)) testIndex
86deleteExampleIndex :: (MonadBH m) => m Reply
90validateStatus :: Show body => Response body -> Int -> Expectation
91validateStatus resp expected =
94 else expectationFailure ("Expected " <> show expected <> " but got " <> show actual <> ": " <> show body)
96 actual = NHTS.statusCode (responseStatus resp)
97 body = responseBody resp
99data ChildMapping = ChildMapping deriving (Eq, Show)
101instance ToJSON ChildMapping where
102 toJSON ChildMapping =
103 object ["_parent" .= object ["type" .= ("parent" :: Text)]
105 object [ "user" .= object ["type" .= ("string" :: Text)
106 , "fielddata" .= True
108 -- Serializing the date as a date is breaking other tests, mysteriously.
109 -- , "postDate" .= object [ "type" .= ("date" :: Text)
110 -- , "format" .= ("YYYY-MM-dd`T`HH:mm:ss.SSSZZ" :: Text)]
111 , "message" .= object ["type" .= ("string" :: Text)]
112 , "age" .= object ["type" .= ("integer" :: Text)]
113 , "location" .= object ["type" .= ("geo_point" :: Text)]
114 , "extra" .= object ["type" .= ("keyword" :: Text)]
117data TweetMapping = TweetMapping deriving (Eq, Show)
119instance ToJSON TweetMapping where
120 toJSON TweetMapping =
122 object ["properties" .=
123 object [ "user" .= object [ "type" .= ("string" :: Text)
124 , "fielddata" .= True
126 -- Serializing the date as a date is breaking other tests, mysteriously.
127 -- , "postDate" .= object [ "type" .= ("date" :: Text)
128 -- , "format" .= ("YYYY-MM-dd`T`HH:mm:ss.SSSZZ" :: Text)]
129 , "message" .= object ["type" .= ("string" :: Text)]
130 , "age" .= object ["type" .= ("integer" :: Text)]
131 , "location" .= object ["type" .= ("geo_point" :: Text)]
132 , "extra" .= object ["type" .= ("keyword" :: Text)]
136exampleTweet = Tweet { user = "bitemyapp"
138 (ModifiedJulianDay 55000)
139 (secondsToDiffTime 10)
140 , message = "Use haskell!"
142 , location = Location 40.12 (-71.34)
145tweetWithExtra :: Tweet
146tweetWithExtra = Tweet { user = "bitemyapp"
148 (ModifiedJulianDay 55000)
149 (secondsToDiffTime 10)
150 , message = "Use haskell!"
152 , location = Location 40.12 (-71.34)
153 , extra = Just "blah blah" }
159newUser = "someotherapp"
163 object [ "age" .= newAge
168patchedTweet = exampleTweet{age = newAge, user = newUser}
171otherTweet = Tweet { user = "notmyapp"
173 (ModifiedJulianDay 55000)
174 (secondsToDiffTime 11)
175 , message = "Use haskell!"
177 , location = Location 40.12 (-71.34)
180resetIndex :: BH IO ()
182 _ <- deleteExampleIndex
183 _ <- createExampleIndex
184 _ <- putMapping testIndex testMapping TweetMapping
187insertData :: BH IO Reply
190 insertData' defaultIndexDocumentSettings
192insertData' :: IndexDocumentSettings -> BH IO Reply
194 r <- indexDocument testIndex testMapping ids exampleTweet (DocId "1")
195 _ <- refreshIndex testIndex
198updateData :: BH IO Reply
200 r <- updateDocument testIndex testMapping defaultIndexDocumentSettings tweetPatch (DocId "1")
201 _ <- refreshIndex testIndex
204insertOther :: BH IO ()
206 _ <- indexDocument testIndex testMapping defaultIndexDocumentSettings otherTweet (DocId "2")
207 _ <- refreshIndex testIndex
210insertExtra :: BH IO ()
212 _ <- indexDocument testIndex testMapping defaultIndexDocumentSettings tweetWithExtra (DocId "4")
213 _ <- refreshIndex testIndex
216insertWithSpaceInId :: BH IO ()
217insertWithSpaceInId = do
218 _ <- indexDocument testIndex testMapping defaultIndexDocumentSettings exampleTweet (DocId "Hello World")
219 _ <- refreshIndex testIndex
222searchTweet :: Search -> BH IO (Either EsError Tweet)
223searchTweet search = do
224 result <- searchTweets search
225 let myTweet :: Either EsError Tweet
226 myTweet = grabFirst result
229searchTweets :: Search -> BH IO (Either EsError (SearchResult Tweet))
230searchTweets search = parseEsResponse =<< searchByIndex testIndex search
232searchExpectNoResults :: Search -> BH IO ()
233searchExpectNoResults search = do
234 result <- searchTweets search
235 let emptyHits = fmap (hits . searchHits) result
237 emptyHits `shouldBe` Right []
239searchExpectAggs :: Search -> BH IO ()
240searchExpectAggs search = do
241 reply <- searchByIndex testIndex search
242 let isEmpty x = return (M.null x)
243 let result = decode (responseBody reply) :: Maybe (SearchResult Tweet)
245 (result >>= aggregations >>= isEmpty) `shouldBe` Just False
247searchValidBucketAgg :: (BucketAggregation a, FromJSON a, Show a) =>
248 Search -> Text -> (Text -> AggregationResults -> Maybe (Bucket a)) -> BH IO ()
249searchValidBucketAgg search aggKey extractor = do
250 reply <- searchByIndex testIndex search
251 let bucketDocs = docCount . head . buckets
252 let result = decode (responseBody reply) :: Maybe (SearchResult Tweet)
253 let count = result >>= aggregations >>= extractor aggKey >>= \x -> return (bucketDocs x)
255 count `shouldBe` Just 1
257searchTermsAggHint :: [ExecutionHint] -> BH IO ()
258searchTermsAggHint hints = do
259 let terms hint = TermsAgg $ (mkTermsAggregation "user") { termExecutionHint = Just hint }
260 let search hint = mkAggregateSearch Nothing $ mkAggregations "users" $ terms hint
261 forM_ hints $ searchExpectAggs . search
262 forM_ hints (\x -> searchValidBucketAgg (search x) "users" toTerms)
264searchTweetHighlight :: Search
265 -> BH IO (Either EsError (Maybe HitHighlight))
266searchTweetHighlight search = do
267 result <- searchTweets search
268 let tweetHit :: Either EsError (Maybe (Hit Tweet))
269 tweetHit = fmap (headMay . hits . searchHits) result
270 myHighlight :: Either EsError (Maybe HitHighlight)
271 myHighlight = (join . fmap hitHighlight) <$> tweetHit
274searchExpectSource :: Source -> Either EsError Value -> BH IO ()
275searchExpectSource src expected = do
277 let query = QueryMatchQuery $ mkMatchQuery (FieldName "_all") (QueryString "haskell")
278 let search = (mkSearch (Just query) Nothing) { source = Just src }
279 reply <- searchByIndex testIndex search
280 result <- parseEsResponse reply
281 let value = grabFirst result
283 value `shouldBe` expected
285atleast :: SemVer.Version -> IO Bool
286atleast v = getServerVersion >>= \x -> return $ x >= Just v
288atmost :: SemVer.Version -> IO Bool
289atmost v = getServerVersion >>= \x -> return $ x <= Just v
291is :: SemVer.Version -> IO Bool
292is v = getServerVersion >>= \x -> return $ x == Just v
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