packages feed

valiant-0.1.0.0: integration/PipelineSpec.hs

module PipelineSpec (spec) where

import Data.Int (Int32, Int64)
import Data.Text (Text)
import Valiant
import PgWire.Protocol.Oid (oidInt4)
import TestSupport
import Test.Hspec

stmtFetchOne :: Statement Int32 (Int32, Text, Maybe Text)
stmtFetchOne = mkStatement
  "SELECT id, name, email FROM users WHERE id = $1"
  [23] ["id", "name", "email"] "<test>"

stmtCount :: Statement () Int64
stmtCount = mkStatement
  "SELECT count(*) FROM users"
  [] ["count"] "<test>"

stmtListAll :: Statement () (Int32, Text)
stmtListAll = mkStatement
  "SELECT id, name FROM users ORDER BY id"
  [] ["id", "name"] "<test>"

spec :: Spec
spec = do
  describe "runPipeline" $ do
    it "executes multiple queries in one round-trip" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        insertTestUsers conn
        (mUser, count) <- runPipeline conn $ (,)
          <$> pipeFetchOne stmtFetchOne 1
          <*> pipeFetchScalar stmtCount ()
        case mUser of
          Just (_, name, _) -> name `shouldBe` "Alice"
          Nothing -> expectationFailure "Expected user"
        count `shouldBe` 5

    it "handles mixed result types" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        insertTestUsers conn
        (users, count, mMissing) <- runPipeline conn $ (,,)
          <$> pipeFetchAll stmtListAll ()
          <*> pipeFetchScalar stmtCount ()
          <*> pipeFetchOne stmtFetchOne 999
        length users `shouldBe` 5
        count `shouldBe` 5
        mMissing `shouldBe` Nothing

    it "handles empty pipeline" $ do
      withTestConnection $ \conn -> do
        result <- runPipeline conn (pure 42 :: Pipeline Int)
        result `shouldBe` 42

    it "handles single query" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        insertTestUsers conn
        count <- runPipeline conn $ pipeFetchScalar stmtCount ()
        count `shouldBe` 5

  describe "fetchBatchOne" $ do
    it "fetches multiple rows by different params" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        insertTestUsers conn
        results <- fetchBatchOne conn stmtFetchOne [1, 2, 3, 999]
        length results `shouldBe` 4
        -- First 3 should be Just, last should be Nothing
        case results of
          [Just (_, n1, _), Just (_, n2, _), Just (_, n3, _), Nothing] -> do
            n1 `shouldBe` "Alice"
            n2 `shouldBe` "Bob"
            n3 `shouldBe` "Carol"
          _ -> expectationFailure $ "Unexpected: " <> show (length results)

    it "handles empty list" $ do
      withTestConnection $ \conn -> do
        results <- fetchBatchOne conn stmtFetchOne ([] :: [Int32])
        results `shouldBe` []

  describe "fetchBatchAll" $ do
    it "fetches multiple result sets" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        insertTestUsers conn
        results <- fetchBatchAll conn stmtListAll [(), ()]
        length results `shouldBe` 2
        case results of
          (r : _) -> length r `shouldBe` 5
          [] -> expectationFailure "Expected non-empty results"
        length (results !! 1) `shouldBe` 5

  describe "fetchByIds" $ do
    it "fetches multiple rows by array parameter" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        insertTestUsers conn
        users <- fetchByIds conn
          "SELECT id, name, email FROM users WHERE id = ANY($1::int4[])"
          oidInt4
          [1 :: Int32, 2, 3]
        length (users :: [(Int32, Text, Maybe Text)]) `shouldBe` 3

    it "returns empty for no matches" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        insertTestUsers conn
        users <- fetchByIds conn
          "SELECT id, name, email FROM users WHERE id = ANY($1::int4[])"
          oidInt4
          [999 :: Int32, 998]
        length (users :: [(Int32, Text, Maybe Text)]) `shouldBe` 0

    it "handles empty ID list" $ do
      withTestConnection $ \conn -> withSchema conn $ do
        users <- fetchByIds conn
          "SELECT id, name, email FROM users WHERE id = ANY($1::int4[])"
          oidInt4
          ([] :: [Int32])
        length (users :: [(Int32, Text, Maybe Text)]) `shouldBe` 0