2019-11-20 21:21:30 +03:00
|
|
|
{-# LANGUAGE UndecidableInstances #-}
|
|
|
|
|
|
|
|
module Hasura.Server.MigrateSpec (CacheRefT(..), spec) where
|
2019-11-18 21:45:54 +03:00
|
|
|
|
|
|
|
import Hasura.Prelude
|
|
|
|
|
2019-11-27 01:49:42 +03:00
|
|
|
import Control.Concurrent.MVar.Lifted
|
|
|
|
import Control.Monad.Trans.Control (MonadBaseControl)
|
|
|
|
import Control.Monad.Unique
|
|
|
|
import Control.Natural ((:~>) (..))
|
2020-02-07 14:03:12 +03:00
|
|
|
import Data.List (isPrefixOf, stripPrefix)
|
|
|
|
import Data.List.Split (splitOn)
|
2019-11-27 01:49:42 +03:00
|
|
|
import Data.Time.Clock (getCurrentTime)
|
|
|
|
import Data.Tuple (swap)
|
2020-02-07 14:03:12 +03:00
|
|
|
import System.Process (readProcess)
|
2019-11-20 21:21:30 +03:00
|
|
|
import Test.Hspec.Core.Spec
|
|
|
|
import Test.Hspec.Expectations.Lifted
|
2019-11-18 21:45:54 +03:00
|
|
|
|
2019-11-27 01:49:42 +03:00
|
|
|
import qualified Database.PG.Query as Q
|
2020-02-07 14:03:12 +03:00
|
|
|
import qualified Safe
|
2019-11-18 21:45:54 +03:00
|
|
|
|
2019-12-13 00:46:33 +03:00
|
|
|
import Hasura.RQL.DDL.Metadata (ClearMetadata (..), runClearMetadata)
|
2019-11-20 21:21:30 +03:00
|
|
|
import Hasura.RQL.DDL.Schema
|
2019-11-27 01:49:42 +03:00
|
|
|
import Hasura.RQL.Types
|
2020-04-24 10:55:51 +03:00
|
|
|
import Hasura.Server.API.PGDump
|
2020-02-07 14:03:12 +03:00
|
|
|
import Hasura.Server.Init (DowngradeOptions (..), downgradeShortcuts)
|
2019-11-18 21:45:54 +03:00
|
|
|
import Hasura.Server.Migrate
|
2020-01-23 00:55:55 +03:00
|
|
|
import Hasura.Server.Version (HasVersion)
|
2019-11-18 21:45:54 +03:00
|
|
|
|
2019-11-20 21:21:30 +03:00
|
|
|
newtype CacheRefT m a
|
|
|
|
= CacheRefT { runCacheRefT :: MVar (RebuildableSchemaCache m) -> m a }
|
|
|
|
deriving
|
|
|
|
( Functor, Applicative, Monad, MonadIO, MonadError e, MonadBase b, MonadBaseControl b
|
|
|
|
, MonadTx, MonadUnique, UserInfoM, HasHttpManager, HasSQLGenCtx )
|
|
|
|
via (ReaderT (MVar (RebuildableSchemaCache m)) m)
|
|
|
|
|
|
|
|
instance MonadTrans CacheRefT where
|
|
|
|
lift = CacheRefT . const
|
|
|
|
|
|
|
|
instance (MonadBase IO m) => TableCoreInfoRM (CacheRefT m)
|
|
|
|
instance (MonadBase IO m) => CacheRM (CacheRefT m) where
|
|
|
|
askSchemaCache = CacheRefT (fmap lastBuiltSchemaCache . readMVar)
|
|
|
|
|
2020-01-30 02:03:49 +03:00
|
|
|
instance (MonadIO m, MonadBaseControl IO m, MonadTx m) => CacheRWM (CacheRefT m) where
|
|
|
|
buildSchemaCacheWithOptions reason invalidations = CacheRefT $ flip modifyMVar \schemaCache -> do
|
|
|
|
((), cache, _) <- runCacheRWT schemaCache (buildSchemaCacheWithOptions reason invalidations)
|
|
|
|
pure (cache, ())
|
2019-11-18 21:45:54 +03:00
|
|
|
|
2019-11-20 21:21:30 +03:00
|
|
|
instance Example (CacheRefT m ()) where
|
|
|
|
type Arg (CacheRefT m ()) = CacheRefT m :~> IO
|
|
|
|
evaluateExample m params action = evaluateExample (action ($$ m)) params ($ ())
|
|
|
|
|
|
|
|
type SpecWithCache m = SpecWith (CacheRefT m :~> IO)
|
|
|
|
|
|
|
|
singleTransaction :: CacheRefT m () -> CacheRefT m ()
|
|
|
|
singleTransaction = id
|
|
|
|
|
|
|
|
spec
|
2020-01-23 00:55:55 +03:00
|
|
|
:: ( HasVersion
|
|
|
|
, MonadIO m
|
2019-11-20 21:21:30 +03:00
|
|
|
, MonadBaseControl IO m
|
|
|
|
, MonadTx m
|
|
|
|
, MonadUnique m
|
|
|
|
, HasHttpManager m
|
|
|
|
, HasSQLGenCtx m
|
|
|
|
)
|
|
|
|
=> Q.ConnInfo -> SpecWithCache m
|
|
|
|
spec pgConnInfo = do
|
|
|
|
let dropAndInit time = CacheRefT $ flip modifyMVar \_ ->
|
|
|
|
dropCatalog *> (swap <$> migrateCatalog time)
|
2020-04-24 10:55:51 +03:00
|
|
|
|
2019-11-18 21:45:54 +03:00
|
|
|
describe "migrateCatalog" $ do
|
2019-11-20 21:21:30 +03:00
|
|
|
it "initializes the catalog" $ singleTransaction do
|
|
|
|
(dropAndInit =<< liftIO getCurrentTime) `shouldReturn` MRInitialized
|
2019-11-18 21:45:54 +03:00
|
|
|
|
2019-11-20 21:21:30 +03:00
|
|
|
it "is idempotent" \(NT transact) -> do
|
2020-02-07 14:03:12 +03:00
|
|
|
let dumpSchema = execPGDump (PGDumpReqBody ["--schema-only"] (Just False)) pgConnInfo
|
2019-11-18 21:45:54 +03:00
|
|
|
time <- getCurrentTime
|
2019-11-20 21:21:30 +03:00
|
|
|
transact (dropAndInit time) `shouldReturn` MRInitialized
|
2020-02-07 14:03:12 +03:00
|
|
|
firstDump <- transact dumpSchema
|
2019-11-20 21:21:30 +03:00
|
|
|
transact (dropAndInit time) `shouldReturn` MRInitialized
|
2020-02-07 14:03:12 +03:00
|
|
|
secondDump <- transact dumpSchema
|
2019-11-18 21:45:54 +03:00
|
|
|
secondDump `shouldBe` firstDump
|
2020-04-24 10:55:51 +03:00
|
|
|
|
2020-02-07 14:03:12 +03:00
|
|
|
it "supports upgrades after downgrade to version 12" \(NT transact) -> do
|
|
|
|
let downgradeTo v = downgradeCatalog DowngradeOptions{ dgoDryRun = False, dgoTargetVersion = v }
|
|
|
|
upgradeToLatest time = CacheRefT $ flip modifyMVar \_ ->
|
|
|
|
swap <$> migrateCatalog time
|
|
|
|
time <- getCurrentTime
|
|
|
|
transact (dropAndInit time) `shouldReturn` MRInitialized
|
|
|
|
downgradeResult <- (transact . lift) (downgradeTo "12" time)
|
|
|
|
downgradeResult `shouldSatisfy` \case
|
|
|
|
MRMigrated{} -> True
|
|
|
|
_ -> False
|
|
|
|
transact (upgradeToLatest time) `shouldReturn` MRMigrated "12"
|
2020-04-24 10:55:51 +03:00
|
|
|
|
2020-02-07 14:03:12 +03:00
|
|
|
it "supports downgrades for every Git tag" $ singleTransaction do
|
|
|
|
gitOutput <- liftIO $ readProcess "git" ["log", "--no-walk", "--tags", "--pretty=%D"] ""
|
|
|
|
let filterOldest = filter (not . isPrefixOf "v1.0.0-alpha")
|
|
|
|
extractTagName = Safe.headMay . splitOn ", " <=< stripPrefix "tag: "
|
|
|
|
supportedDowngrades = sort (map fst downgradeShortcuts)
|
|
|
|
gitTags = (sort . filterOldest . mapMaybe extractTagName . tail . lines) gitOutput
|
2020-04-24 10:55:51 +03:00
|
|
|
for_ gitTags \t ->
|
2020-02-07 14:03:12 +03:00
|
|
|
t `shouldSatisfy` (`elem` supportedDowngrades)
|
2019-11-18 21:45:54 +03:00
|
|
|
|
|
|
|
describe "recreateSystemMetadata" $ do
|
2019-11-20 21:21:30 +03:00
|
|
|
let dumpMetadata = execPGDump (PGDumpReqBody ["--schema=hdb_catalog"] (Just False)) pgConnInfo
|
|
|
|
|
|
|
|
it "is idempotent" \(NT transact) -> do
|
|
|
|
(transact . dropAndInit =<< getCurrentTime) `shouldReturn` MRInitialized
|
|
|
|
firstDump <- transact dumpMetadata
|
|
|
|
transact recreateSystemMetadata
|
|
|
|
secondDump <- transact dumpMetadata
|
2019-11-18 21:45:54 +03:00
|
|
|
secondDump `shouldBe` firstDump
|
|
|
|
|
2019-11-20 21:21:30 +03:00
|
|
|
it "does not create any objects affected by ClearMetadata" \(NT transact) -> do
|
|
|
|
(transact . dropAndInit =<< getCurrentTime) `shouldReturn` MRInitialized
|
|
|
|
firstDump <- transact dumpMetadata
|
|
|
|
transact (runClearMetadata ClearMetadata) `shouldReturn` successMsg
|
|
|
|
secondDump <- transact dumpMetadata
|
2019-11-18 21:45:54 +03:00
|
|
|
secondDump `shouldBe` firstDump
|