packages feed

poppy-1.0.0: test/Poppy/ClientWriteSpec.hs

module Poppy.ClientWriteSpec
  ( clientWriteSpec,
  )
where

import Data.Text (Text)
import Data.UUID.V4 (nextRandom)
import Poppy (NullableValue (Omit, Value), ORMError (..), eq, runDb)
import qualified Schema.Client.Widget as Widget
import Schema.Widget (WidgetCreate (..), WidgetRow (..), WidgetUpdate (..), widgetName)
import Support.Assert (assertRight)
import Support.TestDb (TestEnv (..))
import Test.Hspec (SpecWith, describe, it, shouldBe, shouldMatchList, shouldSatisfy)

clientWriteSpec :: SpecWith TestEnv
clientWriteSpec =
  describe "generated Client batch writes" $ do
    it "createMany inserts rows and counts them" $ \TestEnv {envPool = pool} -> do
      n <-
        runDb pool (Widget.createMany [widgetCreate "batch-a", widgetCreate "batch-b"])
          >>= assertRight
      n `shouldBe` 2
      rows <- runDb pool (Widget.findMany Widget.emptyQuery)
      map (.name) rows `shouldMatchList` ["batch-a", "batch-b"]

    it "createMany rolls back when one insert fails" $ \TestEnv {envPool = pool} -> do
      uid <- nextRandom
      result <-
        runDb
          pool
          ( Widget.createMany
              [ (widgetCreate "keep-me") {id = Just uid},
                (widgetCreate "also-same-id") {id = Just uid}
              ]
          )
      result
        `shouldSatisfy` ( \case
                            Left (UniqueViolation _) -> True
                            _ -> False
                        )
      rows <- runDb pool (Widget.findMany Widget.emptyQuery)
      rows `shouldBe` []

    it "updateMany updates matching rows" $ \TestEnv {envPool = pool} -> do
      _ <- runDb pool (Widget.create (widgetCreate "before")) >>= assertRight
      _ <- runDb pool (Widget.create (widgetCreate "other")) >>= assertRight
      n <-
        runDb
          pool
          ( Widget.updateMany
              (eq widgetName "before")
              widgetUpdate {name = Just "after"}
          )
          >>= assertRight
      n `shouldBe` 1
      rows <- runDb pool (Widget.findMany Widget.emptyQuery)
      map (.name) rows `shouldMatchList` ["after", "other"]

    it "deleteMany deletes matching rows" $ \TestEnv {envPool = pool} -> do
      _ <- runDb pool (Widget.create (widgetCreate "keep")) >>= assertRight
      _ <- runDb pool (Widget.create (widgetCreate "drop")) >>= assertRight
      n <- runDb pool (Widget.deleteMany (eq widgetName "drop")) >>= assertRight
      n `shouldBe` 1
      rows <- runDb pool (Widget.findMany Widget.emptyQuery)
      map (.name) rows `shouldBe` ["keep"]

    it "upsert OnName updates on conflict" $ \TestEnv {envPool = pool} -> do
      inserted <-
        runDb
          pool
          (Widget.upsert Widget.OnName (widgetCreate "first") widgetUpdate)
          >>= assertRight
      inserted.name `shouldBe` "first"
      updated <-
        runDb
          pool
          ( Widget.upsert
              Widget.OnName
              (widgetCreate "first")
              widgetUpdate {name = Just "second"}
          )
          >>= assertRight
      updated.id `shouldBe` inserted.id
      updated.name `shouldBe` "second"
      rows <- runDb pool (Widget.findMany Widget.emptyQuery)
      map (.name) rows `shouldBe` ["second"]

    it "update and delete take a unique key" $ \TestEnv {envPool = pool} -> do
      created <- runDb pool (Widget.create (widgetCreate "sage")) >>= assertRight
      same <-
        runDb pool (Widget.update (Widget.ByName "sage") widgetUpdate)
          >>= assertRight
      same.id `shouldBe` created.id
      updated <-
        runDb
          pool
          ( Widget.update
              (Widget.ByName "sage")
              widgetUpdate {description = Value "fresh"}
          )
          >>= assertRight
      updated.description `shouldBe` Just "fresh"
      n <-
        runDb pool (Widget.delete (Widget.ById created.id))
          >>= assertRight
      n `shouldBe` 1

widgetCreate :: Text -> WidgetCreate
widgetCreate name =
  WidgetCreate
    { id = Nothing,
      createdAt = Nothing,
      updatedAt = Nothing,
      name,
      description = Omit
    }

widgetUpdate :: WidgetUpdate
widgetUpdate =
  WidgetUpdate
    { createdAt = Nothing,
      updatedAt = Nothing,
      name = Nothing,
      description = Omit
    }