packages feed

sqel-0.0.1.0: test/Sqel/Test/StatementTest.hs

{-# options_ghc -Wno-partial-type-signatures -fconstraint-solver-iterations=10 #-}

module Sqel.Test.StatementTest where

import Data.Type.Equality ((:~:) (Refl))
import Generics.SOP (NP (..))
import Hedgehog (TestT, (===))
import Prelude hiding (sum)
import Test (unitTest)
import Test.Tasty (TestTree, testGroup)

import Sqel.Class.MatchView (HasPath)
import Sqel.Comp (Column, CompName (compName))
import Sqel.Data.Codec (FullCodec)
import Sqel.Data.Dd (
  Dd (Dd),
  DdInc (DdNest),
  DdK (DdK),
  DdSort (DdProd),
  DdStruct (DdComp),
  DdType,
  DdTypeSel,
  type (:>) ((:>)),
  )
import Sqel.Data.Mods (pattern NoMods)
import Sqel.Data.Order (Order (Desc))
import Sqel.Data.QuerySchema (QuerySchema)
import Sqel.Data.Sel (MkTSel (mkTSel), Sel (SelAuto), SelW (SelWAuto))
import Sqel.Data.Sql (Sql, sql, toSql)
import Sqel.Data.SqlFragment (Create (Create), Select (Select))
import Sqel.Data.TableSchema (TableSchema)
import Sqel.Data.Uid (Uid)
import Sqel.Merge (merge)
import Sqel.PgType (MkTableSchema, tableSchema)
import Sqel.Prim (prim, primAs, primNewtypes, prims)
import Sqel.Product (prod, prodSel)
import Sqel.Query (checkQuery)
import Sqel.Query.Combinators (order)
import Sqel.ReifyCodec (ReifyCodec)
import Sqel.ReifyDd (ReifyDd)
import qualified Sqel.Sql.Select as Sql
import Sqel.Sum (ConColumn (con), con1, sum)
import Sqel.Test.Bug ()
import qualified Sqel.Type as T
import Sqel.Type (Prim, PrimNewtype, Prod, ProdPrimsNewtype, TypeSel, type (*>), type (>))
import Sqel.Uid (UidDd, uid)

newtype IntNt =
  IntNt { unIntNt :: Int }
  deriving stock (Eq, Show, Generic)

data Three =
  Three {
    a :: IntNt,
    b :: IntNt,
    c :: IntNt
  }
  deriving stock (Eq, Show, Generic)

type ThreeTableGen =
  ProdPrimsNewtype Three

type ThreeTable =
  Prod Three *> PrimNewtype "a" IntNt > PrimNewtype "b" IntNt > PrimNewtype "c" IntNt

test_prodGen :: TestT IO ()
test_prodGen =
  case Refl :: ThreeTable :~: ThreeTableGen of
    Refl -> unit

ddThree :: Dd ThreeTableGen
ddThree =
  prod primNewtypes

data Pro =
  Pro {
    num :: Int64,
    name :: Text
  }
  deriving stock (Eq, Show, Generic)

data Dat =
  Dat {
    num :: Int
  }
  deriving stock (Eq, Show, Generic)

data Q =
  Q {
    num :: ()
  }
  deriving stock (Eq, Show, Generic)

type ProdTable =
  Prod Pro *> (Prim "num" Int64 > Prim "name" Text)

ddPro :: Dd ProdTable
ddPro =
  prod prims

target_order :: Sql
target_order =
  [sql|select "num" from "dat" order by "num" desc|]

test_statement_order :: TestT IO ()
test_statement_order =
  target_order === Sql.selectWhere qs ts
  where
    ts :: TableSchema Dat
    ts = tableSchema (prod prim)
    qs :: QuerySchema Q Dat
    qs = checkQuery (prod (order Desc)) (prod prim)

target_mergeSum :: Sql
target_mergeSum =
  [sql|select "id", "sqel_sum_index__merge_sum", ("merge_sum1").num1, ("merge_sum1").name1, ("merge_sum2").num2,
       ("merge_sum2").name2 from "merge_sum"|]

target_create_mergeSum :: Sql
target_create_mergeSum =
  [sql|create table "merge_sum"
  ("id" bigint not null,
    "sqel_sum_index__merge_sum" bigint not null,
    "merge_sum1" sqel_type__merge_sum1 not null,
    "merge_sum2" sqel_type__merge_sum2 not null)
  |]

data MergeSum =
  MergeSum1 { num1 :: Int, name1 :: Text }
  |
  MergeSum2 { num2 :: Int, name2 :: Text }
  deriving stock (Eq, Show, Generic)

dd_uid_merge_sum_manual :: Dd ('DdK _ _ (Uid Int64 MergeSum) _)
dd_uid_merge_sum_manual =
  prod (prim :> merge (sum (con prims :> con prims)))

dd_uid_merge_sum :: Dd ('DdK _ _ (Uid Int64 MergeSum) _)
dd_uid_merge_sum =
  uid prim (sum (con prims :> con prims))

test_statement_merge_sum :: TestT IO ()
test_statement_merge_sum = do
  target_mergeSum === toSql (Select (tableSchema dd_uid_merge_sum))
  target_mergeSum === toSql (Select (tableSchema dd_uid_merge_sum_manual))
  target_create_mergeSum === toSql (Create (tableSchema dd_uid_merge_sum_manual))

data MergeProd =
  MergeProd { count :: Int, b :: Pro }
  deriving stock (Eq, Show, Generic)

dd_merge_prod :: Dd ('DdK _ _ MergeProd _)
dd_merge_prod =
  prod (prim :> merge (prod prims))

target_merge_prod :: Sql
target_merge_prod =
  [sql|select "count", "num", "name" from "merge_prod"|]

test_statement_merge_prod :: TestT IO ()
test_statement_merge_prod =
  target_merge_prod === toSql (Select (tableSchema dd_merge_prod))

data Wrap a =
  Wrap { wrapped :: a, length :: Int64 }
  deriving stock (Eq, Show, Generic)

instance CompName a name => CompName (Wrap a) name where
  compName = compName @a

type WrapDd sa =
  TypeSel (DdTypeSel sa) (Prod (Wrap (DdType sa))) *> (
    T.Merge sa >
    Prim "length" Int64
  )

schema_higherOrder ::
  ∀ s0 table a sel mods s .
  s0 ~ 'DdK sel mods a s =>
  table ~ WrapDd s0 =>
  MkTSel (DdTypeSel s0) =>
  MkTableSchema table =>
  Dd s0 ->
  TableSchema (Wrap a)
schema_higherOrder wrapped =
  tableSchema dd
  where
    dd :: Dd table
    dd = Dd SelWAuto NoMods (DdComp mkTSel DdProd DdNest fields)
    fields = merge wrapped :* primAs @"length" :* Nil

target_higherOrder :: Sql
target_higherOrder =
  [sql|select "num", "name", "length" from "pro"|]

test_statement_higherOrder :: TestT IO ()
test_statement_higherOrder =
  target_higherOrder === toSql (Select (schema_higherOrder ddPro))

data Merge1 =
  One { one :: Pro }
  |
  Two { two :: Pro }
  deriving stock (Eq, Show, Generic)

data QHo = QHo { name :: Text }
  deriving stock (Eq, Show, Generic)

data QWrap = QWrap { two :: QHo }
  deriving stock (Eq, Show, Generic)

statement_query_higherOrder ::
  ∀ a s0 table mods s .
  s0 ~ 'DdK 'SelAuto mods a s =>
  MkTSel (DdTypeSel s0) =>
  table ~ UidDd (Prim "id" Int64) (WrapDd s0) =>
  MkTableSchema table =>
  HasPath ["two", "name"] Text table =>
  Dd s0 ->
  Sql
statement_query_higherOrder wrapped =
  Sql.selectWhere qs ts
  where
    ts :: TableSchema (Uid Int64 (Wrap a))
    ts = tableSchema dd
    qs :: QuerySchema QWrap (Uid Int64 (Wrap a))
    qs = checkQuery q dd
    q = prod (prod prim)
    dd :: Dd table
    dd = uid prim pro
    pro = Dd SelWAuto NoMods (DdComp mkTSel DdProd DdNest fields)
    fields = merge wrapped :* primAs @"length" :* Nil

ddMerge1 :: Dd ('DdK _ _ Merge1 _)
ddMerge1 = sum (con1 ddPro :> con1 ddPro)

target_merge_query_higherOrder :: Sql
target_merge_query_higherOrder =
  [sql|select "id", "sqel_sum_index__merge1", ("one").num, ("one").name, ("two").num, ("two").name, "length"
       from "merge1" where ((("two")."name" = $1))|]

test_statement_merge_query_higherOrder :: TestT IO ()
test_statement_merge_query_higherOrder =
  target_merge_query_higherOrder === statement_query_higherOrder ddMerge1

ddHigherOrder2 ::
  ∀ s merged .
  merged ~ T.Merge s =>
  MkTSel (DdTypeSel s) =>
  Column (DdType s) "wrapped" merged merged =>
  Dd s ->
  Dd (UidDd (Prim "id" Int64) (WrapDd s))
ddHigherOrder2 wrapped =
  uid prim (prodSel @(DdTypeSel s) (merge wrapped :> prim))

ddUidWrapPro :: Dd (UidDd (Prim "id" Int64) (WrapDd ProdTable))
ddUidWrapPro =
  ddHigherOrder2 ddPro

higherOrder2 ::
  ∀ a s merged .
  merged ~ T.Merge s =>
  MkTSel (DdTypeSel s) =>
  Column a "wrapped" merged merged =>
  ReifyDd merged =>
  ReifyCodec FullCodec merged a =>
  Dd s ->
  Sql
higherOrder2 wrapped =
  toSql (Select ts)
  where
    ts :: TableSchema (Wrap a)
    ts = tableSchema dd
    dd = prodSel @(DdTypeSel s) (merge wrapped :> prim)

test_higherOrder2 :: TestT IO ()
test_higherOrder2 =
  [sql|select "num", "name", "length" from "pro"|] === higherOrder2 ddPro

data NaNu =
  Na { name :: Text }
  |
  Nu Int64
  deriving stock (Eq, Show, Generic)

ddNaNu :: Dd ('DdK _ _ NaNu _)
ddNaNu =
  sum (con1 prim :> con1 prim)

statement_con1 :: Sql
statement_con1 =
  toSql (Select (tableSchema ddNaNu))

test_statement_con1 :: TestT IO ()
test_statement_con1 =
  [sql|select "sqel_sum_index__na_nu", "name", "nu" from "na_nu"|] === statement_con1

newtype TextNt =
  TextNt { unTextNt :: Text }
  deriving stock (Eq, Show, Generic)
  deriving newtype (IsString, Ord)

test_statement :: TestTree
test_statement =
  testGroup "statement" [
    unitTest "order" test_statement_order,
    unitTest "merge sum" test_statement_merge_sum,
    unitTest "merge prod" test_statement_merge_prod,
    unitTest "higher-order merge statement" test_statement_higherOrder,
    unitTest "higher-order double merge query" test_statement_merge_query_higherOrder,
    unitTest "higher-order with new product class" test_higherOrder2,
    unitTest "unary con with record and positional fields" test_statement_con1
  ]