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 }