tests: Add tests for the pg-safeupdate library
This commit is contained in:
+6
-6
@@ -46,12 +46,12 @@ let
|
|||||||
|
|
||||||
postgresqlVersions =
|
postgresqlVersions =
|
||||||
[
|
[
|
||||||
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis ]); }
|
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||||
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis ]); }
|
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||||
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis ]); }
|
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||||
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11.withPackages (p: [ p.postgis ]); }
|
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||||
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10.withPackages (p: [ p.postgis ]); }
|
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||||
{ name = "postgresql-9.6"; postgresql = pkgs.postgresql_9_6.withPackages (p: [ p.postgis ]); }
|
{ name = "postgresql-9.6"; postgresql = pkgs.postgresql_9_6.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||||
];
|
];
|
||||||
|
|
||||||
patches =
|
patches =
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ test-suite spec
|
|||||||
Feature.Query.JsonOperatorSpec
|
Feature.Query.JsonOperatorSpec
|
||||||
Feature.Query.MultipleSchemaSpec
|
Feature.Query.MultipleSchemaSpec
|
||||||
Feature.Query.ErrorSpec
|
Feature.Query.ErrorSpec
|
||||||
|
Feature.Query.PgSafeUpdateSpec
|
||||||
Feature.Query.PostGISSpec
|
Feature.Query.PostGISSpec
|
||||||
Feature.Query.QueryLimitedSpec
|
Feature.Query.QueryLimitedSpec
|
||||||
Feature.Query.QuerySpec
|
Feature.Query.QuerySpec
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
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 SpecHelper
|
||||||
|
|
||||||
|
spec :: SpecWith ((), Application)
|
||||||
|
spec =
|
||||||
|
describe "Enabling pg-safeupdate" $ do
|
||||||
|
context "Full table update" $
|
||||||
|
it "does not update and throws no error if no condition is present" $
|
||||||
|
request methodPatch "/safe_update"
|
||||||
|
[("Prefer", "count=exact")]
|
||||||
|
[json| {"name": "New name"} |]
|
||||||
|
`shouldRespondWith`
|
||||||
|
""
|
||||||
|
{ matchStatus = 404
|
||||||
|
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||||
|
, "Content-Range" <:> "*/0" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
context "Full table delete" $ do
|
||||||
|
it "does not delete and throws error if no condition is present" $
|
||||||
|
request methodDelete "/safe_delete" [] mempty
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json|{
|
||||||
|
"code": "21000",
|
||||||
|
"details": null,
|
||||||
|
"hint": null,
|
||||||
|
"message": "DELETE requires a WHERE clause"
|
||||||
|
}|]
|
||||||
|
{ matchStatus = 400 }
|
||||||
|
|
||||||
|
it "allows full table delete if a filter is present" $
|
||||||
|
request methodDelete "/safe_delete?id=gt.0"
|
||||||
|
[("Prefer", "count=exact")]
|
||||||
|
mempty
|
||||||
|
`shouldRespondWith`
|
||||||
|
""
|
||||||
|
{ matchStatus = 204
|
||||||
|
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||||
|
, "Content-Range" <:> "*/3" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
disabledSpec :: SpecWith ((), Application)
|
||||||
|
disabledSpec =
|
||||||
|
describe "Disabling pg-safeupdate" $ do
|
||||||
|
context "Full table update" $
|
||||||
|
it "does not update and does not throw error if no condition is present" $
|
||||||
|
request methodPatch "/unsafe_update"
|
||||||
|
[("Prefer", "count=exact")]
|
||||||
|
[json| {"name": "New name"} |]
|
||||||
|
`shouldRespondWith`
|
||||||
|
""
|
||||||
|
{ matchStatus = 404
|
||||||
|
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||||
|
, "Content-Range" <:> "*/0" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
context "Full table delete" $
|
||||||
|
it "deletes and does not throw error if no condition is present" $ do
|
||||||
|
request methodDelete "/unsafe_delete"
|
||||||
|
[("Prefer", "count=exact")]
|
||||||
|
mempty
|
||||||
|
`shouldRespondWith`
|
||||||
|
""
|
||||||
|
{ matchStatus = 204
|
||||||
|
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||||
|
, "Content-Range" <:> "*/3" ]
|
||||||
|
}
|
||||||
+25
-18
@@ -45,6 +45,7 @@ import qualified Feature.Query.HtmlRawOutputSpec
|
|||||||
import qualified Feature.Query.InsertSpec
|
import qualified Feature.Query.InsertSpec
|
||||||
import qualified Feature.Query.JsonOperatorSpec
|
import qualified Feature.Query.JsonOperatorSpec
|
||||||
import qualified Feature.Query.MultipleSchemaSpec
|
import qualified Feature.Query.MultipleSchemaSpec
|
||||||
|
import qualified Feature.Query.PgSafeUpdateSpec
|
||||||
import qualified Feature.Query.PlanSpec
|
import qualified Feature.Query.PlanSpec
|
||||||
import qualified Feature.Query.PostGISSpec
|
import qualified Feature.Query.PostGISSpec
|
||||||
import qualified Feature.Query.QueryLimitedSpec
|
import qualified Feature.Query.QueryLimitedSpec
|
||||||
@@ -112,6 +113,7 @@ main = do
|
|||||||
forceRollbackApp = app testCfgForceRollback
|
forceRollbackApp = app testCfgForceRollback
|
||||||
testCfgLegacyGucsApp = app testCfgLegacyGucs
|
testCfgLegacyGucsApp = app testCfgLegacyGucs
|
||||||
planEnabledApp = app testPlanEnabledCfg
|
planEnabledApp = app testPlanEnabledCfg
|
||||||
|
pgSafeUpdateApp = app testPgSafeUpdateEnabledCfg
|
||||||
|
|
||||||
extraSearchPathApp = appDbs testCfgExtraSearchPath
|
extraSearchPathApp = appDbs testCfgExtraSearchPath
|
||||||
unicodeApp = appDbs testUnicodeCfg
|
unicodeApp = appDbs testUnicodeCfg
|
||||||
@@ -126,24 +128,25 @@ main = do
|
|||||||
analyzeTable "child_entities"
|
analyzeTable "child_entities"
|
||||||
|
|
||||||
specs = uncurry describe <$> [
|
specs = uncurry describe <$> [
|
||||||
("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion)
|
("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion)
|
||||||
, ("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec actualPgVersion)
|
, ("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec actualPgVersion)
|
||||||
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
|
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
|
||||||
, ("Feature.CorsSpec" , Feature.CorsSpec.spec)
|
, ("Feature.CorsSpec" , Feature.CorsSpec.spec)
|
||||||
, ("Feature.Query.DeleteSpec" , Feature.Query.DeleteSpec.spec)
|
, ("Feature.Query.DeleteSpec" , Feature.Query.DeleteSpec.spec)
|
||||||
, ("Feature.Query.EmbedDisambiguationSpec" , Feature.Query.EmbedDisambiguationSpec.spec)
|
, ("Feature.Query.EmbedDisambiguationSpec" , Feature.Query.EmbedDisambiguationSpec.spec)
|
||||||
, ("Feature.Query.EmbedInnerJoinSpec" , Feature.Query.EmbedInnerJoinSpec.spec)
|
, ("Feature.Query.EmbedInnerJoinSpec" , Feature.Query.EmbedInnerJoinSpec.spec)
|
||||||
, ("Feature.Query.InsertSpec" , Feature.Query.InsertSpec.spec actualPgVersion)
|
, ("Feature.Query.InsertSpec" , Feature.Query.InsertSpec.spec actualPgVersion)
|
||||||
, ("Feature.Query.JsonOperatorSpec" , Feature.Query.JsonOperatorSpec.spec actualPgVersion)
|
, ("Feature.Query.JsonOperatorSpec" , Feature.Query.JsonOperatorSpec.spec actualPgVersion)
|
||||||
, ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec actualPgVersion)
|
, ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec actualPgVersion)
|
||||||
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec actualPgVersion)
|
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec actualPgVersion)
|
||||||
, ("Feature.Query.PlanSpec.disabledSpec" , Feature.Query.PlanSpec.disabledSpec)
|
, ("Feature.Query.PgSafeUpdateSpec.disabledSpec" , Feature.Query.PgSafeUpdateSpec.disabledSpec)
|
||||||
, ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion)
|
, ("Feature.Query.PlanSpec.disabledSpec" , Feature.Query.PlanSpec.disabledSpec)
|
||||||
, ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec)
|
, ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion)
|
||||||
, ("Feature.Query.RpcSpec" , Feature.Query.RpcSpec.spec actualPgVersion)
|
, ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec)
|
||||||
, ("Feature.Query.SingularSpec" , Feature.Query.SingularSpec.spec)
|
, ("Feature.Query.RpcSpec" , Feature.Query.RpcSpec.spec actualPgVersion)
|
||||||
, ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec)
|
, ("Feature.Query.SingularSpec" , Feature.Query.SingularSpec.spec)
|
||||||
, ("Feature.Query.UpsertSpec" , Feature.Query.UpsertSpec.spec actualPgVersion)
|
, ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec)
|
||||||
|
, ("Feature.Query.UpsertSpec" , Feature.Query.UpsertSpec.spec actualPgVersion)
|
||||||
]
|
]
|
||||||
|
|
||||||
hspec $ do
|
hspec $ do
|
||||||
@@ -234,6 +237,10 @@ main = do
|
|||||||
parallel $ before planEnabledApp $
|
parallel $ before planEnabledApp $
|
||||||
describe "Feature.Query.PlanSpec.spec" $ Feature.Query.PlanSpec.spec actualPgVersion
|
describe "Feature.Query.PlanSpec.spec" $ Feature.Query.PlanSpec.spec actualPgVersion
|
||||||
|
|
||||||
|
-- this test runs with a pre request to enable the pg-safeupdate library per-session
|
||||||
|
parallel $ before pgSafeUpdateApp $
|
||||||
|
describe "Feature.Query.PgSafeUpdateSpec.spec" Feature.Query.PgSafeUpdateSpec.spec
|
||||||
|
|
||||||
-- Note: the rollback tests can not run in parallel, because they test persistance and
|
-- Note: the rollback tests can not run in parallel, because they test persistance and
|
||||||
-- this results in race conditions
|
-- this results in race conditions
|
||||||
|
|
||||||
|
|||||||
@@ -196,6 +196,9 @@ testMultipleSchemaCfg = baseCfg { configDbSchemas = fromList ["v1", "v2", "SPECI
|
|||||||
testCfgLegacyGucs :: AppConfig
|
testCfgLegacyGucs :: AppConfig
|
||||||
testCfgLegacyGucs = baseCfg { configDbUseLegacyGucs = False }
|
testCfgLegacyGucs = baseCfg { configDbUseLegacyGucs = False }
|
||||||
|
|
||||||
|
testPgSafeUpdateEnabledCfg :: AppConfig
|
||||||
|
testPgSafeUpdateEnabledCfg = baseCfg { configDbPreRequest = Just $ QualifiedIdentifier "test" "load_safeupdate" }
|
||||||
|
|
||||||
analyzeTable :: Text -> IO ()
|
analyzeTable :: Text -> IO ()
|
||||||
analyzeTable tableName =
|
analyzeTable tableName =
|
||||||
void $ readProcess "psql" ["--set", "ON_ERROR_STOP=1", "-a", "-c", toS $ "ANALYZE test.\"" <> tableName <> "\""] []
|
void $ readProcess "psql" ["--set", "ON_ERROR_STOP=1", "-a", "-c", toS $ "ANALYZE test.\"" <> tableName <> "\""] []
|
||||||
|
|||||||
Vendored
+9
@@ -803,3 +803,12 @@ INSERT INTO "SPECIAL ""@/\#~_-".names (id, name) VALUES (1, 'John'), (2, 'Mary')
|
|||||||
|
|
||||||
TRUNCATE TABLE do$llar$s CASCADE;
|
TRUNCATE TABLE do$llar$s CASCADE;
|
||||||
INSERT INTO do$llar$s (a$num$) VALUES (100), (200), (300);
|
INSERT INTO do$llar$s (a$num$) VALUES (100), (200), (300);
|
||||||
|
|
||||||
|
TRUNCATE TABLE safe_update CASCADE;
|
||||||
|
INSERT INTO safe_update(id, name) VALUES (1, 'First'), (2, 'Second'), (3, 'Third');
|
||||||
|
TRUNCATE TABLE safe_delete CASCADE;
|
||||||
|
INSERT INTO safe_delete(id, name) VALUES (1, 'First'), (2, 'Second'), (3, 'Third');
|
||||||
|
TRUNCATE TABLE unsafe_update CASCADE;
|
||||||
|
INSERT INTO unsafe_update(id, name) VALUES (1, 'First'), (2, 'Second'), (3, 'Third');
|
||||||
|
TRUNCATE TABLE unsafe_delete CASCADE;
|
||||||
|
INSERT INTO unsafe_delete(id, name) VALUES (1, 'First'), (2, 'Second'), (3, 'Third');
|
||||||
|
|||||||
Vendored
+4
@@ -195,6 +195,10 @@ GRANT ALL ON TABLE
|
|||||||
, shop_bles
|
, shop_bles
|
||||||
, "SPECIAL ""@/\#~_-".names
|
, "SPECIAL ""@/\#~_-".names
|
||||||
, do$llar$s
|
, do$llar$s
|
||||||
|
, safe_update
|
||||||
|
, safe_delete
|
||||||
|
, unsafe_update
|
||||||
|
, unsafe_delete
|
||||||
TO postgrest_test_anonymous;
|
TO postgrest_test_anonymous;
|
||||||
|
|
||||||
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
||||||
|
|||||||
Vendored
+27
@@ -2689,3 +2689,30 @@ $$ LANGUAGE sql;
|
|||||||
CREATE TABLE do$llar$s (
|
CREATE TABLE do$llar$s (
|
||||||
a$num$ numeric
|
a$num$ numeric
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- Tables and functions to test the pg-safeupdate library
|
||||||
|
|
||||||
|
CREATE TABLE test.safe_update(
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE test.safe_delete(
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE test.unsafe_update(
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE test.unsafe_delete(
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION test.load_safeupdate() RETURNS VOID AS $$
|
||||||
|
BEGIN
|
||||||
|
LOAD 'safeupdate';
|
||||||
|
END; $$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|||||||
Reference in New Issue
Block a user