tests: Add tests for the pg-safeupdate library
This commit is contained in:
+6
-6
@@ -46,12 +46,12 @@ let
|
||||
|
||||
postgresqlVersions =
|
||||
[
|
||||
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis ]); }
|
||||
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis ]); }
|
||||
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis ]); }
|
||||
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11.withPackages (p: [ p.postgis ]); }
|
||||
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10.withPackages (p: [ p.postgis ]); }
|
||||
{ name = "postgresql-9.6"; postgresql = pkgs.postgresql_9_6.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 p.pg_safeupdate ]); }
|
||||
{ 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 p.pg_safeupdate ]); }
|
||||
{ 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 p.pg_safeupdate ]); }
|
||||
];
|
||||
|
||||
patches =
|
||||
|
||||
@@ -201,6 +201,7 @@ test-suite spec
|
||||
Feature.Query.JsonOperatorSpec
|
||||
Feature.Query.MultipleSchemaSpec
|
||||
Feature.Query.ErrorSpec
|
||||
Feature.Query.PgSafeUpdateSpec
|
||||
Feature.Query.PostGISSpec
|
||||
Feature.Query.QueryLimitedSpec
|
||||
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.JsonOperatorSpec
|
||||
import qualified Feature.Query.MultipleSchemaSpec
|
||||
import qualified Feature.Query.PgSafeUpdateSpec
|
||||
import qualified Feature.Query.PlanSpec
|
||||
import qualified Feature.Query.PostGISSpec
|
||||
import qualified Feature.Query.QueryLimitedSpec
|
||||
@@ -112,6 +113,7 @@ main = do
|
||||
forceRollbackApp = app testCfgForceRollback
|
||||
testCfgLegacyGucsApp = app testCfgLegacyGucs
|
||||
planEnabledApp = app testPlanEnabledCfg
|
||||
pgSafeUpdateApp = app testPgSafeUpdateEnabledCfg
|
||||
|
||||
extraSearchPathApp = appDbs testCfgExtraSearchPath
|
||||
unicodeApp = appDbs testUnicodeCfg
|
||||
@@ -126,24 +128,25 @@ main = do
|
||||
analyzeTable "child_entities"
|
||||
|
||||
specs = uncurry describe <$> [
|
||||
("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion)
|
||||
, ("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec actualPgVersion)
|
||||
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
|
||||
, ("Feature.CorsSpec" , Feature.CorsSpec.spec)
|
||||
, ("Feature.Query.DeleteSpec" , Feature.Query.DeleteSpec.spec)
|
||||
, ("Feature.Query.EmbedDisambiguationSpec" , Feature.Query.EmbedDisambiguationSpec.spec)
|
||||
, ("Feature.Query.EmbedInnerJoinSpec" , Feature.Query.EmbedInnerJoinSpec.spec)
|
||||
, ("Feature.Query.InsertSpec" , Feature.Query.InsertSpec.spec actualPgVersion)
|
||||
, ("Feature.Query.JsonOperatorSpec" , Feature.Query.JsonOperatorSpec.spec actualPgVersion)
|
||||
, ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec actualPgVersion)
|
||||
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec actualPgVersion)
|
||||
, ("Feature.Query.PlanSpec.disabledSpec" , Feature.Query.PlanSpec.disabledSpec)
|
||||
, ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion)
|
||||
, ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec)
|
||||
, ("Feature.Query.RpcSpec" , Feature.Query.RpcSpec.spec actualPgVersion)
|
||||
, ("Feature.Query.SingularSpec" , Feature.Query.SingularSpec.spec)
|
||||
, ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec)
|
||||
, ("Feature.Query.UpsertSpec" , Feature.Query.UpsertSpec.spec actualPgVersion)
|
||||
("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion)
|
||||
, ("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec actualPgVersion)
|
||||
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
|
||||
, ("Feature.CorsSpec" , Feature.CorsSpec.spec)
|
||||
, ("Feature.Query.DeleteSpec" , Feature.Query.DeleteSpec.spec)
|
||||
, ("Feature.Query.EmbedDisambiguationSpec" , Feature.Query.EmbedDisambiguationSpec.spec)
|
||||
, ("Feature.Query.EmbedInnerJoinSpec" , Feature.Query.EmbedInnerJoinSpec.spec)
|
||||
, ("Feature.Query.InsertSpec" , Feature.Query.InsertSpec.spec actualPgVersion)
|
||||
, ("Feature.Query.JsonOperatorSpec" , Feature.Query.JsonOperatorSpec.spec actualPgVersion)
|
||||
, ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec actualPgVersion)
|
||||
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec actualPgVersion)
|
||||
, ("Feature.Query.PgSafeUpdateSpec.disabledSpec" , Feature.Query.PgSafeUpdateSpec.disabledSpec)
|
||||
, ("Feature.Query.PlanSpec.disabledSpec" , Feature.Query.PlanSpec.disabledSpec)
|
||||
, ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion)
|
||||
, ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec)
|
||||
, ("Feature.Query.RpcSpec" , Feature.Query.RpcSpec.spec actualPgVersion)
|
||||
, ("Feature.Query.SingularSpec" , Feature.Query.SingularSpec.spec)
|
||||
, ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec)
|
||||
, ("Feature.Query.UpsertSpec" , Feature.Query.UpsertSpec.spec actualPgVersion)
|
||||
]
|
||||
|
||||
hspec $ do
|
||||
@@ -234,6 +237,10 @@ main = do
|
||||
parallel $ before planEnabledApp $
|
||||
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
|
||||
-- this results in race conditions
|
||||
|
||||
|
||||
@@ -196,6 +196,9 @@ testMultipleSchemaCfg = baseCfg { configDbSchemas = fromList ["v1", "v2", "SPECI
|
||||
testCfgLegacyGucs :: AppConfig
|
||||
testCfgLegacyGucs = baseCfg { configDbUseLegacyGucs = False }
|
||||
|
||||
testPgSafeUpdateEnabledCfg :: AppConfig
|
||||
testPgSafeUpdateEnabledCfg = baseCfg { configDbPreRequest = Just $ QualifiedIdentifier "test" "load_safeupdate" }
|
||||
|
||||
analyzeTable :: Text -> IO ()
|
||||
analyzeTable 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;
|
||||
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
|
||||
, "SPECIAL ""@/\#~_-".names
|
||||
, do$llar$s
|
||||
, safe_update
|
||||
, safe_delete
|
||||
, unsafe_update
|
||||
, unsafe_delete
|
||||
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 (
|
||||
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