{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Consistency.Migrations where
import Data.Aeson
import Data.Text (Text, pack)
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Data.SafeJSON
import Data.SafeJSON.Test
-- 'OldType' and 'NewType' should be well-defined for these
-- tests to succeed.
migrationConsistency :: TestTree
migrationConsistency = testGroup "Migration Consistency"
[ oldNewTypeChecks
, roundTripTest
, reverseRoundTripTest
, roundTripTestProp
, reverseRoundTripTestProp
]
oldNewTypeChecks :: TestTree
oldNewTypeChecks = testGroup "Consistent Old-/NewType"
[ testCase "OldType is consistent" $ testConsistency @OldType
, testCase "NewType is consistent" $ testConsistency @NewType
, testRoundTripProp @OldType "Round trip (OldType)"
, testRoundTripProp @NewType "Round trip (NewType)"
]
roundTripTest :: TestTree
roundTripTest = testCase "Round trip function test" $ do
let oldType = OldType "test" 1
testRoundTrip oldType
migrateRoundTrip @NewType oldType
reverseRoundTripTest :: TestTree
reverseRoundTripTest = testCase "Reverse round trip function test" $ do
let newType = NewType [1,2,3] False
testRoundTrip newType
migrateReverseRoundTrip @OldType newType
roundTripTestProp :: TestTree
roundTripTestProp = migrateRoundTripProp @NewType @OldType "Round trip property function test"
reverseRoundTripTestProp :: TestTree
reverseRoundTripTestProp = migrateReverseRoundTripProp @OldType @NewType "Reverse round trip property function test"
----------------------------------------------------------
-- Well-defined types
----------------------------------------------------------
data OldType = OldType Text Int
deriving (Eq, Show)
instance FromJSON OldType where
parseJSON = withObject "OldType" $ \o -> do
i <- o .: "old_type_int"
t <- o .: "old_type_text"
return $ OldType t i
instance ToJSON OldType where
toJSON (OldType t i) = object
[ "old_type_int" .= i
, "old_type_text" .= t
]
instance SafeJSON OldType where
version = noVersion
kind = extended_base
instance Arbitrary OldType where
arbitrary = OldType . pack <$> arbitrary <*> arbitrary
data NewType = NewType {
newInts :: [Int],
hasText :: Bool
} deriving (Eq, Show)
instance FromJSON NewType where
parseJSON = withObject "NewType" $ \o -> do
newInts <- o .: "new_type_ints"
hasText <- o .: "new_type_bool"
return NewType{..}
instance ToJSON NewType where
toJSON (NewType is b) = object
[ "new_type_ints" .= is
, "new_type_bool" .= b
]
instance SafeJSON NewType where
version = 1
kind = extension
instance Migrate NewType where
type MigrateFrom NewType = OldType
migrate (OldType t i) = NewType [i] $ if t == mempty then False else True
instance Arbitrary NewType where
arbitrary = NewType <$> arbitrary <*> arbitrary
instance Migrate (Reverse OldType) where
type MigrateFrom (Reverse OldType) = NewType
migrate (NewType is b) = Reverse $ OldType t i
where i = case is of
[] -> 0
(x:_) -> x
t = if b then "yes" else ""