packages feed

hix-0.9.0: test/Hix/Test/Managed/ReleaseMaintenanceTest.hs

module Hix.Test.Managed.ReleaseMaintenanceTest where

import Hedgehog (Property, TestT, forAllWith, property, test, (===))
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Hedgehog (testProperty)

import Hix.Class.Map (nElems, nMap, nTo)
import Hix.Data.PackageId (PackageId (..))
import Hix.Data.PackageName (localPackageName)
import Hix.Managed.Data.BuildOutput (DepChanges (..))
import Hix.Managed.Data.Envs (Envs)
import Hix.Managed.Data.MaintConfig (MaintConfig (..))
import Hix.Managed.Data.Packages (Packages)
import Hix.Managed.Git (MaintBranch (..), Tag (..))
import Hix.Managed.Maint.Data.MaintResult (
  MaintChanged (..),
  MaintResult (..),
  NoPublishReason (..),
  UnchangedReason (..),
  )
import Hix.Managed.Maint.Git (releaseBranchName)
import Hix.Managed.ReleaseMaintenance (releaseMaintenance)
import Hix.Maybe (justIf)
import Hix.Pretty (showHP, showP)
import Hix.Test.Managed.Maint.Handlers (
  EnvOutput (..),
  GitState (..),
  MaintEvent (..),
  MaintPremise (..),
  MaintState (..),
  buildOutputs,
  dummyRevision,
  evolveHistory,
  maintTestHandlers,
  )
import Hix.Test.Managed.ReleaseMaintenance.Case (MaintTestCase (..), PackageMeta (..))
import Hix.Test.Managed.ReleaseMaintenance.Gen (genMaintTestCase)
import Hix.Test.Utils (runMTest)

config :: MaintConfig
config =
  MaintConfig {
    noFailures = False,
    commit = True,
    push = False,
    revision = True,
    targets = Nothing,
    fetch = False,
    globalGit = False,
    pr = False
  }

data MaintTestOutput =
  MaintTestOutput {
    results :: Packages MaintResult,
    events :: [MaintEvent],
    git :: GitState,
    packages :: Packages PackageMeta,
    outputs :: Envs EnvOutput
  }
  deriving stock (Eq, Show)

runMaintTest ::
  MaintTestCase ->
  TestT IO MaintTestOutput
runMaintTest testCase = do
  runMTest False do
    (handlers, context, state) <- maintTestHandlers outputs premise
    results <- releaseMaintenance handlers config context
    MaintState {events, git} <- liftIO $ readMVar state
    pure MaintTestOutput {packages = premise.packages, ..}
  where
    outputs = buildOutputs premise.packages testCase.envStyle
    premise = evolveHistory testCase

resultProps ::
  Packages PackageMeta ->
  Packages MaintResult
resultProps =
  nMap \ meta -> result (mkChanged (mkBranch meta)) meta
  where
    result changed = \case
      PackageMeta {modified, bumped, released, revision, envModified}
        | bumped
        , released
        -> changed (Published (dummyRevision (revision + 1)))

        | modified
        , released
        -> changed (Modified NoRangeUpdates)

        | released
        , envModified
        -> changed (Modified NoDirectDepUpdates)

        | released
        -> Unchanged NoUpdates

        | otherwise
        -> Unchanged NoTags

    mkChanged branch resolution = Changed {baseBranch = branch, ..}

    mkBranch PackageMeta {package, version} =
      releaseBranchName MaintBranch {..}

eventProps ::
  MaintTestOutput ->
  [MaintEvent]
eventProps MaintTestOutput {packages, outputs} =
  mconcat $ reverse $ nTo packages $ const \case
    PackageMeta {package, version, released, bumped}
      | released ->
        fold (justIf bumped (publishEvents PackageId {name = localPackageName package, version})) ++
        maybeToList (gitEvents package version)
      | otherwise ->
        []
  where
    publishEvents packageId =
      [
        EventFetchRevisions,
        EventPublishRevision {
          packageId,
          form = ("pkgid", showP packageId)
        },
        EventFetchRevisions
      ]

    gitEvents package version = do
      output <- find (isTarget package) (nElems outputs)
      modified <- nonEmpty output.changes.modified
      pure GitCommitted {package, branch = MaintBranch {package, version}, modified}

    isTarget target EnvOutput {targets} = flip any targets \ PackageMeta {package} -> package == target

gitProps :: Packages PackageMeta -> GitState
gitProps packages =
  GitState {branches, current}
  where
    branches =
      reverse $ catMaybes $ nTo packages $ const \case
        PackageMeta {package, version, revision, released, shared}
          | released
          , revision == 0
          , let maintBranch = MaintBranch {package, version}
          -> Just (maintBranch, releaseBranchName maintBranch, Tag {package = justIf (not shared) package, version})
          | otherwise
          -> Nothing

    current =
      last $ catMaybes $ nTo packages $ const \case
        PackageMeta {package, version, released}
          | released
          -> Just MaintBranch {package, version}
          | otherwise
          -> Nothing


prop_maint :: Property
prop_maint =
  property do
    testCase <- forAllWith showHP genMaintTestCase
    output <- test $ runMaintTest testCase
    resultProps output.packages === output.results
    eventProps output === output.events
    gitProps output.packages === output.git

test_maint :: TestTree
test_maint =
  testGroup "managed bounds maintenance" [
    testProperty "maint logic" prop_maint
  ]