test(spec): inline config into test suite

Previously, information about each test-suite was repeated in 3 separate
places:
- as a label and as implicit knowledge in the test-suite itself,
- as a comment in Main.hs, and
- as a configuration in SpecHelper.hs.

With this change, there will be a single source of truth in the test
suite itself. This will allow a single test-suite to easily test
multiple different configurations.
This commit is contained in:
Wolfgang Walther
2026-06-02 06:49:15 +00:00
parent 7803960cd1
commit 268ab00ed9
52 changed files with 280 additions and 439 deletions
@@ -1,16 +1,16 @@
module Feature.Query.AggregateFunctionsSpec where
import Network.Wai (Application)
import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude hiding (get)
import SpecHelper
allowed :: SpecWith ((), Application)
allowed =
allowed :: SpecWithConfig
allowed withConfig = withConfig (baseCfg { configDbAggregates = True }) $
describe "aggregate functions" $ do
context "performing a count without specifying a field" $ do
it "returns the count of all rows when no other fields are selected" $
@@ -306,8 +306,8 @@ allowed =
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson] }
disallowed :: SpecWith ((), Application)
disallowed =
disallowed :: SpecWithConfig
disallowed withConfig = withConfig baseCfg $
describe "attempting to use an aggregate when aggregate functions are disallowed" $ do
it "prevents the use of aggregates" $
get "/project_invoices?select=invoice_total.sum()" `shouldRespondWith`
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.AndOrParamsSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "and/or params used for complex boolean logic" $ do
context "used with GET" $ do
context "or param" $ do
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.ComputedRelsSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = describe "computed relationships" $ do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ describe "computed relationships" $ do
it "can define a many-to-one relationship with SETOF and ROWS 1 and embed" $
get "/videogames?select=name,designer:computed_designers(name)"
`shouldRespondWith`
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.CustomMediaSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
import Test.Hspec
@@ -12,8 +10,8 @@ import Text.Heredoc (str)
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = describe "custom media types" $ do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ describe "custom media types" $ do
context "for tables with aggregate" $ do
it "can query if there's an aggregate defined for the table" $ do
r <- request methodGet "/lines" (acceptHdrs "application/vnd.twkb") ""
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.DeleteSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "Deleting" $ do
context "existing record" $ do
it "succeeds with 204 and deletion count" $
@@ -1,7 +1,5 @@
module Feature.Query.EmbedDisambiguationSpec where
import Network.Wai (Application)
import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
@@ -9,8 +7,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "resource embedding disambiguation" $ do
context "ambiguous requests that give 300 Multiple Choices" $ do
it "errs when there are o2m and m2m cardinalities to the target table" $
@@ -1,7 +1,6 @@
module Feature.Query.EmbedInnerJoinSpec where
import Network.HTTP.Types
import Network.Wai (Application)
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +9,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "Embedding with an inner join" $ do
context "many-to-one relationships" $ do
it "ignores null embeddings while the default left join doesn't" $ do
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.ErrorSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
pgErrorCodeMapping :: SpecWith ((), Application)
pgErrorCodeMapping = do
pgErrorCodeMapping :: SpecWithConfig
pgErrorCodeMapping withConfig = withConfig baseCfg $ do
describe "PostreSQL error code mappings" $ do
it "should return 500 for cardinality_violation" $
get "/bad_subquery" `shouldRespondWith` 500
+2 -3
View File
@@ -1,7 +1,6 @@
module Feature.Query.InsertSpec where
import Data.List (lookup)
import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleHeaders))
import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai.Matcher (bodyEquals)
@@ -14,8 +13,8 @@ import Text.Heredoc
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ do
describe "Posting new record" $ do
context "disparate json types" $ do
it "accepts disparate json types" $ do
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.JsonOperatorSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = describe "json and jsonb operators" $ do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ describe "json and jsonb operators" $ do
context "Shaping response with select parameter" $ do
it "obtains a json subfield one level with casting" $
get "/complex_items?id=eq.1&select=settings->>foo::json" `shouldRespondWith`
@@ -1,22 +1,24 @@
module Feature.Query.MultipleSchemaSpec where
import Control.Lens ((^?))
import Control.Lens ((^?))
import Data.Aeson.Lens
import Data.Aeson.QQ
import Data.List.NonEmpty (fromList)
import Network.HTTP.Types
import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleHeaders), simpleBody)
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig (baseCfg { configDbSchemas = fromList ["v1", "v2", "SPECIAL \"@/\\#~_-"] }) $ describe "PostGIS features" $
describe "multiple schemas in single instance" $ do
context "Reading tables on different schemas" $ do
it "succeeds in reading table from default schema v1 if no schema is selected via header" $
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.NullsStripSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "Stripping null values from JSON response" $ do
let arrayStrip = ("Accept", "application/vnd.pgrst.array+json;nulls=stripped")
let singularStrip = ("Accept", "application/vnd.pgrst.object+json;nulls=stripped")
+9 -7
View File
@@ -1,16 +1,18 @@
module Feature.Query.PgSafeUpdateSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import Protolude hiding (get, put)
import PostgREST.Config (AppConfig (..))
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
spec :: SpecWith ((), Application)
spec =
import Protolude hiding (get, put)
import SpecHelper
spec :: SpecWithConfig
spec withConfig = withConfig (baseCfg { configDbPreRequest = Just $ QualifiedIdentifier "test" "load_safeupdate" }) $
describe "Enabling pg-safeupdate" $ do
context "Full table update" $ do
it "does not update and throws error if no condition is present" $
@@ -48,8 +50,8 @@ spec =
`shouldRespondWith`
204
disabledSpec :: SpecWith ((), Application)
disabledSpec =
disabledSpec :: SpecWithConfig
disabledSpec withConfig = withConfig baseCfg $
describe "Disabling pg-safeupdate" $ do
context "Full table update" $ do
it "works if no condition is present" $
+6 -5
View File
@@ -3,7 +3,6 @@
module Feature.Query.PlanSpec where
import Control.Lens ((^?))
import Network.Wai (Application)
import Network.Wai.Test (SResponse (..))
import Data.Aeson.Lens
@@ -15,11 +14,13 @@ import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = do
spec :: SpecWithConfig
spec withConfig = withConfig (baseCfg { configDbPlanEnabled = True }) $ do
describe "read table/view plan" $ do
it "outputs the total cost for a single filter on a table" $ do
r <- request methodGet "/projects?id=in.(1,2,3)"
@@ -529,8 +530,8 @@ spec = do
totalCost `shouldSatisfy` (> 67.0)
aggregateQty `shouldSatisfy` (> 1)
disabledSpec :: SpecWith ((), Application)
disabledSpec =
disabledSpec :: SpecWithConfig
disabledSpec withConfig = withConfig baseCfg $
it "doesn't work if db-plan-enabled=false(the default)" $ do
request methodGet "/projects?id=in.(1,2,3)"
(acceptHdrs "application/vnd.pgrst.plan") ""
+4 -4
View File
@@ -1,17 +1,17 @@
module Feature.Query.PostGISSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = describe "PostGIS features" $
spec :: SpecWithConfig
spec withConfig = withConfig (baseCfg { configDbExtraSearchPath = ["public", "extensions", "EXTRA \"@/\\#~_-"] }) $ describe "PostGIS features" $
context "GeoJSON output" $ do
it "works for a table that has a geometry column" $
request methodGet "/shops"
@@ -1,7 +1,5 @@
module Feature.Query.Preferences.HandlingSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "test Prefer: handling" $ do
context "check behaviour of Prefer: handling=strict" $ do
it "throws error when handling=strict and invalid prefs are given" $
@@ -1,17 +1,15 @@
module Feature.Query.Preferences.MaxAffectedSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
describe "test Prefer: max-affected" $ do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ describe "test Prefer: max-affected" $ do
context "test Prefer: max-affected with handling=strict" $ do
it "should fail if items deleted more than 10" $
request methodDelete "/items?id=lt.15"
@@ -1,18 +1,17 @@
module Feature.Query.Preferences.TimezoneSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude hiding (get)
import SpecHelper
enabledSpec :: SpecWith ((), Application)
enabledSpec =
describe "test Prefer: timezone with db-timezone-enabled is true" $ do
enabledSpec :: SpecWithConfig
enabledSpec withConfig = withConfig baseCfg $ describe "test Prefer: timezone with db-timezone-enabled is true" $ do
context "test Prefer: timezone=America/Los_Angeles" $ do
it "should change timezone with handling=strict" $
request methodGet "/timestamps"
@@ -62,8 +61,8 @@ enabledSpec =
, "Preference-Applied" <:> "handling=lenient"]}
disabledSpec :: SpecWith ((), Application)
disabledSpec =
disabledSpec :: SpecWithConfig
disabledSpec withConfig = withConfig (baseCfg { configDbTimezoneEnabled = False }) $
describe "test Prefer: timezone with db-timezone-enabled is false" $ do
context "test Prefer: timezone=America/Los_Angeles when timezone is disabled" $ do
it "should throw error with handling=strict" $
+4 -4
View File
@@ -1,17 +1,17 @@
module Feature.Query.QueryLimitedSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig (baseCfg { configDbMaxRows = Just 2 }) $
describe "Requesting many items with server limits(max-rows) enabled" $ do
it "restricts results" $
get "/items?order=id"
+2 -3
View File
@@ -1,6 +1,5 @@
module Feature.Query.QuerySpec where
import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleHeaders))
import Network.HTTP.Types
@@ -11,8 +10,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ do
describe "Querying a table with a column called count" $
it "should not confuse count column with pg_catalog.count aggregate" $
+2 -3
View File
@@ -1,6 +1,5 @@
module Feature.Query.RangeSpec where
import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleHeaders, simpleStatus))
import Network.HTTP.Types
@@ -11,8 +10,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ do
describe "GET /rpc/getitemrange" $ do
context "without range headers" $ do
context "with response under server size limit" $
@@ -1,17 +1,15 @@
module Feature.Query.RawOutputTypesSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import Protolude
import SpecHelper (acceptHdrs)
import SpecHelper
spec :: SpecWith ((), Application)
spec = describe "When raw-media-types config variable is missing or left empty" $ do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ describe "When raw-media-types config variable is missing or left empty" $ do
let firefoxAcceptHdrs = acceptHdrs "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
chromeAcceptHdrs = acceptHdrs "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"
it "responds json to a GET request with Firefox Accept headers" $
@@ -1,7 +1,5 @@
module Feature.Query.RelatedQueriesSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -10,8 +8,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = describe "related queries" $ do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ describe "related queries" $ do
context "related orders" $ do
it "works on a many-to-one relationship" $ do
get "/projects?select=id,clients(name)&order=clients(name).nullsfirst" `shouldRespondWith`
+2 -3
View File
@@ -2,7 +2,6 @@ module Feature.Query.RpcSpec where
import qualified Data.ByteString.Lazy as BL (empty)
import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
import Network.HTTP.Types
@@ -16,8 +15,8 @@ import PostgREST.Config.PgVersion (PgVersion, pgVersion180)
import Protolude hiding (get)
import SpecHelper
spec :: PgVersion -> SpecWith ((), Application)
spec actualPgVersion =
spec :: PgVersion -> SpecWithConfig
spec actualPgVersion withConfig = withConfig baseCfg $
describe "remote procedure call" $ do
context "a proc that returns a set" $ do
context "returns paginated results" $ do
+4 -4
View File
@@ -1,17 +1,17 @@
module Feature.Query.ServerTimingSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig (baseCfg { configDbPlanEnabled = True }) $
describe "Show Duration on Server-Timing header" $ do
context "responds with Server-Timing header" $ do
+2 -3
View File
@@ -1,6 +1,5 @@
module Feature.Query.SingularSpec where
import Network.Wai (Application)
import Network.Wai.Test (SResponse (..))
import Network.HTTP.Types
@@ -11,8 +10,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "Requesting singular json object" $ do
let singular = ("Accept", "application/vnd.pgrst.object+json")
+2 -4
View File
@@ -1,7 +1,5 @@
module Feature.Query.SpreadQueriesSpec where
import Network.Wai (Application)
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
@@ -9,8 +7,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "spread embeds" $ do
it "works on a many-to-one relationship" $ do
get "/projects?select=id,...clients(client_name:name)" `shouldRespondWith`
+5 -4
View File
@@ -1,17 +1,18 @@
module Feature.Query.UnicodeSpec where
import Network.Wai (Application)
import Data.List.NonEmpty (fromList)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import PostgREST.Config (AppConfig (..))
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig (baseCfg { configDbSchemas = fromList ["تست"] }) $
describe "Reading and writing to unicode schema and table names" $
it "Can read and write values" $ do
get "/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
+3 -4
View File
@@ -1,7 +1,6 @@
module Feature.Query.UpdateSpec where
import Network.Wai (Application)
import Test.Hspec hiding (pendingWith)
import Test.Hspec hiding (pendingWith)
import Network.HTTP.Types
import Test.Hspec.Wai
@@ -10,8 +9,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec = do
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $ do
describe "Patching record" $ do
context "to unknown uri" $
it "indicates no table found by returning 404" $
+2 -4
View File
@@ -3,8 +3,6 @@
-- - Upsert/IgnoreDuplicates.hs
module Feature.Query.UpsertSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
@@ -13,8 +11,8 @@ import Test.Hspec.Wai.JSON
import Protolude hiding (get, put)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
spec :: SpecWithConfig
spec withConfig = withConfig baseCfg $
describe "UPSERT" $ do
context "with POST" $ do
context "when Prefer: resolution=merge-duplicates is specified" $ do