packages feed

hwormhole-0.1.0.0: tests/MessagesTests.hs

{-# LANGUAGE OverloadedStrings #-}
module MessagesTests
  ( tests
  , messagesRoundTripTests
  )
where

import Protolude

import qualified Data.Set as Set

import Transit.Internal.Messages

import Test.Hspec
import Data.Aeson
  ( encode
  , decode
  , eitherDecode
  )
import Hedgehog (forAll, property, Property, Group(..), checkSequential, tripping)
import qualified Generator

tests :: IO ()
tests = hspec $ do
  describe "Ability tests" $ do
    it "encode Ability" $ do
      encode DirectTcpV1 `shouldBe` "\"direct-tcp-v1\""
      encode RelayV1 `shouldBe` "\"relay-v1\""
    it "decode Ability" $ do
      let s1 = "\"direct-tcp-v1\"" :: Text
      decode (toS s1) `shouldBe` Just DirectTcpV1
      let s2 = "\"relay-v1\"" :: Text
      decode (toS s2) `shouldBe` Just RelayV1
  describe "Hint tests" $ do
    it "encode Hint" $ do
      let h1 = Hint { ctype = DirectTcpV1
                    , priority = 0.5
                    , hostname = "foo.bar.baz"
                    , port = 1234 }
      encode h1 `shouldBe` "{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234}"
    it "decode Hint" $ do
      let h1 = "{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234}" :: Text
      decode (toS h1) `shouldBe` Just Hint { ctype = DirectTcpV1
                                           , priority = 0.5
                                           , hostname = "foo.bar.baz"
                                           , port = 1234 }
  describe "ConnectionHint tests" $ do
    it "encode Direct ConnectionHint" $ do
      let h1 = Hint { ctype = DirectTcpV1
                    , priority = 0.5
                    , hostname = "foo.bar.baz"
                    , port = 1234 }
          ch1 = Direct h1
      encode ch1 `shouldBe` "{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234}"
    it "encode Relay ConnectionHint" $ do
      let h1 = Hint { ctype = DirectTcpV1
                    , priority = 0.5
                    , hostname = "foo.bar.baz"
                    , port = 1234 }
          ch1 = Relay { rtype = RelayV1
                      , hints = [h1] }
      encode ch1 `shouldBe` "{\"hints\":[{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234}],\"type\":\"relay-v1\"}"
    it "decode Direct ConnectionHint" $ do
      let h1text = "{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234}" :: Text
      decode (toS h1text) `shouldBe`  Just (Direct Hint { ctype = DirectTcpV1
                                                        , priority = 0.5
                                                        , hostname = "foo.bar.baz"
                                                        , port = 1234 })
    it "decode Relay ConnectionHint" $ do
      let h1text = "{\"type\": \"relay-v1\", \"hints\": [{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234}]}" :: Text
          h1 = Hint { ctype = DirectTcpV1
                    , priority = 0.5
                    , hostname = "foo.bar.baz"
                    , port = 1234 }
      decode (toS h1text) `shouldBe` Just Relay { rtype = RelayV1
                                                , hints = [h1] }

    it "encode and decode Transit type" $ do
      let t1 = Transit { abilitiesV1 = [Ability DirectTcpV1, Ability RelayV1]
                       , hintsV1 = Set.fromList [ch1, ch2] }
          h1 = Hint { ctype = DirectTcpV1
                    , priority = 0.5
                    , hostname = "foo.bar.baz"
                    , port = 1234 }
          ch1 = Direct h1
          h2 = Hint { ctype = DirectTcpV1
                    , priority = 0.5
                    , hostname = "foo.bar.baz"
                    , port = 1234 }
          ch2 = Relay { rtype = RelayV1
                      , hints = [h2] }
          h3 = Hint { ctype = DirectTcpV1
                    , priority = 0.0
                    , hostname = "192.168.1.106"
                    , port = 36097 }
          ch3 = Direct h3
          h4 = Hint { ctype = DirectTcpV1
                    , priority = 0.0
                    , hostname = "transit.magic-wormhole.io"
                    , port = 4001 }
          ch4 = Relay { rtype = RelayV1
                      , hints = [h4] }
          t2 = Transit { abilitiesV1 = [Ability DirectTcpV1, Ability RelayV1]
                       , hintsV1 = Set.fromList [ch3 ,ch4] }
          t1text = "{\"transit\":{\"hints-v1\":[{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234},{\"hints\":[{\"hostname\":\"foo.bar.baz\",\"priority\":0.5,\"type\":\"direct-tcp-v1\",\"port\":1234}],\"type\":\"relay-v1\"}],\"abilities-v1\":[{\"type\":\"direct-tcp-v1\"},{\"type\":\"relay-v1\"}]}}" :: Text
          t2text = "{\"transit\": {\"abilities-v1\": [{\"type\": \"direct-tcp-v1\"}, {\"type\": \"relay-v1\"}], \"hints-v1\": [{\"priority\": 0.0, \"hostname\": \"192.168.1.106\", \"type\": \"direct-tcp-v1\", \"port\": 36097}, {\"type\": \"relay-v1\", \"hints\": [{\"priority\": 0.0, \"hostname\": \"transit.magic-wormhole.io\", \"type\": \"direct-tcp-v1\", \"port\": 4001}]}]}}" :: ByteString
      encode t1 `shouldBe` toS t1text
      decode (encode t1) `shouldBe` Just t1
      decode (toS t2text) `shouldBe` Just t2

  describe "Ack message tests" $ do
    it "encode and decode FileAck responses" $ do
      let f1 = FileAck "ok"
      encode f1 `shouldBe` "{\"file_ack\":\"ok\"}"
      decode (encode f1) `shouldBe` Just f1

  describe "Response message tests" $ do
    it "encode and decode Error response" $ do
      let r1 = Error "transfer rejected"
      encode r1 `shouldBe` "{\"error\":\"transfer rejected\"}"
      decode (encode r1) `shouldBe` Just r1
    it "encode and decode answer response" $ do
      let a1 = Answer (FileAck "ok")
      encode a1 `shouldBe` "{\"answer\":{\"file_ack\":\"ok\"}}"
      decode (encode a1) `shouldBe` Just a1
      
  describe "Transit Ack tests" $ do
    it "encode and decode Transit Ack from receiver to sender" $ do
      let a1 = TransitAck "ok" "e4f1684a5375ebf7f1dcde02a66026f937a8c6195adf31813ef21b3ccadfb11f"
      encode a1 `shouldBe` "{\"ack\":\"ok\",\"sha256\":\"e4f1684a5375ebf7f1dcde02a66026f937a8c6195adf31813ef21b3ccadfb11f\"}"
      decode (encode a1) `shouldBe` Just a1

prop_abilityTrip :: Property
prop_abilityTrip = property $ do
  x <- forAll Generator.abilityGen
  tripping x encode eitherDecode

prop_hintTrip :: Property
prop_hintTrip = property $ do
  x <- forAll Generator.hintGen
  tripping x encode eitherDecode

prop_connectionHintTrip :: Property
prop_connectionHintTrip = property $ do
  x <- forAll Generator.connectionHintGen
  tripping x encode eitherDecode

prop_ackTrip :: Property
prop_ackTrip = property $ do
  x <- forAll Generator.ackGen
  tripping x encode eitherDecode

prop_transitMsgTrip :: Property
prop_transitMsgTrip = property $ do
  x <- forAll Generator.transitMsgGen
  tripping x encode eitherDecode

prop_transitAckTrip :: Property
prop_transitAckTrip = property $ do
  x <- forAll Generator.transitAckGen
  tripping x encode eitherDecode

messagesRoundTripTests :: IO Bool
messagesRoundTripTests =
  checkSequential $ Group "Messages"
  [ ("prop_abilityTrip", prop_abilityTrip)
  , ("prop_hintTrip", prop_hintTrip)
  , ("prop_connectionHintTrip", prop_connectionHintTrip)
  , ("prop_ackTrip", prop_ackTrip)
  , ("prop_transitMsgTrip", prop_transitMsgTrip)
  , ("prop_transitAckTrip", prop_transitAckTrip)
  ]