tasty-groundhog-converters (empty) → 0.1.0
raw patch · 7 files changed
+317/−0 lines, 7 filesdep +aesondep +basedep +bimapsetup-changed
Dependencies added: aeson, base, bimap, bytestring, containers, groundhog, groundhog-converters, groundhog-sqlite, groundhog-th, tasty, tasty-hunit, tasty-quickcheck
Files
- ChangeLog.md +6/−0
- LICENSE +30/−0
- README.md +90/−0
- Setup.hs +2/−0
- src/Test/Tasty/Groundhog/Converters.hs +52/−0
- src/Test/Tasty/Groundhog/Converters/Example.hs +97/−0
- tasty-groundhog-converters.cabal +40/−0
+ ChangeLog.md view
@@ -0,0 +1,6 @@+# Revision history for tasty-groundhog-converters++## 0.1.0 -- 2016-03-18++* First version. Released on an unsuspecting world.+
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016, Scott Murphy++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Scott Murphy nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,90 @@+# tasty-groundhog-converters+This library provides a tasty, test harness for groundhog and groundhog-converters.++The key functions are:+``` haskell+roundTripConverter :: Arbitrary a => TestName -> (a -> a -> Bool) -> (Converter a b) -> TestTree+```++and ++``` haskell+goldenSqlConverter :: (PersistEntity b) => TestName -> FilePath -> a -> (b -> b -> Bool) -> Converter a b -> TestTree+```++Which provide tests for serialization (goldenSqlConverter).+and isomorphism (roundTripConverter).++These two together allow a user to quickly add simple testing to a database project using groundhog.++## Usage+From the example:+++``` haskell+data Group = Group {+ _people :: Map Integer Person+ }+ deriving (Eq)+++-- | A wrapped representation of a Person+data Person = Person { _unPerson :: String}+ deriving (Eq)+++-- | To Build up the converter we have to have an arbitrary instance+instance Arbitrary Person where+ arbitrary = Person <$> arbitrary++-- | An Isomorphism between the representation that is pleasent to use in haskell+-- and the one that makes sense to store i.e. 'PersistEntity' +personMapConverter :: Converter (Map Integer Person) [(Int64,String)]+personMapConverter = mapConverter `composeConverter` fmapConverter (bicomposeConverter integerConverter personConverter)++-- | This converter is embedded in 'personMapConverter'+personConverter :: Converter Person String+personConverter = (_unPerson,Person)+++-- | A declaration for group.+mkPersist defaultCodegenConfig [groundhog|+- entity: Group+ constructors:+ - name: Group+ fields:+ - name: _people+ dbName: people+ exprName: MappedIdToPerson+ converter: personMapConverter+- primitive: Person+ converter: personConverter+++|]+++-- | build a golden test (a single test designed to make sure a representation stays constant over time).+-- The aGroup provided is only used the first time the test is used. The converter at the top level here+-- is just (id, id) and (==) is used because there is an Eq instance on Group.+exampleGoldenSqlConverter :: TestTree+exampleGoldenSqlConverter = goldenSqlConverter "Test The test GoldenSqlConverter" "TestGolden" aGroup (==) (id,id) + where+ aGroup = Group somePeople+ somePeople = (Map.fromList . zip [1 ..] . fmap Person ) ["Margret"]++-- | There are no database hits on a round trip test+-- Converter makes the claim that a Converter is an Isomorphism between the two DataTypes.+-- Round trip tests should verify this.+exampleRoundTripTest :: TestTree+exampleRoundTripTest = roundTripConverter "roundtrip personMapConverter" (==) personMapConverter ++-- | call the example test+tastyTest :: IO ()+tastyTest = defaultMain allTests+ where+ allTests = testGroup "all example groundhog converter tests" [ exampleGoldenSqlConverter+ , exampleRoundTripTest]+```++
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Test/Tasty/Groundhog/Converters.hs view
@@ -0,0 +1,52 @@+module Test.Tasty.Groundhog.Converters where+--------------------------------------------------+-- Imports TO TEST SUITE+--------------------------------------------------+import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.QuickCheck+import Database.Groundhog.Core+import Database.Groundhog.Sqlite+import Database.Groundhog.Converters+++{- TO TEST SUITE-}++-- | Test round trip property of a converter,+-- >>> roundTripConverter "should convert a RecordBiMap to a StorableList" (==) myRecordConverter+-- You will need to create an Arbitrary instance for the incoming item+roundTripConverter :: Arbitrary a => TestName -> (a -> a -> Bool) -> (Converter a b) -> TestTree+roundTripConverter testName toBool converterToTest = testProperty testName runRoundTripTest + where+ (f,g) = converterToTest+ runRoundTripTest = do+ val <- arbitrary+ (return . toBool val . g . f) val++-- | goldenSqlConverter takes advantage of the file nature of SQLlite to read in your data+-- using groundhog. Even if you store your data in a different database+-- if the serialization of a converter works in SQLite it will probably work in+-- your given DB. Obviously that isn't perfect but this makes writing a quick test easy+-- The 'a' you give the function will only be active for the first insert.+-- The rest of the time, the conversion and insertion are self contained.+++goldenSqlConverter :: (PersistEntity b) => TestName ->+ FilePath -> a -> + (b -> b -> Bool) -> Converter a b ->+ TestTree+goldenSqlConverter testName fp someA bToBool converter = testCase testName (runSqlTest >>= assertBool "SQLite insertion and conversion should match original")+ where+ (toB,toA) = converter+ runSqlTest = withSqliteConn fp $ runDbConn $ do+ runMigration $ migrate (toB someA)+ bs <- selectAll+ case bs of+ [] -> do+ _ <- insert (toB someA)+ bs' <- selectAll+ return (and $ (\(_,b) -> (bToBool b . toB.toA) b) <$> bs' )+ _ -> return (and $ (\(_,b) -> (bToBool b . toB.toA ) b) <$> bs) +++--------------------------------------------------
+ src/Test/Tasty/Groundhog/Converters/Example.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE QuasiQuotes #-}++{- |+Module : Test.Tasty.Groundhog.Converters.Example+Description : An example of creating a Converter test+Copyright : Plow Technologies LLC+License : MIT License++Maintainer : Scott Murphy++Database entries are persisted state, which means they are a serialization and should be tested for change.+Here is an example of doing that.+ -}+++module Test.Tasty.Groundhog.Converters.Example where++import Database.Groundhog.Converters+import Data.Int (Int64)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Database.Groundhog.TH+import Test.Tasty.Groundhog.Converters+import Test.Tasty+import Test.Tasty.QuickCheck+++-- | Sample DataType 'Group' proviides a Map between an Integer and a 'Person'+-- However, the person is embedded in the Datatype relative to the SQL database++data Group = Group {+ _people :: Map Integer Person+ }+ deriving (Eq)+++-- | A wrapped representation of a Person+data Person = Person { _unPerson :: String}+ deriving (Eq)+++-- | To Build up the converter we have to have an arbitrary instance+instance Arbitrary Person where+ arbitrary = Person <$> arbitrary++-- | An Isomorphism between the representation that is pleasent to use in haskell+-- and the one that makes sense to store i.e. 'PersistEntity' +personMapConverter :: Converter (Map Integer Person) [(Int64,String)]+personMapConverter = mapConverter `composeConverter` fmapConverter (bicomposeConverter integerConverter personConverter)++-- | This converter is embedded in 'personMapConverter'+personConverter :: Converter Person String+personConverter = (_unPerson,Person)+++-- | A declaration for group.+mkPersist defaultCodegenConfig [groundhog|+- entity: Group+ constructors:+ - name: Group+ fields:+ - name: _people+ dbName: people+ exprName: MappedIdToPerson+ converter: personMapConverter+- primitive: Person+ converter: personConverter+++|]+++-- | build a golden test (a single test designed to make sure a representation stays constant over time).+-- The aGroup provided is only used the first time the test is used. The converter at the top level here+-- is just (id, id) and (==) is used because there is an Eq instance on Group.+exampleGoldenSqlConverter :: TestTree+exampleGoldenSqlConverter = goldenSqlConverter "Test The test GoldenSqlConverter" "TestGolden" aGroup (==) (id,id) + where+ aGroup = Group somePeople+ somePeople = (Map.fromList . zip [1 ..] . fmap Person ) ["Margret"]++-- | There are no database hits on a round trip test+-- Converter makes the claim that a Converter is an Isomorphism between the two DataTypes.+-- Round trip tests should verify this.+exampleRoundTripTest :: TestTree+exampleRoundTripTest = roundTripConverter "roundtrip personMapConverter" (==) personMapConverter ++-- | call the example test+tastyTest :: IO ()+tastyTest = defaultMain allTests+ where+ allTests = testGroup "all example groundhog converter tests" [ exampleGoldenSqlConverter+ , exampleRoundTripTest]
+ tasty-groundhog-converters.cabal view
@@ -0,0 +1,40 @@+Name: tasty-groundhog-converters+Version: 0.1.0+Author: Scott Murphy <scottmurphy09@gmail.com>+Maintainer: Scott Murphy <scottmurphy09@gmail.com>+License: BSD3+License-File: LICENSE+Category: Test +Synopsis: Tasty Tests for groundhog converters +Description:+ Groundhog converters are vulnerable to serialization changes. Round trip tests and a SQLite test framework are provided+ to help correct this. +Cabal-Version: >= 1.10+Build-Type: Simple+Extra-Source-Files: README.md, ChangeLog.md++Library+ Default-Language: Haskell2010+ HS-Source-Dirs: src+ GHC-Options: -Wall+ Exposed-Modules: Test.Tasty.Groundhog.Converters+ Test.Tasty.Groundhog.Converters.Example+ +-- Other-Modules: + Build-Depends: base >= 4 && < 5+ , groundhog-converters+ , groundhog+ , tasty + , tasty-hunit+ , tasty-quickcheck+ , groundhog-sqlite+ , groundhog-th + , containers + , bimap + , aeson+ , bytestring +++Source-Repository head+ Type: git+ Location: https://github.com/plow-technologies/tasty-groundhog-converters.git