packages feed

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 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