packages feed

poppy-1.0.0: test/Poppy/SelectSpec.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedRecordDot #-}

module Poppy.SelectSpec
  ( selectSpec,
  )
where

import Poppy (ORMError (..), Picked (..), asc, desc, loadWith, runDb, skip)
import qualified Poppy.Internal.Operations as Ops
import Poppy.Internal.Query (selectColumns)
import Poppy.Internal.Select (picked)
import qualified Poppy.ShelfFixtures as ShelfFixtures
import Poppy.Internal.Where (eq)
import qualified Poppy.WidgetFixtures as WidgetFixtures
import Schema.Book (BookRow (..))
import qualified Schema.Client.Shelf as Shelf
import qualified Schema.Client.Widget as Widget
import Schema.Include.Book (BookInclude (..), BookWith (..))
import Schema.Include.Shelf (ShelfInclude (..), ShelfWith (..), ShelfWithPicked (..))
import Schema.Shelf (ShelfPicked (..), ShelfRow (..), shelfId, shelfName)
import Schema.Widget
  ( WidgetPicked (..),
    WidgetRow (..),
    WidgetSelect (..),
    WidgetTable,
    parseWidgetPicked,
    widgetCreatedAt,
    widgetName,
    widgetSelectColumns,
  )
import Support.Assert (assertRight)
import Support.TestDb (TestEnv (..))
import Test.Hspec (SpecWith, describe, it, shouldBe, shouldSatisfy)

selectSpec :: SpecWith TestEnv
selectSpec = do
  describe "Poppy.Internal.Select columns" $ do
    it "always includes the primary key and only requested scalars" $ \_ ->
      widgetSelectColumns
        WidgetSelect
          { id = False,
            createdAt = False,
            updatedAt = False,
            name = True,
            description = True
          }
        `shouldBe` ["id", "name", "description"]

    it "maps False fields to Skipped" $ \_ -> do
      picked False ("secret" :: String) `shouldBe` Skipped
      picked True ("shown" :: String) `shouldBe` Picked "shown"

  describe "single-table select_" $ do
    it "loads only requested widget columns into WidgetPicked" $ \TestEnv {envPool = pool} -> do
      widget <- WidgetFixtures.insertWidget pool "alpha"
      let sel =
            WidgetSelect
              { id = False,
                createdAt = False,
                updatedAt = False,
                name = True,
                description = False
              }
      [row] <-
        runDb pool $
          Ops.findManyWith @WidgetTable
            (parseWidgetPicked sel)
            (selectColumns (widgetSelectColumns sel))
      let WidgetRow {id = widgetId} = widget
          WidgetPicked {id = rowId, name = rowName, description = rowDescription, createdAt = rowCreatedAt, updatedAt = rowUpdatedAt} = row
      rowId `shouldBe` widgetId
      rowName `shouldBe` Picked "alpha"
      rowDescription `shouldBe` Skipped
      rowCreatedAt `shouldBe` Skipped
      rowUpdatedAt `shouldBe` Skipped

    it "keeps WidgetRow when select_ is OmitSelect" $ \TestEnv {envPool = pool} -> do
      created <- WidgetFixtures.insertWidget pool "salt"
      rows <- runDb pool (Widget.findMany Widget.emptyQuery)
      map (.name) rows `shouldBe` ["salt"]
      map (.id) rows `shouldBe` [created.id]

    it "findUnique looks up by primary key" $ \TestEnv {envPool = pool} -> do
      created <- WidgetFixtures.insertWidget pool "thyme"
      found <-
        runDb
          pool
          (Widget.findUnique (Widget.uniqueQuery (Widget.ById created.id)))
      found `shouldBe` Right (Just created)

    it "findUnique looks up by a declared unique" $ \TestEnv {envPool = pool} -> do
      created <- WidgetFixtures.insertWidget pool "sage"
      found <-
        runDb
          pool
          (Widget.findUnique (Widget.uniqueQuery (Widget.ByName "sage")))
      found `shouldBe` Right (Just created)

    it "findUnique projects columns when select_ is set" $ \TestEnv {envPool = pool} -> do
      created <- WidgetFixtures.insertWidget pool "basil"
      let sel =
            WidgetSelect
              { id = False,
                createdAt = False,
                updatedAt = False,
                name = True,
                description = False
              }
      found <-
        runDb
          pool
          (Widget.findUnique ((Widget.uniqueQuery (Widget.ById created.id)) {Widget.select_ = sel}))
          >>= assertRight
      fmap (.name) found `shouldBe` Just (Picked "basil")

    it "returns WidgetPicked when select_ is set" $ \TestEnv {envPool = pool} -> do
      created <- WidgetFixtures.insertWidget pool "pepper"
      let sel =
            WidgetSelect
              { id = False,
                createdAt = False,
                updatedAt = False,
                name = True,
                description = False
              }
          query :: Widget.WidgetQuery WidgetSelect
          query =
            Widget.WidgetQuery
              { select_ = sel,
                where_ = Nothing,
                orderBy_ = [],
                limit_ = Nothing,
                offset_ = Nothing
              }
      [row] <- runDb pool (Widget.findMany query)
      row.id `shouldBe` created.id
      row.name `shouldBe` Picked "pepper"
      row.createdAt `shouldBe` Skipped
      row.updatedAt `shouldBe` Skipped

    it "findMany orderBy_ sorts by listed fields" $ \TestEnv {envPool = pool} -> do
      _ <- WidgetFixtures.insertWidget pool "beta"
      _ <- WidgetFixtures.insertWidget pool "alpha"
      _ <- WidgetFixtures.insertWidget pool "gamma"
      ascending <-
        runDb
          pool
          (Widget.findMany Widget.emptyQuery {Widget.orderBy_ = [asc widgetName]})
      descending <-
        runDb
          pool
          (Widget.findMany Widget.emptyQuery {Widget.orderBy_ = [desc widgetName]})
      multi <-
        runDb
          pool
          ( Widget.findMany
              Widget.emptyQuery {Widget.orderBy_ = [asc widgetName, desc widgetCreatedAt]}
          )
      map (.name) ascending `shouldBe` ["alpha", "beta", "gamma"]
      map (.name) descending `shouldBe` ["gamma", "beta", "alpha"]
      map (.name) multi `shouldBe` ["alpha", "beta", "gamma"]

    it "count and findFirst use the generated Client" $ \TestEnv {envPool = pool} -> do
      _ <- WidgetFixtures.insertWidget pool "beta"
      alpha <- WidgetFixtures.insertWidget pool "alpha"
      n <- runDb pool (Widget.count Widget.emptyQuery {Widget.where_ = Just (eq widgetName "alpha")})
      n `shouldBe` 1
      firstAsc <-
        runDb
          pool
          ( Widget.findFirst
              Widget.emptyQuery {Widget.orderBy_ = [asc widgetName]}
          )
      firstAsc `shouldBe` Just alpha
      missing <-
        runDb
          pool
          ( Widget.findFirstOrFail
              Widget.emptyQuery {Widget.where_ = Just (eq widgetName "missing")}
          )
      missing
        `shouldSatisfy` ( \case
                            Left (RecordNotFound _) -> True
                            _ -> False
                        )

  describe "select_ + include" $ do
    it "projects shelf scalars and keeps nested books" $ \TestEnv {envPool = pool} -> do
      shelf <- ShelfFixtures.insertShelf pool "fiction"
      _ <- ShelfFixtures.insertBook pool shelf.id "Dune"
      let sel =
            Shelf.ShelfSelect
              { id = False,
                name = True
              }
          query =
            Shelf.ShelfQuery
              { include_ = shelfInclude,
                select_ = sel,
                where_ = Just (eq shelfName "fiction"),
                orderBy_ = [],
                limit_ = Nothing,
                offset_ = Nothing
              }
      [row] <- runDb pool (Shelf.findMany query)
      row.shelf.id `shouldBe` shelf.id
      row.shelf.name `shouldBe` Picked "fiction"
      length row.books `shouldBe` 1
      (head row.books).book.title `shouldBe` "Dune"

    it "findMany emptyQuery returns ShelfRow without include_" $ \TestEnv {envPool = pool} -> do
      created <- ShelfFixtures.insertShelf pool "Toast"
      rows <- runDb pool (Shelf.findMany Shelf.emptyQuery)
      map (.name) rows `shouldBe` ["Toast"]
      map (.id) rows `shouldBe` [created.id]
      found <-
        runDb
          pool
          (Shelf.findUnique (Shelf.uniqueQuery (Shelf.ById created.id)))
          >>= assertRight
      fmap (.name) found `shouldBe` Just "Toast"

    it "findUnique loads included relations" $ \TestEnv {envPool = pool} -> do
      shelf <- ShelfFixtures.insertShelf pool "Omelette"
      _ <- ShelfFixtures.insertBook pool shelf.id "Eggs"
      found <-
        runDb
          pool
          (Shelf.findUnique ((Shelf.uniqueQuery (Shelf.ById shelf.id)) {Shelf.include_ = shelfInclude}))
          >>= assertRight
      fmap (.shelf.name) found `shouldBe` Just "Omelette"
      fmap (length . (.books)) found `shouldBe` Just 1

    it "create and findMany share Schema.Client.Shelf" $ \TestEnv {envPool = pool} -> do
      created <-
        runDb pool (Shelf.create (Shelf.ShelfCreate {id = Nothing, name = "Pantry", books = [], tags = []}))
          >>= assertRight
      rows <-
        runDb
          pool
          ( Shelf.findMany
              Shelf.emptyQuery
                { Shelf.include_ = shelfInclude,
                  Shelf.where_ = Just (eq shelfId created.id)
                }
          )
      map ((.name) . (.shelf)) rows `shouldBe` ["Pantry"]
      map ((.id) . (.shelf)) rows `shouldBe` [created.id]

shelfInclude =
  ShelfInclude {books = loadWith (BookInclude {chapters = skip}), tags = skip}