module Hydra.NetworkVersionsSpec where

import Hydra.Prelude
import Test.Hydra.Prelude

import Data.Version (Version (..), makeVersion)
import Hydra.NetworkVersions (hydraNodeVersion, parseNetworkTxIds)
import Hydra.Version (embeddedRevision, gitRevision, unknownVersion)
import Test.QuickCheck (Property, counterexample, forAll, property)

spec :: Spec
spec :: Spec
spec =
  String -> Spec -> Spec
forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
"NetworkVersions" (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
"parseNetworkTxIds produces list TxId" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      let networks :: [String]
networks = [String
"mainnet", String
"preview", String
"preprod"]
      let versions :: [Version]
versions = [Int] -> Version
makeVersion ([Int] -> Version) -> (Int -> [Int]) -> Int -> Version
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\Int
v -> [Int
0, Int
v, Int
0]) (Int -> Version) -> [Int] -> [Version]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Int
13 .. Int
21]
      [String] -> (String -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [String]
networks ((String -> IO ()) -> IO ()) -> (String -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \String
network ->
        [Version] -> (Version -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Version]
versions ((Version -> IO ()) -> IO ()) -> (Version -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Version
version -> do
          case Version -> String -> Either String [TxId]
forall (m :: * -> *). MonadFail m => Version -> String -> m [TxId]
parseNetworkTxIds Version
version String
network of
            Left String
err -> String -> IO ()
forall (m :: * -> *) a.
(HasCallStack, MonadThrow m) =>
String -> m a
failure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Failed to parse network tx ids: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
err
            Right [TxId]
txIds -> [TxId]
txIds [TxId] -> ([TxId] -> Bool) -> IO ()
forall a. (HasCallStack, Show a) => a -> (a -> Bool) -> IO ()
`shouldSatisfy` Bool -> Bool
not (Bool -> Bool) -> ([TxId] -> Bool) -> [TxId] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TxId] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null

    String -> Property -> Spec
forall prop.
(HasCallStack, Testable prop) =>
String -> prop -> Spec
prop String
"parseNetworkTxIds works with expected versions and networks" (Property -> Spec) -> Property -> Spec
forall a b. (a -> b) -> a -> b
$
      Gen Version -> (Version -> Property) -> Property
forall a prop.
(Show a, Testable prop) =>
Gen a -> (a -> prop) -> Property
forAll Gen Version
forall a. Arbitrary a => Gen a
arbitrary ((Version -> Property) -> Property)
-> (Version -> Property) -> Property
forall a b. (a -> b) -> a -> b
$ \Version
version ->
        Gen String -> (String -> Property) -> Property
forall a prop.
(Show a, Testable prop) =>
Gen a -> (a -> prop) -> Property
forAll Gen String
forall a. Arbitrary a => Gen a
arbitrary ((String -> Property) -> Property)
-> (String -> Property) -> Property
forall a b. (a -> b) -> a -> b
$ \String
network ->
          Version -> String -> Property
propParseNetworkTxIds Version
version String
network

    -- Only the executables are patched by nix, never this test binary, so the
    -- embedded array still holds the placeholder here. A 'Just' means the
    -- placeholder in 'Hydra.Version' has drifted from 'cbits/revision.c' again:
    -- every unpatched build would then report the placeholder as its revision,
    -- and 'gitRevision' would never be consulted.
    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"finds no embedded revision in an unpatched binary" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$
      Maybe String
embeddedRevision Maybe String -> Maybe String -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Maybe String
forall a. Maybe a
Nothing

    -- With no embedded revision (see above), the version tag is the git
    -- revision, or 'unknownVersion' where git information is unavailable, as in
    -- a nix build whose source has no '.git'. Before the placeholder fix this
    -- reported the 'cbits/revision.c' placeholder in both cases, so this still
    -- catches that regression without depending on git being present.
    String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"falls back to the git revision" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$
      case Version
hydraNodeVersion of
        Version [Int]
_ [String]
tags -> [String]
tags [String] -> [String] -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` [String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe String
unknownVersion Maybe String
gitRevision]

propParseNetworkTxIds :: Version -> String -> Property
propParseNetworkTxIds :: Version -> String -> Property
propParseNetworkTxIds Version
version String
network = do
  let varlidNetworks :: [String]
varlidNetworks = [String
"mainnet", String
"preview", String
"preprod"]
  let validVersions :: [Version]
validVersions = ([Int] -> Version
makeVersion ([Int] -> Version) -> (Int -> [Int]) -> Int -> Version
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\Int
v -> [Int
0, Int
v, Int
0]) (Int -> Version) -> [Int] -> [Version]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Int
13 .. Int
21]) [Version] -> [Version] -> [Version]
forall a. Semigroup a => a -> a -> a
<> [Version
hydraNodeVersion]
  case Version -> String -> Either String [TxId]
forall (m :: * -> *). MonadFail m => Version -> String -> m [TxId]
parseNetworkTxIds Version
version String
network of
    Left String
err ->
      if String
network String -> [String] -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` [String]
varlidNetworks Bool -> Bool -> Bool
&& Version
version Version -> [Version] -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` [Version]
validVersions
        then Bool -> Property
forall prop. Testable prop => prop -> Property
property Bool
False Property -> (Property -> Property) -> Property
forall a b. a -> (a -> b) -> b
& String -> Property -> Property
forall prop. Testable prop => String -> prop -> Property
counterexample (String
"error: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
err)
        else Bool -> Property
forall prop. Testable prop => prop -> Property
property Bool
True
    Right [TxId]
txIds -> Bool -> Property
forall prop. Testable prop => prop -> Property
property (Bool -> Property) -> Bool -> Property
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not ([TxId] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [TxId]
txIds)