packages feed

net-spider-0.4.2.0: test/ServerTest/Attributes.hs

{-# LANGUAGE OverloadedStrings #-}
module ServerTest.Attributes (main,spec) where

import Data.Aeson (ToJSON)
import Data.List (sort)
import Data.Hashable (Hashable)
import Data.Greskell
  ( FromGraphSON,
    Key
  )
import Data.Text (Text)
import Data.Time.LocalTime (TimeZone(..))
import Test.Hspec

import TestCommon (AText(..), AInt(..))
import ServerTest.ServerCommon (withServer, withSpider', withSpider)

import NetSpider.Found (FoundNode(..), FoundLink(..), LinkState(..))
import NetSpider.Graph (NodeAttributes(..), LinkAttributes(..), VNode)
import NetSpider.Spider
  ( addFoundNode, getSnapshotSimple
  )
import NetSpider.Spider.Config (Host, Port, Config(..), defConfig)
import NetSpider.Snapshot (nodeTimestamp, linkTimestamp)
import qualified NetSpider.Snapshot as S (nodeAttributes, linkAttributes)
import NetSpider.Timestamp (Timestamp(..), fromS)

main :: IO ()
main = hspec spec


typeTestCase :: (FromGraphSON n, ToJSON n, Ord n, Hashable n, Show n, NodeAttributes na, Eq na, Show na, LinkAttributes la, Eq la, Show la)
             => String
             -> Config n na la
             -> n
             -> n
             -> na
             -> la
             -> SpecWith (Host,Port)
typeTestCase test_label conf n1_id n2_id node_attrs link_attrs =
  specify test_label $ withSpider' conf $ \spider -> do
    let n1 = FoundNode { subjectNode = n1_id,
                         foundAt = fromS "2018-10-11T11:00:00",
                         neighborLinks = return link1,
                         nodeAttributes = node_attrs
                       }
        link1 = FoundLink { targetNode = n2_id,
                            linkState = LinkToSubject,
                            linkAttributes = link_attrs
                          }
    addFoundNode spider n1
    (got_ns, got_ls) <- getSnapshotSimple spider n1_id
    let (got_n1, got_n2, got_l) = case (sort got_ns, sort got_ls) of
          ([a,b], [c]) -> (a,b,c)
          _ -> error ("Unexpected pattern: got = " ++ show (got_ns, got_ls))
    S.nodeAttributes got_n1 `shouldBe` Just node_attrs
    S.nodeAttributes got_n2 `shouldBe` Nothing
    S.linkAttributes got_l `shouldBe` link_attrs

attributeTestCase :: (NodeAttributes na, Eq na, Show na, LinkAttributes la, Eq la, Show la)
                  => String
                  -> na
                  -> la
                  -> SpecWith (Host,Port)
attributeTestCase type_label na la = typeTestCase (type_label ++ " attributes") defConfig
                                     ("n1" :: Text) ("n2" :: Text) na la

nodeIdTestCase :: (FromGraphSON n, ToJSON n, Ord n, Hashable n, Show n)
               => String
               -> Key VNode n
               -> n -> n -> SpecWith (Host,Port)
nodeIdTestCase label node_id_key n1 n2 = typeTestCase (label ++ " nodeID") conf n1 n2 () ()
  where
    conf = defConfig { nodeIdKey = node_id_key
                     }

timestampTestCase :: String -> Timestamp -> SpecWith (Host, Port)
timestampTestCase label ts = specify label $ withSpider $ \spider -> do
  let fn :: FoundNode Text () ()
      fn = FoundNode
           { subjectNode = "n1",
             foundAt = ts,
             nodeAttributes = (),
             neighborLinks = return $ FoundLink
                             { targetNode = "n2",
                               linkState = LinkToTarget,
                               linkAttributes = ()
                             }
           }
  addFoundNode spider fn
  (got_ns, got_ls) <- getSnapshotSimple spider "n1"
  let (got_n1, got_n2, got_l) = case (sort got_ns, sort got_ls) of
        ([a,b], [c]) -> (a,b,c)
        _ -> error ("Unexpected pattern: got = " ++ show (got_ns, got_ls))
  nodeTimestamp got_n1 `shouldBe` Just ts
  nodeTimestamp got_n2 `shouldBe` Nothing
  linkTimestamp got_l `shouldBe` ts

spec :: Spec
spec = withServer $ do
  describe "node and link attributes" $ do
    attributeTestCase "Text" (AText "node attrs") (AText "link attrs")
    attributeTestCase "Int" (AInt 128) (AInt 64)
  describe "nodeId" $ do
    nodeIdTestCase "Text" "@node_id_text" ("n1" :: Text) ("n2" :: Text)
    nodeIdTestCase "Int" "@node_id_int" (100 :: Int) (255 :: Int)
    -- We need to use different property keys for different NodeID
    -- types, because the graph database (at least JanusGraph)
    -- automatically fixes the schema (internal data type) for a
    -- property key when some data is given for it for the first time.
  describe "timestamp with timezone" $ do
    timestampTestCase "positive timezone"
      $ Timestamp { epochTime = 200,
                    timeZone = Just $ TimeZone
                               { timeZoneMinutes = 9*60,
                                 timeZoneSummerOnly = False,
                                 timeZoneName = "Asia/Tokyo"
                               }
                  }
    timestampTestCase "negative timezone"
      $ Timestamp { epochTime = 150,
                    timeZone = Just $ TimeZone
                               { timeZoneMinutes = (-5)*60,
                                 timeZoneSummerOnly = True,
                                 timeZoneName = "America/Chicago"
                               }
                  }