{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}

module Test.Cardano.Base.IP (tests) where

import Cardano.Base.IP
import Control.Exception (evaluate)
import Data.Aeson (Result (..), fromJSON, toJSON)
import Data.Aeson.QQ (aesonQQ)
import Test.Hspec

isError :: Result a -> Bool
isError :: forall a. Result a -> Bool
isError (Error String
_) = Bool
True
isError Result a
_ = Bool
False

tests :: Spec
tests :: Spec
tests = String -> Spec -> Spec
forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
"IP" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
  String -> Spec -> Spec
forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
"IPv4" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Show instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      IPv4 -> String
forall a. Show a => a -> String
show ([Int] -> IPv4
toIPv4 [Int
192, Int
168, Int
1, Int
1]) String -> String -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` String
"\"192.168.1.1\""
      IPv4 -> String
forall a. Show a => a -> String
show ([Int] -> IPv4
toIPv4 [Int
0, Int
0, Int
0, Int
0]) String -> String -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` String
"\"0.0.0.0\""

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Read instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      (String -> IPv4
forall a. Read a => String -> a
read String
"\"192.168.1.1\"" :: IPv4) IPv4 -> IPv4 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv4
toIPv4 [Int
192, Int
168, Int
1, Int
1]
      (String -> IPv4
forall a. Read a => String -> a
read String
"\"0.0.0.0\"" :: IPv4) IPv4 -> IPv4 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv4
toIPv4 [Int
0, Int
0, Int
0, Int
0]

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Read fails on invalid input" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      IPv4 -> IO IPv4
forall a. a -> IO a
evaluate (String -> IPv4
forall a. Read a => String -> a
read String
"\"invalid\"" :: IPv4) IO IPv4 -> Selector SomeException -> IO ()
forall e a.
(HasCallStack, Exception e) =>
IO a -> Selector e -> IO ()
`shouldThrow` Selector SomeException
anyException
      IPv4 -> IO IPv4
forall a. a -> IO a
evaluate (String -> IPv4
forall a. Read a => String -> a
read String
"\"256.0.0.1\"" :: IPv4) IO IPv4 -> Selector SomeException -> IO ()
forall e a.
(HasCallStack, Exception e) =>
IO a -> Selector e -> IO ()
`shouldThrow` Selector SomeException
anyException

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Show/Read roundtrip" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      String -> IPv4
forall a. Read a => String -> a
read (IPv4 -> String
forall a. Show a => a -> String
show ([Int] -> IPv4
toIPv4 [Int
192, Int
168, Int
1, Int
1])) IPv4 -> IPv4 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv4
toIPv4 [Int
192, Int
168, Int
1, Int
1]
      String -> IPv4
forall a. Read a => String -> a
read (IPv4 -> String
forall a. Show a => a -> String
show ([Int] -> IPv4
toIPv4 [Int
0, Int
0, Int
0, Int
0])) IPv4 -> IPv4 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv4
toIPv4 [Int
0, Int
0, Int
0, Int
0]

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"ToJSON instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      IPv4 -> Value
forall a. ToJSON a => a -> Value
toJSON ([Int] -> IPv4
toIPv4 [Int
192, Int
168, Int
1, Int
1]) Value -> Value -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [aesonQQ| "192.168.1.1" |]
      IPv4 -> Value
forall a. ToJSON a => a -> Value
toJSON ([Int] -> IPv4
toIPv4 [Int
0, Int
0, Int
0, Int
0]) Value -> Value -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [aesonQQ| "0.0.0.0" |]

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"FromJSON instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      Value -> Result IPv4
forall a. FromJSON a => Value -> Result a
fromJSON [aesonQQ| "192.168.1.1" |] Result IPv4 -> Result IPv4 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` IPv4 -> Result IPv4
forall a. a -> Result a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Int] -> IPv4
toIPv4 [Int
192, Int
168, Int
1, Int
1])
      Value -> Result IPv4
forall a. FromJSON a => Value -> Result a
fromJSON [aesonQQ| "invalid" |] Result IPv4 -> (Result IPv4 -> Bool) -> IO ()
forall a. (HasCallStack, Show a) => a -> (a -> Bool) -> IO ()
`shouldSatisfy` (forall a. Result a -> Bool
isError @IPv4)
      Value -> Result IPv4
forall a. FromJSON a => Value -> Result a
fromJSON [aesonQQ| 123 |] Result IPv4 -> (Result IPv4 -> Bool) -> IO ()
forall a. (HasCallStack, Show a) => a -> (a -> Bool) -> IO ()
`shouldSatisfy` (forall a. Result a -> Bool
isError @IPv4)

  String -> Spec -> Spec
forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
"IPv6" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Show instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      IPv6 -> String
forall a. Show a => a -> String
show ([Int] -> IPv6
toIPv6 [Int
0x2001, Int
0xdb8, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]) String -> String -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` String
"\"2001:db8::1\""
      IPv6 -> String
forall a. Show a => a -> String
show ([Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0]) String -> String -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` String
"\"::\""
      IPv6 -> String
forall a. Show a => a -> String
show ([Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]) String -> String -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` String
"\"::1\""

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Read instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      (String -> IPv6
forall a. Read a => String -> a
read String
"\"2001:db8::1\"" :: IPv6) IPv6 -> IPv6 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv6
toIPv6 [Int
0x2001, Int
0xdb8, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]
      (String -> IPv6
forall a. Read a => String -> a
read String
"\"::\"" :: IPv6) IPv6 -> IPv6 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0]
      (String -> IPv6
forall a. Read a => String -> a
read String
"\"::1\"" :: IPv6) IPv6 -> IPv6 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Read fails on invalid input" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      IPv6 -> IO IPv6
forall a. a -> IO a
evaluate (String -> IPv6
forall a. Read a => String -> a
read String
"\"invalid\"" :: IPv6) IO IPv6 -> Selector SomeException -> IO ()
forall e a.
(HasCallStack, Exception e) =>
IO a -> Selector e -> IO ()
`shouldThrow` Selector SomeException
anyException
      IPv6 -> IO IPv6
forall a. a -> IO a
evaluate (String -> IPv6
forall a. Read a => String -> a
read String
"\"gggg::1\"" :: IPv6) IO IPv6 -> Selector SomeException -> IO ()
forall e a.
(HasCallStack, Exception e) =>
IO a -> Selector e -> IO ()
`shouldThrow` Selector SomeException
anyException

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"Show/Read roundtrip" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      String -> IPv6
forall a. Read a => String -> a
read (IPv6 -> String
forall a. Show a => a -> String
show ([Int] -> IPv6
toIPv6 [Int
0x2001, Int
0xdb8, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]))
        IPv6 -> IPv6 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv6
toIPv6 [Int
0x2001, Int
0xdb8, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]
      String -> IPv6
forall a. Read a => String -> a
read (IPv6 -> String
forall a. Show a => a -> String
show ([Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0])) IPv6 -> IPv6 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0]
      String -> IPv6
forall a. Read a => String -> a
read (IPv6 -> String
forall a. Show a => a -> String
show ([Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1])) IPv6 -> IPv6 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"ToJSON instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      IPv6 -> Value
forall a. ToJSON a => a -> Value
toJSON ([Int] -> IPv6
toIPv6 [Int
0x2001, Int
0xdb8, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1]) Value -> Value -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [aesonQQ| "2001:db8::1" |]
      IPv6 -> Value
forall a. ToJSON a => a -> Value
toJSON ([Int] -> IPv6
toIPv6 [Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0, Int
0]) Value -> Value -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [aesonQQ| "::" |]

    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"FromJSON instance" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      Value -> Result IPv6
forall a. FromJSON a => Value -> Result a
fromJSON [aesonQQ| "2001:db8::1" |] Result IPv6 -> Result IPv6 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` IPv6 -> Result IPv6
forall a. a -> Result a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Int] -> IPv6
toIPv6 [Int
0x2001, Int
0xdb8, Int
0, Int
0, Int
0, Int
0, Int
0, Int
1])
      Value -> Result IPv6
forall a. FromJSON a => Value -> Result a
fromJSON [aesonQQ| "invalid" |] Result IPv6 -> (Result IPv6 -> Bool) -> IO ()
forall a. (HasCallStack, Show a) => a -> (a -> Bool) -> IO ()
`shouldSatisfy` (forall a. Result a -> Bool
isError @IPv6)
      Value -> Result IPv6
forall a. FromJSON a => Value -> Result a
fromJSON [aesonQQ| 123 |] Result IPv6 -> (Result IPv6 -> Bool) -> IO ()
forall a. (HasCallStack, Show a) => a -> (a -> Bool) -> IO ()
`shouldSatisfy` (forall a. Result a -> Bool
isError @IPv6)