{-# LANGUAGE Arrows #-}
{-# LANGUAGE OverloadedStrings #-}

module Niv.Git.Test
  ( tests,
  )
where

import Control.Monad
import qualified Data.Aeson.KeyMap as KM
import Data.Bifunctor
import qualified Data.HashMap.Strict as HMS
import Data.IORef
import Niv.Git.Cmd
import Niv.Sources
import Niv.Update
import qualified Test.Tasty as Tasty
import Test.Tasty.HUnit ((@=?))
import qualified Test.Tasty.HUnit as Tasty

tests :: [Tasty.TestTree]
tests = [test_repositoryParse, test_gitUpdates]

test_repositoryParse :: Tasty.TestTree
test_repositoryParse =
  Tasty.testGroup
    "repository parse"
    [ Tasty.testCase "goo" $
        parseGitShortcut "goo" @=? Nothing,
      Tasty.testCase ":nmattia/niv" $
        parseGitShortcut ":nmattia/niv"
          @=? Just
            (PackageName "niv", KM.singleton "repo" ":nmattia/niv"),
      Tasty.testCase "ssh" $
        parseGitShortcut "ssh"
          @=? Just
            (PackageName "jq", KM.singleton "repo" "ssh"),
      Tasty.testCase "" $
        parseGitShortcut ""
          @=? Just
            (PackageName "jq", KM.singleton "repo" ""),
      Tasty.testCase "" $
        parseGitShortcut "" @=? Nothing,
      Tasty.testCase "~/path/to/repo.git" $
        parseGitShortcut "~/path/to/repo.git"
          @=? Just
            (PackageName "repo", KM.singleton "repo" "~/path/to/repo.git")
    ]

test_gitUpdates :: Tasty.TestTree
test_gitUpdates =
  Tasty.testGroup
    "updates"
    [ Tasty.testCase "rev is updated" test_gitUpdateRev,
      Tasty.testCase "git is called once" test_gitCalledOnce
    ]

test_gitUpdateRev :: IO ()
test_gitUpdateRev = do
  interState <- evalUpdate initialState $ proc () ->
    gitUpdate (error "should be def") defaultBranchAndHEAD' -< ()
  let interState' = HMS.map (first (\_ -> Free)) interState
  actualState <- evalUpdate interState' $ proc () ->
    gitUpdate latestRev' (error "should update") -< ()
  unless ((snd <$> actualState) == expectedState) $
    error $
      "State mismatch: " <> show actualState
  where
    latestRev' _ _ = pure "some-other-rev"
    defaultBranchAndHEAD' _ = pure ("some-branch", "some-rev")
    initialState =
      HMS.fromList
        [("repo", (Free, ":nmattia/niv"))]
    expectedState =
      HMS.fromList
        [ ("repo", ":nmattia/niv"),
          ("branch", "some-branch"),
          ("rev", "some-other-rev"),
          ("type", "git")
        ]

once1 :: (b -> IO a) -> IO (b -> IO a)
once1 f = do
  used <- newIORef False
  pure $ \x -> do
    used' <- readIORef used
    if used'
      then error "already used"
      else do
        writeIORef used True
        f x

once2 :: (a -> b -> IO c) -> IO (a -> b -> IO c)
once2 f = do
  used <- newIORef False
  pure $ \x y -> do
    used' <- readIORef used
    if used'
      then error "already used"
      else do
        writeIORef used True
        f x y

-- | This tests that we don't run the same git operations several times during
-- the update
test_gitCalledOnce :: IO ()
test_gitCalledOnce = do
  defaultBranchAndHEAD'' <- once1 defaultBranchAndHEAD'
  latestRev'' <- once2 latestRev'
  interState <- evalUpdate initialState $ proc () ->
    gitUpdate (error "should be def") defaultBranchAndHEAD'' -< ()
  let interState' = HMS.map (first (\_ -> Free)) interState
  actualState <- evalUpdate interState' $ proc () ->
    gitUpdate latestRev'' (error "should update") -< ()
  unless ((snd <$> actualState) == expectedState) $
    error $
      "State mismatch: " <> show actualState
  where
    latestRev' _ _ = pure "some-other-rev"
    defaultBranchAndHEAD' _ = pure ("some-branch", "some-rev")
    initialState =
      HMS.fromList
        [("repo", (Free, ":nmattia/niv"))]
    expectedState =
      HMS.fromList
        [ ("repo", ":nmattia/niv"),
          ("branch", "some-branch"),
          ("rev", "some-other-rev"),
          ("type", "git")
        ]

# LANGUAGE Arrows #
# LANGUAGE OverloadedStrings #
 | This tests that we don't run the same git operations several times during
 the update

module Niv.Git.Test
  ( tests,
  )
where

import Control.Monad
import qualified Data.Aeson.KeyMap as KM
import Data.Bifunctor
import qualified Data.HashMap.Strict as HMS
import Data.IORef
import Niv.Git.Cmd
import Niv.Sources
import Niv.Update
import qualified Test.Tasty as Tasty
import Test.Tasty.HUnit ((@=?))
import qualified Test.Tasty.HUnit as Tasty

tests :: [Tasty.TestTree]
tests = [test_repositoryParse, test_gitUpdates]

test_repositoryParse :: Tasty.TestTree
test_repositoryParse =
  Tasty.testGroup
    "repository parse"
    [ Tasty.testCase "goo" $
        parseGitShortcut "goo" @=? Nothing,
      Tasty.testCase ":nmattia/niv" $
        parseGitShortcut ":nmattia/niv"
          @=? Just
            (PackageName "niv", KM.singleton "repo" ":nmattia/niv"),
      Tasty.testCase "ssh" $
        parseGitShortcut "ssh"
          @=? Just
            (PackageName "jq", KM.singleton "repo" "ssh"),
      Tasty.testCase "" $
        parseGitShortcut ""
          @=? Just
            (PackageName "jq", KM.singleton "repo" ""),
      Tasty.testCase "" $
        parseGitShortcut "" @=? Nothing,
      Tasty.testCase "~/path/to/repo.git" $
        parseGitShortcut "~/path/to/repo.git"
          @=? Just
            (PackageName "repo", KM.singleton "repo" "~/path/to/repo.git")
    ]

test_gitUpdates :: Tasty.TestTree
test_gitUpdates =
  Tasty.testGroup
    "updates"
    [ Tasty.testCase "rev is updated" test_gitUpdateRev,
      Tasty.testCase "git is called once" test_gitCalledOnce
    ]

test_gitUpdateRev :: IO ()
test_gitUpdateRev = do
  interState <- evalUpdate initialState $ proc () ->
    gitUpdate (error "should be def") defaultBranchAndHEAD' -< ()
  let interState' = HMS.map (first (\_ -> Free)) interState
  actualState <- evalUpdate interState' $ proc () ->
    gitUpdate latestRev' (error "should update") -< ()
  unless ((snd <$> actualState) == expectedState) $
    error $
      "State mismatch: " <> show actualState
  where
    latestRev' _ _ = pure "some-other-rev"
    defaultBranchAndHEAD' _ = pure ("some-branch", "some-rev")
    initialState =
      HMS.fromList
        [("repo", (Free, ":nmattia/niv"))]
    expectedState =
      HMS.fromList
        [ ("repo", ":nmattia/niv"),
          ("branch", "some-branch"),
          ("rev", "some-other-rev"),
          ("type", "git")
        ]

once1 :: (b -> IO a) -> IO (b -> IO a)
once1 f = do
  used <- newIORef False
  pure $ \x -> do
    used' <- readIORef used
    if used'
      then error "already used"
      else do
        writeIORef used True
        f x

once2 :: (a -> b -> IO c) -> IO (a -> b -> IO c)
once2 f = do
  used <- newIORef False
  pure $ \x y -> do
    used' <- readIORef used
    if used'
      then error "already used"
      else do
        writeIORef used True
        f x y

test_gitCalledOnce :: IO ()
test_gitCalledOnce = do
  defaultBranchAndHEAD'' <- once1 defaultBranchAndHEAD'
  latestRev'' <- once2 latestRev'
  interState <- evalUpdate initialState $ proc () ->
    gitUpdate (error "should be def") defaultBranchAndHEAD'' -< ()
  let interState' = HMS.map (first (\_ -> Free)) interState
  actualState <- evalUpdate interState' $ proc () ->
    gitUpdate latestRev'' (error "should update") -< ()
  unless ((snd <$> actualState) == expectedState) $
    error $
      "State mismatch: " <> show actualState
  where
    latestRev' _ _ = pure "some-other-rev"
    defaultBranchAndHEAD' _ = pure ("some-branch", "some-rev")
    initialState =
      HMS.fromList
        [("repo", (Free, ":nmattia/niv"))]
    expectedState =
      HMS.fromList
        [ ("repo", ":nmattia/niv"),
          ("branch", "some-branch"),
          ("rev", "some-other-rev"),
          ("type", "git")
        ]
