generated from tpl/purs
Migrate tests to purescript-test-unit + Instant fetching + test
This commit is contained in:
parent
45eb1acb91
commit
bb493e5983
@ -2,10 +2,18 @@
|
|||||||
|
|
||||||
purescript-postgresql-client is a PostgreSQL client library for PureScript.
|
purescript-postgresql-client is a PostgreSQL client library for PureScript.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
To use this library, you need to add `pg` and `decimal.js` as an npm dependency. You can also
|
To use this library, you need to add `pg` and `decimal.js` as an npm dependency. You can also
|
||||||
find this npm library on [https://github.com/brianc/node-postgres][pg].
|
find this npm library on [https://github.com/brianc/node-postgres][pg].
|
||||||
|
|
||||||
|
## Preprocessing static SQL statements
|
||||||
|
|
||||||
The purspgpp preprocessor has been replaced by [sqltopurs], which is a code
|
The purspgpp preprocessor has been replaced by [sqltopurs], which is a code
|
||||||
generator instead of a preprocessor, and easier to use.
|
generator instead of a preprocessor, and easier to use.
|
||||||
|
|
||||||
[sqltopurs]: https://github.com/rightfold/sqltopurs
|
[sqltopurs]: https://github.com/rightfold/sqltopurs
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
To run test you have to prepare "purspg" database and use standard command: `pulp test`
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
"purescript-bifunctors": "^3.0.0",
|
"purescript-bifunctors": "^3.0.0",
|
||||||
"purescript-eff": "^3.1.0",
|
"purescript-eff": "^3.1.0",
|
||||||
"purescript-exceptions": "^3.0.0",
|
"purescript-exceptions": "^3.0.0",
|
||||||
"purescript-decimals": "^3.4.0"
|
"purescript-decimals": "^3.4.0",
|
||||||
|
"purescript-js-date": "^5.2.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -25,6 +26,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"purescript-assert": "^3.0.0",
|
"purescript-assert": "^3.0.0",
|
||||||
"purescript-eff": "^3.1.0"
|
"purescript-eff": "^3.1.0",
|
||||||
|
"purescript-debug": "^3.0.0",
|
||||||
|
"purescript-test-unit": "^13.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,18 @@ exports.instantToString = function(i) {
|
|||||||
return new Date(i).toUTCString();
|
return new Date(i).toUTCString();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.instantFromString = function(Left) {
|
||||||
|
return function(Right) {
|
||||||
|
return function(s) {
|
||||||
|
try {
|
||||||
|
return Right(Date.parse(s));
|
||||||
|
} catch(e) {
|
||||||
|
return Left("Date string parsing failed: \"" + s + "\", with: " + e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
exports.unsafeIsBuffer = function(x) {
|
exports.unsafeIsBuffer = function(x) {
|
||||||
return x instanceof Buffer;
|
return x instanceof Buffer;
|
||||||
};
|
};
|
||||||
|
@ -8,14 +8,15 @@ import Control.Monad.Except (runExcept)
|
|||||||
import Data.Array as Array
|
import Data.Array as Array
|
||||||
import Data.Bifunctor (lmap)
|
import Data.Bifunctor (lmap)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.DateTime.Instant (Instant)
|
import Data.DateTime.Instant (Instant, instant)
|
||||||
import Data.Decimal (Decimal)
|
import Data.Decimal (Decimal)
|
||||||
import Data.Decimal as Decimal
|
import Data.Decimal as Decimal
|
||||||
import Data.Either (Either, note)
|
import Data.Either (Either(..), note)
|
||||||
import Data.Foreign (Foreign, isNull, readArray, readBoolean, readChar, readInt, readNumber, readString, toForeign, unsafeFromForeign)
|
import Data.Foreign (Foreign, isNull, readArray, readBoolean, readChar, readInt, readNumber, readString, toForeign, unsafeFromForeign)
|
||||||
import Data.List (List)
|
import Data.List (List)
|
||||||
import Data.List as List
|
import Data.List as List
|
||||||
import Data.Maybe (Maybe(..))
|
import Data.Maybe (Maybe(..))
|
||||||
|
import Data.Time.Duration (Milliseconds(..))
|
||||||
import Data.Traversable (traverse)
|
import Data.Traversable (traverse)
|
||||||
|
|
||||||
-- | Convert things to SQL values.
|
-- | Convert things to SQL values.
|
||||||
@ -79,6 +80,11 @@ instance fromSQLValueByteString :: FromSQLValue ByteString where
|
|||||||
instance toSQLValueInstant :: ToSQLValue Instant where
|
instance toSQLValueInstant :: ToSQLValue Instant where
|
||||||
toSQLValue = instantToString
|
toSQLValue = instantToString
|
||||||
|
|
||||||
|
instance fromSQLValueInstant :: FromSQLValue Instant where
|
||||||
|
fromSQLValue v = do
|
||||||
|
t ← instantFromString Left Right v
|
||||||
|
note ("Instant construction failed for given timestamp: " <> show t) $ instant (Milliseconds t)
|
||||||
|
|
||||||
instance toSQLValueMaybe :: (ToSQLValue a) => ToSQLValue (Maybe a) where
|
instance toSQLValueMaybe :: (ToSQLValue a) => ToSQLValue (Maybe a) where
|
||||||
toSQLValue Nothing = null
|
toSQLValue Nothing = null
|
||||||
toSQLValue (Just x) = toSQLValue x
|
toSQLValue (Just x) = toSQLValue x
|
||||||
@ -103,4 +109,5 @@ instance fromSQLValueDecimal :: FromSQLValue Decimal where
|
|||||||
|
|
||||||
foreign import null :: Foreign
|
foreign import null :: Foreign
|
||||||
foreign import instantToString :: Instant -> Foreign
|
foreign import instantToString :: Instant -> Foreign
|
||||||
|
foreign import instantFromString :: (String → Either String Number) → (Number → Either String Number) → Foreign → Either String Number
|
||||||
foreign import unsafeIsBuffer :: ∀ a. a -> Boolean
|
foreign import unsafeIsBuffer :: ∀ a. a -> Boolean
|
||||||
|
149
test/Main.purs
149
test/Main.purs
@ -4,17 +4,53 @@ module Test.Main
|
|||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
import Control.Monad.Aff (launchAff)
|
import Control.Monad.Aff (Aff, launchAff)
|
||||||
|
import Control.Monad.Aff.AVar (AVAR)
|
||||||
|
import Control.Monad.Aff.Console (CONSOLE)
|
||||||
import Control.Monad.Eff (Eff)
|
import Control.Monad.Eff (Eff)
|
||||||
import Control.Monad.Eff.Class (liftEff)
|
import Control.Monad.Eff.Class (liftEff)
|
||||||
import Control.Monad.Eff.Exception (EXCEPTION, error)
|
import Control.Monad.Eff.Exception (EXCEPTION, error)
|
||||||
import Control.Monad.Error.Class (throwError, try)
|
import Control.Monad.Eff.Now (NOW)
|
||||||
|
import Control.Monad.Error.Class (catchError, throwError, try)
|
||||||
|
import Data.DateTime.Instant (Instant, unInstant)
|
||||||
import Data.Decimal as D
|
import Data.Decimal as D
|
||||||
import Data.Maybe (Maybe(..))
|
import Data.Foldable (all)
|
||||||
import Database.PostgreSQL (POSTGRESQL, PoolConfiguration, Query(Query), Row0(Row0), Row1(Row1), Row2(Row2), Row3(Row3), Row9(Row9), execute, newPool, query, scalar, withConnection, withTransaction)
|
import Data.JSDate (toInstant)
|
||||||
|
import Data.JSDate as JSDate
|
||||||
|
import Data.Maybe (Maybe(..), fromJust)
|
||||||
|
import Data.Newtype (unwrap)
|
||||||
|
import Database.PostgreSQL (Connection, POSTGRESQL, PoolConfiguration, Query(Query), Row0(Row0), Row1(Row1), Row2(Row2), Row3(Row3), Row9(Row9), execute, newPool, query, scalar, withConnection, withTransaction)
|
||||||
|
import Math ((%))
|
||||||
|
import Partial.Unsafe (unsafePartial)
|
||||||
import Test.Assert (ASSERT, assert)
|
import Test.Assert (ASSERT, assert)
|
||||||
|
import Test.Unit (suite)
|
||||||
|
import Test.Unit as Test.Unit
|
||||||
|
import Test.Unit.Console (TESTOUTPUT)
|
||||||
|
import Test.Unit.Main (runTest)
|
||||||
|
|
||||||
main :: ∀ eff. Eff (assert :: ASSERT, exception :: EXCEPTION, postgreSQL :: POSTGRESQL | eff) Unit
|
withRollback
|
||||||
|
:: ∀ eff
|
||||||
|
. Connection
|
||||||
|
-> Aff (postgreSQL :: POSTGRESQL | eff) Unit
|
||||||
|
-> Aff (postgreSQL :: POSTGRESQL | eff) Unit
|
||||||
|
withRollback conn action = do
|
||||||
|
execute conn (Query "BEGIN TRANSACTION") Row0
|
||||||
|
catchError (action >>= const rollback) (\e -> rollback >>= const (throwError e))
|
||||||
|
where
|
||||||
|
rollback = execute conn (Query "ROLLBACK") Row0
|
||||||
|
|
||||||
|
test
|
||||||
|
:: ∀ eff
|
||||||
|
. Connection
|
||||||
|
-> String
|
||||||
|
-> Aff ( postgreSQL :: POSTGRESQL | eff) Unit
|
||||||
|
-> Test.Unit.TestSuite (postgreSQL :: POSTGRESQL | eff)
|
||||||
|
test conn t a = Test.Unit.test t (withRollback conn a)
|
||||||
|
|
||||||
|
now :: ∀ eff. Eff (now :: NOW | eff) Instant
|
||||||
|
now = unsafePartial $ (fromJust <<< toInstant) <$> JSDate.now
|
||||||
|
|
||||||
|
main :: ∀ eff. Eff (assert :: ASSERT, avar :: AVAR, console :: CONSOLE, exception :: EXCEPTION, now :: NOW, postgreSQL :: POSTGRESQL, testOutput :: TESTOUTPUT | eff) Unit
|
||||||
main = void $ launchAff do
|
main = void $ launchAff do
|
||||||
pool <- newPool config
|
pool <- newPool config
|
||||||
withConnection pool \conn -> do
|
withConnection pool \conn -> do
|
||||||
@ -23,10 +59,45 @@ main = void $ launchAff do
|
|||||||
name text NOT NULL,
|
name text NOT NULL,
|
||||||
delicious boolean NOT NULL,
|
delicious boolean NOT NULL,
|
||||||
price NUMERIC(4,2) NOT NULL,
|
price NUMERIC(4,2) NOT NULL,
|
||||||
|
added TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (name)
|
PRIMARY KEY (name)
|
||||||
)
|
)
|
||||||
""") Row0
|
""") Row0
|
||||||
|
|
||||||
|
liftEff $ runTest $ do
|
||||||
|
suite "Postgresql client" $ do
|
||||||
|
let
|
||||||
|
testCount n = do
|
||||||
|
count <- scalar conn (Query """
|
||||||
|
SELECT count(*) = $1
|
||||||
|
FROM foods
|
||||||
|
""") (Row1 n)
|
||||||
|
liftEff <<< assert $ count == Just true
|
||||||
|
|
||||||
|
Test.Unit.test "transaction commit" $ do
|
||||||
|
withTransaction conn do
|
||||||
|
execute conn (Query """
|
||||||
|
INSERT INTO foods (name, delicious, price)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
""") (Row3 "pork" true (D.fromString "8.30"))
|
||||||
|
testCount 1
|
||||||
|
testCount 1
|
||||||
|
execute conn (Query """
|
||||||
|
DELETE FROM foods
|
||||||
|
""") Row0
|
||||||
|
|
||||||
|
Test.Unit.test "transaction rollback" $ do
|
||||||
|
_ <- try $ withTransaction conn do
|
||||||
|
execute conn (Query """
|
||||||
|
INSERT INTO foods (name, delicious, price)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
""") (Row3 "pork" true (D.fromString "8.30"))
|
||||||
|
testCount 1
|
||||||
|
throwError $ error "fail"
|
||||||
|
testCount 0
|
||||||
|
|
||||||
|
let
|
||||||
|
insertFood =
|
||||||
execute conn (Query """
|
execute conn (Query """
|
||||||
INSERT INTO foods (name, delicious, price)
|
INSERT INTO foods (name, delicious, price)
|
||||||
VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9)
|
VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9)
|
||||||
@ -34,61 +105,41 @@ main = void $ launchAff do
|
|||||||
"pork" true (D.fromString "8.30")
|
"pork" true (D.fromString "8.30")
|
||||||
"sauerkraut" false (D.fromString "3.30")
|
"sauerkraut" false (D.fromString "3.30")
|
||||||
"rookworst" true (D.fromString "5.60"))
|
"rookworst" true (D.fromString "5.60"))
|
||||||
|
test conn "select column subset" $ do
|
||||||
|
insertFood
|
||||||
names <- query conn (Query """
|
names <- query conn (Query """
|
||||||
SELECT name
|
SELECT name, delicious
|
||||||
FROM foods
|
FROM foods
|
||||||
WHERE delicious
|
WHERE delicious
|
||||||
ORDER BY name ASC
|
ORDER BY name ASC
|
||||||
""") Row0
|
""") Row0
|
||||||
|
liftEff <<< assert $ names == [Row2 "pork" true, Row2 "rookworst" true]
|
||||||
|
|
||||||
liftEff <<< assert $ names == [Row1 "pork", Row1 "rookworst"]
|
test conn "select default instant value" $ do
|
||||||
|
before <- liftEff $ (unwrap <<< unInstant) <$> now
|
||||||
|
insertFood
|
||||||
|
added <- query conn (Query """
|
||||||
|
SELECT added
|
||||||
|
FROM foods
|
||||||
|
""") Row0
|
||||||
|
after <- liftEff $ (unwrap <<< unInstant) <$> now
|
||||||
|
-- | timestamps are fetched without milliseconds so we have to
|
||||||
|
-- | round before value down
|
||||||
|
liftEff <<< assert $ all
|
||||||
|
(\(Row1 t) ->
|
||||||
|
( unwrap $ unInstant t) >= (before - before % 1000.0)
|
||||||
|
&& after >= (unwrap $ unInstant t))
|
||||||
|
added
|
||||||
|
|
||||||
sour <- query conn (Query """
|
test conn "select decimal" $ do
|
||||||
SELECT name, price
|
insertFood
|
||||||
|
sauerkrautPrice <- query conn (Query """
|
||||||
|
SELECT price
|
||||||
FROM foods
|
FROM foods
|
||||||
WHERE NOT delicious
|
WHERE NOT delicious
|
||||||
ORDER BY name ASC
|
|
||||||
""") Row0
|
""") Row0
|
||||||
liftEff <<< assert $ sour == [Row2 "sauerkraut" (D.fromString "3.30")]
|
liftEff <<< assert $ sauerkrautPrice == [Row1 (D.fromString "3.30")]
|
||||||
|
|
||||||
testTransactionCommit conn
|
|
||||||
testTransactionRollback conn
|
|
||||||
|
|
||||||
pure unit
|
|
||||||
where
|
|
||||||
testTransactionCommit conn = do
|
|
||||||
deleteAll conn
|
|
||||||
withTransaction conn do
|
|
||||||
execute conn (Query """
|
|
||||||
INSERT INTO foods (name, delicious, price)
|
|
||||||
VALUES ($1, $2, $3)
|
|
||||||
""") (Row3 "pork" true (D.fromString "8.30"))
|
|
||||||
testCount conn 1
|
|
||||||
testCount conn 1
|
|
||||||
|
|
||||||
testTransactionRollback conn = do
|
|
||||||
deleteAll conn
|
|
||||||
_ <- try $ withTransaction conn do
|
|
||||||
execute conn (Query """
|
|
||||||
INSERT INTO foods (name, delicious, price)
|
|
||||||
VALUES ($1, $2, $3)
|
|
||||||
""") (Row3 "pork" true (D.fromString "8.30"))
|
|
||||||
testCount conn 1
|
|
||||||
throwError $ error "fail"
|
|
||||||
testCount conn 0
|
|
||||||
|
|
||||||
deleteAll conn =
|
|
||||||
execute conn (Query """
|
|
||||||
DELETE FROM foods
|
|
||||||
""") Row0
|
|
||||||
|
|
||||||
testCount conn n = do
|
|
||||||
count <- scalar conn (Query """
|
|
||||||
SELECT count(*) = $1
|
|
||||||
FROM foods
|
|
||||||
""") (Row1 n)
|
|
||||||
liftEff <<< assert $ count == Just true
|
|
||||||
|
|
||||||
config :: PoolConfiguration
|
config :: PoolConfiguration
|
||||||
config =
|
config =
|
||||||
|
Loading…
Reference in New Issue
Block a user