packages feed

factual-api-0.2.0: test/Tests.hs

import Test.HUnit
import Network.Factual.API
import Data.Factual.Query
import Data.Factual.Query.ReadQuery
import Data.Factual.Query.SchemaQuery
import Data.Factual.Query.ResolveQuery
import Data.Factual.Query.GeocodeQuery
import Data.Factual.Response
import qualified Data.Map as M
import qualified Data.Factual.Write as W
import qualified Data.Factual.Query.CrosswalkQuery as C
import qualified Data.Factual.Query.FacetsQuery as F
import qualified Data.Factual.Query.GeopulseQuery as G
import qualified Data.Factual.Write.Submit as S
import qualified Data.Factual.Write.Flag as L

runUnitTests = runTestTT unitTests

runIntegrationTests key secret = runTestTT $ integrationTests key secret

unitTests = TestList [ TestLabel "Place table test" placeTablePathTest
                     , TestLabel "Restaurants table test" restaurantsTablePathTest
                     , TestLabel "Global table test" globalTablePathTest
                     , TestLabel "Custom table test" customTablePathTest
                     , TestLabel "Geopulse test" geopulsePathTest
                     , TestLabel "Geocode test" geocodePathTest
                     , TestLabel "And search test" andSearchPathTest
                     , TestLabel "Or search test" orSearchPathTest
                     , TestLabel "Select test" selectPathTest
                     , TestLabel "Limit test" limitPathTest
                     , TestLabel "Offset test" offsetPathTest
                     , TestLabel "Equal number filter test" equalNumFilterTest
                     , TestLabel "Equal string filter test" equalStrFilterTest
                     , TestLabel "Not equal number filter test" notEqualNumFilterTest
                     , TestLabel "Not equal string filter test" notEqualStrFilterTest
                     , TestLabel "In number list filter test" inNumListFilterTest
                     , TestLabel "In string list filter test" inStrListFilterTest
                     , TestLabel "Not in number list filter test" notInNumListFilterTest
                     , TestLabel "Not in string list filter test" notInStrListFilterTest
                     , TestLabel "Begins with filter test" beginsWithFilterTest
                     , TestLabel "Not begins with filter test" notBeginsWithFilterTest
                     , TestLabel "Begins with any filter test" beginsWithAnyFilterTest
                     , TestLabel "Not begins with any filter test" notBeginsWithAnyFilterTest
                     , TestLabel "Is blank filter test" isBlankFilterTest
                     , TestLabel "Is not blank filter test" isNotBlankFilterTest
                     , TestLabel "And filter test" andFilterTest
                     , TestLabel "Or filter test" andFilterTest
                     , TestLabel "Geo test" geoTest
                     , TestLabel "Include count test" includeCountTest
                     , TestLabel "Schema query test" schemaQueryTest
                     , TestLabel "Resolve query test" resolveQueryTest
                     , TestLabel "Factual ID test" factualIdTest
                     , TestLabel "Crosswalk limit test" limitCWPathTest
                     , TestLabel "Namespace test" namespaceTest
                     , TestLabel "Namespace ID test" namespaceIdTest
                     , TestLabel "Only test" onlyTest
                     , TestLabel "Facets test" facetsTest
                     , TestLabel "Submit path test" submitPathTest
                     , TestLabel "Submit body test" submitBodyTest
                     , TestLabel "Flag path test" flagPathTest
                     , TestLabel "Flag body test" flagBodyTest ]

integrationTests key secret = TestList [ TestLabel "Read test" (readIntegrationTest token)
                                       , TestLabel "Schema test" (schemaIntegrationTest token)
                                       , TestLabel "Resolve test" (resolveIntegrationTest token)
                                       , TestLabel "Crosswalk test" (crosswalkIntegrationTest token)
                                       , TestLabel "Raw read test" (rawIntegrationTest token)
                                       , TestLabel "Facets test" (facetsIntegrationTest token)
                                       , TestLabel "Geopulse test" (geopulseIntegrationTest token)
                                       , TestLabel "Geocode test" (geocodeIntegrationTest token) ]
                            where token = generateToken key secret

placeTablePathTest = TestCase (do
  let expected = "/t/places/read?include_count=false"
  let path = toPath $ blankReadQuery { table = Places }
  assertEqual "Correct path for places table" expected path)

restaurantsTablePathTest = TestCase (do
  let expected = "/t/restaurants-us/read?include_count=false"
  let path = toPath $ blankReadQuery { table = USRestaurants }
  assertEqual "Correct path for us restaurants table" expected path)

globalTablePathTest = TestCase (do
  let expected = "/t/global/read?include_count=false"
  let path = toPath $ blankReadQuery { table = Global }
  assertEqual "Correct path for global table" expected path)

customTablePathTest = TestCase (do
  let expected = "/t/foo/read?include_count=false"
  let path = toPath $ blankReadQuery { table = Custom "foo" }
  assertEqual "Correct path for custom table" expected path)

geopulsePathTest = TestCase (do
  let expected = "/places/geopulse?geo={\"$point\":[34.06021,-118.41828]}&select=commercial_density"
  let path = toPath $ G.GeopulseQuery { G.geo = Point 34.06021 (-118.41828) , G.select = ["commercial_density"] }
  assertEqual "Correct path for geopulse" expected path)

geocodePathTest = TestCase (do
  let expected = "/places/geocode?geo={\"$point\":[34.06021,-118.41828]}"
  let path = toPath $ GeocodeQuery $ Point 34.06021 (-118.41828)
  assertEqual "Correct path for geocode" expected path)

andSearchPathTest = TestCase (do
  let expected = "/t/places/read?q=foo bar&include_count=false"
  let path = toPath $ blankReadQuery { search = AndSearch ["foo", "bar"] }
  assertEqual "Correct path for ANDed search" expected path)

orSearchPathTest = TestCase (do
  let expected = "/t/places/read?q=foo,bar&include_count=false"
  let path = toPath $ blankReadQuery { search = OrSearch ["foo", "bar"] }
  assertEqual "Correct path for ANDed search" expected path)

selectPathTest = TestCase (do
  let expected = "/t/places/read?select=foo,bar&include_count=false"
  let path = toPath $ blankReadQuery { select = ["foo", "bar"] }
  assertEqual "Correct path for select terms" expected path)

limitPathTest = TestCase (do
  let expected = "/t/places/read?limit=321&include_count=false"
  let path = toPath $ blankReadQuery { limit = Just 321 }
  assertEqual "Correct path for limit" expected path)

offsetPathTest = TestCase (do
  let expected = "/t/places/read?offset=321&include_count=false"
  let path = toPath $ blankReadQuery { offset = Just 321 }
  assertEqual "Correct path for offset" expected path)

equalNumFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":123.4}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [EqualNum "field" 123.4] }
  assertEqual "Correct path for equal number filter" expected path)

equalStrFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":\"value\"}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [EqualStr "field" "value"] }
  assertEqual "Correct path for equal string filter" expected path)

notEqualNumFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$neq\":123.4}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [NotEqualNum "field" 123.4] }
  assertEqual "Correct path for not equal number filter" expected path)

notEqualStrFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$neq\":\"value\"}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [NotEqualStr "field" "value"] }
  assertEqual "Correct path for not equal string filter" expected path)

inNumListFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$in\":[123.4,5432.1]}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [InNumList "field" [123.4, 5432.1]] }
  assertEqual "Correct path for in number list filter" expected path)

inStrListFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$in\":[\"value\",\"other\"]}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [InStrList "field" ["value","other"]] }
  assertEqual "Correct path for in string list filter" expected path)

notInNumListFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$nin\":[123.4,5432.1]}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [NotInNumList "field" [123.4, 5432.1]] }
  assertEqual "Correct path for not in number list filter" expected path)

notInStrListFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$nin\":[\"value\",\"other\"]}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [NotInStrList "field" ["value","other"]] }
  assertEqual "Correct path for not in string list filter" expected path)

beginsWithFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$bw\":\"val\"}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [BeginsWith "field" "val"] }
  assertEqual "Correct path for begins with filter" expected path)

notBeginsWithFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$nbw\":\"val\"}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [NotBeginsWith "field" "val"] }
  assertEqual "Correct path for not begins with filter" expected path)

beginsWithAnyFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$bwin\":[\"val\",\"ot\"]}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [BeginsWithAny "field" ["val","ot"]] }
  assertEqual "Correct path for begins with any filter" expected path)

notBeginsWithAnyFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$nbwin\":[\"val\",\"ot\"]}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [NotBeginsWithAny "field" ["val","ot"]] }
  assertEqual "Correct path for not begins with any filter" expected path)

isBlankFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$blank\":true}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [IsBlank "field"] }
  assertEqual "Correct path for is blank filter" expected path)

isNotBlankFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"field\":{\"$blank\":false}}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [IsNotBlank "field"] }
  assertEqual "Correct path for is not blank filter" expected path)

andFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"$and\":[{\"field1\":{\"$blank\":true}},{\"field2\":{\"$blank\":false}}]}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [And [IsBlank "field1", IsNotBlank "field2"]] }
  assertEqual "Correct path for and filter" expected path)

orFilterTest = TestCase (do
  let expected = "/t/places/read?filters={\"$or\":[{\"field1\":{\"$blank\":true}},{\"field2\":{\"$blank\":false}}]}&include_count=false"
  let path = toPath $ blankReadQuery { filters = [Or [IsBlank "field1", IsNotBlank "field2"]] }
  assertEqual "Correct path for or filter" expected path)

geoTest = TestCase (do
  let expected = "/t/places/read?geo={\"$circle\":{\"$center\":[300.1, 200.3],\"$meters\":100.5}}&include_count=false"
  let path = toPath $ blankReadQuery { geo = Just (Circle 300.1 200.3 100.5) }
  assertEqual "Correct path for geo" expected path)

includeCountTest = TestCase (do
  let expected = "/t/places/read?include_count=true"
  let path = toPath $ blankReadQuery { includeCount = True }
  assertEqual "Correct path for include count" expected path)

schemaQueryTest = TestCase (do
  let expected = "/t/places/schema"
  let path = toPath $ SchemaQuery Places
  assertEqual "Correct path for a schema query" expected path)

resolveQueryTest = TestCase (do
  let expected = "/places/resolve?values={\"field1\":\"value1\",\"field2\":32.1}"
  let path = toPath $ ResolveQuery [ResolveStr "field1" "value1", ResolveNum "field2" 32.1]
  assertEqual "Correct path for a resolve query" expected path)

factualIdTest = TestCase (do
  let expected = "/places/crosswalk?factual_id=1234"
  let path = toPath $ blankCrosswalkQuery { C.factualId = Just "1234" }
  assertEqual "Correct path for a factual id" expected path)

limitCWPathTest = TestCase (do
  let expected = "/places/crosswalk?limit=1234"
  let path = toPath $ blankCrosswalkQuery { C.limit = Just 1234 }
  assertEqual "Correct path for a limit in a crosswalk query" expected path)

namespaceTest = TestCase (do
  let expected = "/places/crosswalk?namespace=yelp"
  let path = toPath $ blankCrosswalkQuery { C.namespace = Just "yelp" }
  assertEqual "Correct path for a namespace" expected path)

namespaceIdTest = TestCase (do
  let expected = "/places/crosswalk?namespace_id=5432"
  let path = toPath $ blankCrosswalkQuery { C.namespaceId = Just "5432" }
  assertEqual "Correct path for a namespace id" expected path)

onlyTest = TestCase (do
  let expected = "/places/crosswalk?only=yelp,loopd"
  let path = toPath $ blankCrosswalkQuery { C.only = ["yelp", "loopd"] }
  assertEqual "Correct path for a only" expected path)

facetsTest = TestCase (do
  let expected = "/t/places/facets?q=starbucks&select=locality,region&"
               ++ "filters={\"country\":\"US\"}&limit=10&min_count=2&"
               ++ "include_count=false"
  let path = toPath $ F.FacetsQuery { F.table        = Places
                                    , F.search       = AndSearch ["starbucks"]
                                    , F.select       = ["locality", "region"]
                                    , F.filters      = [EqualStr "country" "US"]
                                    , F.geo          = Nothing
                                    , F.limit        = Just 10
                                    , F.minCount     = Just 2
                                    , F.includeCount = False }
  assertEqual "Correct path for a facets query" expected path)

submitPathTest = TestCase (do
  let expected = "/t/places/foobar/submit"
  let path = W.path submitWrite
  assertEqual "Correct path for submit" expected path)

submitBodyTest = TestCase (do
  let expected = "user=user123&values={\"key\":\"val\"}"
  let body = W.body submitWrite
  assertEqual "Correct body for submit" expected body)

flagPathTest = TestCase (do
  let expected = "/t/places/foobar/flag"
  let path = W.path flagWrite
  assertEqual "Correct path for flag" expected path)

flagBodyTest = TestCase (do
  let expected = "problem=Duplicate&user=user123&comment=There was a problem&debug=false"
  let body = W.body flagWrite
  assertEqual "Correct body for flag" expected body)

readIntegrationTest :: Token -> Test
readIntegrationTest token = TestCase (do
  let query = ReadQuery { table = Places
                        , search = AndSearch ["McDonalds", "Burger King"]
                        , select = ["name"]
                        , limit = Just 50
                        , offset = Just 10
                        , includeCount = True
                        , geo = Just (Circle 34.06021 (-118.41828) 5000.0)
                        , filters = [EqualStr "name" "Stand"] }
  result <- makeRequest token query
  assertEqual "Valid read query" "ok" (status result))

schemaIntegrationTest :: Token -> Test
schemaIntegrationTest token = TestCase (do
  let query = SchemaQuery Places
  result <- makeRequest token query
  assertEqual "Valid schema query" "ok" (status result))

resolveIntegrationTest :: Token -> Test
resolveIntegrationTest token = TestCase (do
  let query = ResolveQuery [ResolveStr "name" "McDonalds"]
  result <- makeRequest token query
  assertEqual "Valid resolve query" "ok" (status result))

crosswalkIntegrationTest :: Token -> Test
crosswalkIntegrationTest token = TestCase (do
  let query = C.CrosswalkQuery { C.factualId = Just "97598010-433f-4946-8fd5-4a6dd1639d77"
                               , C.limit = Nothing
                               , C.namespace = Nothing
                               , C.namespaceId = Nothing
                               , C.only = ["loopt"] }
  result <- makeRequest token query
  assertEqual "Valid crosswalk query" "ok" (status result))

rawIntegrationTest :: Token -> Test
rawIntegrationTest token = TestCase (do
  result <- makeRawRequest token "/t/places?q=starbucks"
  assertEqual "Valid read query" "ok" (status result))

facetsIntegrationTest token = TestCase (do
  let query = F.FacetsQuery { F.table        = Places
                            , F.search       = AndSearch ["Starbucks"]
                            , F.select       = ["country"]
                            , F.filters      = []
                            , F.geo          = Nothing
                            , F.limit        = Just 100
                            , F.minCount     = Just 1
                            , F.includeCount = False }
  result <- makeRequest token query
  assertEqual "Valid facets query" "ok" (status result))

geopulseIntegrationTest token = TestCase (do
  let query = G.GeopulseQuery { G.geo    = Point 34.06021 (-118.41828)
                              , G.select = [] }
  result <- makeRequest token query
  assertEqual "Valid geopulse query" "ok" (status result))

geocodeIntegrationTest token = TestCase (do
  let query = GeocodeQuery $ Point 34.06021 (-118.41828)
  result <- makeRequest token query
  assertEqual "Valid geopulse query" "ok" (status result))

blankReadQuery :: ReadQuery
blankReadQuery = ReadQuery { table = Places
                           , search = AndSearch []
                           , select = []
                           , limit = Nothing
                           , offset = Nothing
                           , filters = []
                           , geo = Nothing
                           , includeCount = False }

blankCrosswalkQuery :: C.CrosswalkQuery
blankCrosswalkQuery = C.CrosswalkQuery { C.factualId = Nothing
                                       , C.limit = Nothing
                                       , C.namespace = Nothing
                                       , C.namespaceId = Nothing
                                       , C.only = [] }

submitWrite :: S.Submit
submitWrite = S.Submit { S.table     = Places
                       , S.user      = "user123"
                       , S.factualId = Just "foobar"
                       , S.values    = M.fromList [("key", "val")] }

flagWrite :: L.Flag
flagWrite = L.Flag { L.table     = Places
                   , L.factualId = "foobar"
                   , L.problem   = L.Duplicate
                   , L.user      = "user123"
                   , L.comment   = Just "There was a problem"
                   , L.debug     = False
                   , L.reference = Nothing }