packages feed

squeal-postgresql-0.1.0.0: test/Squeal/PostgreSQL/ManipulationSpec.hs

{-# LANGUAGE
    DataKinds
  , MagicHash
  , OverloadedLabels
  , OverloadedStrings
  , TypeApplications
  , TypeOperators
#-}

module Squeal.PostgreSQL.ManipulationSpec where

import Generics.SOP hiding (from)
import Test.Hspec

import Squeal.PostgreSQL

spec :: Spec
spec = do
  let
    manipulation `manipulationRenders` str =
      renderManipulation manipulation `shouldBe` str
  it "correctly renders returning INSERTs" $ do
    let
      statement :: Manipulation Tables '[] SumAndCol1
      statement =
        insertInto #table1 (Values (2 `As` #col1 :* 4 `As` #col2 :* Nil) [])
          OnConflictDoRaise
          (Returning $ (#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
    statement `manipulationRenders`
      "INSERT INTO table1 (col1, col2) VALUES (2, 4)\
      \ RETURNING (col1 + col2) AS sum, col1 AS col1;"
  it "correctly renders simple UPDATEs" $ do
    let
      statement :: Manipulation Tables '[] '[]
      statement =
        update #table1 (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
          (#col1 ./= #col2) (Returning Nil)
    statement `manipulationRenders`
      "UPDATE table1 SET col1 = 2\
      \ WHERE (col1 <> col2);"
  it "correctly renders returning UPDATEs" $ do
    let
      statement :: Manipulation Tables '[] SumAndCol1
      statement =
        update #table1 (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
          (#col1 ./= #col2)
          (Returning $ (#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
    statement `manipulationRenders`
      "UPDATE table1 SET col1 = 2\
      \ WHERE (col1 <> col2)\
      \ RETURNING (col1 + col2) AS sum, col1 AS col1;"
  it "correctly renders upsert INSERTs" $ do
    let
      statement :: Manipulation Tables '[] '[]
      statement =
        insertInto #table1 (Values (2 `As` #col1 :* 4 `As` #col2 :* Nil) [])
          (OnConflictDoUpdate
            (Set 2 `As` #col1 :* Same `As` #col2 :* Nil) Nothing)
          (Returning Nil)
    statement `manipulationRenders`
      "INSERT INTO table1 (col1, col2) VALUES (2, 4)\
      \ ON CONFLICT DO UPDATE\
      \ SET col1 = 2;"
  it "correctly renders returning upsert INSERTs" $ do
    let
      statement :: Manipulation Tables '[] SumAndCol1
      statement =
        insertInto #table1 (Values (2 `As` #col1 :* 4 `As` #col2 :* Nil) [])
          (OnConflictDoUpdate
            (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
            (Just (#col1 ./= #col2)))
          (Returning $ (#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
    statement `manipulationRenders`
      "INSERT INTO table1 (col1, col2) VALUES (2, 4)\
      \ ON CONFLICT DO UPDATE\
      \ SET col1 = 2\
      \ WHERE (col1 <> col2)\
      \ RETURNING (col1 + col2) AS sum, col1 AS col1;"
  it "correctly renders DELETEs" $ do
    let
      statement :: Manipulation Tables '[] '[]
      statement = deleteFrom #table1 (#col1 .== #col2) (Returning Nil)
    statement `manipulationRenders`
      "DELETE FROM table1 WHERE (col1 = col2);"
  it "should be safe against SQL injection in literal text" $ do
    let
      statement :: Manipulation StudentsTable '[] '[]
      statement = insertInto #students
        (Values ("Robert'); DROP TABLE students;" `As` #name :* Nil) [])
        OnConflictDoRaise (Returning Nil)
    statement `manipulationRenders`
      "INSERT INTO students (name) VALUES (E'Robert''); DROP TABLE students;');"

type Columns =
  '[ "col1" ::: 'Required ('NotNull 'PGint4)
   , "col2" ::: 'Required ('NotNull 'PGint4)
   ]
type Tables = '[ "table1" ::: Columns ]
type SumAndCol1 =
  '[ "sum" ::: 'Required ('NotNull 'PGint4)
   , "col1" ::: 'Required ('NotNull 'PGint4)
   ]
type StudentsColumns = '["name" ::: 'Required ('NotNull 'PGtext)]
type StudentsTable = '["students" ::: StudentsColumns]
type OrderColumns =
  '[ "orderID"    ::: 'Required ('NotNull 'PGint4)
   , "orderVal"   ::: 'Required ('NotNull 'PGtext)
   , "customerID" ::: 'Required ('NotNull 'PGint4)
   , "shipperID"  ::: 'Required ('NotNull 'PGint4)
   ]
type CustomerColumns =
  '[ "customerID" ::: 'Required ('NotNull 'PGint4)
   , "customerVal" ::: 'Required ('NotNull 'PGfloat4)
   ]
type ShipperColumns =
  '[ "shipperID" ::: 'Required ('NotNull 'PGint4)
   , "shipperVal" ::: 'Required ('NotNull 'PGbool)
   ]
type JoinTables =
  '[ "orders"    ::: OrderColumns
   , "customers" ::: CustomerColumns
   , "shippers"  ::: ShipperColumns
   ]
type ValueColumns =
  '[ "orderVal"    ::: 'Required ('NotNull 'PGtext)
   , "customerVal" ::: 'Required ('NotNull 'PGfloat4)
   , "shipperVal"  ::: 'Required ('NotNull 'PGbool)
   ]