diff --git a/doc/pgtap.mmd b/doc/pgtap.mmd index e5a6091a..37dbeeb9 100644 --- a/doc/pgtap.mmd +++ b/doc/pgtap.mmd @@ -2254,12 +2254,12 @@ missing triggers, like so: : Name of a schema in which to find functions. `:functions` -: An array of function and/or procedure names. +: An array of function names. `:description` : A short description of the test. -This function tests that all of the functions or procedures in the named schema, +This function tests that all of the functions in the named schema, or that are visible in the search path, are only the functions that *should* be there. If the `:schema` argument is omitted, functions will be sought in the search path, excluding `pg_catalog` and `information_schema` If the description @@ -2279,6 +2279,46 @@ missing functions, like so: # Missing functions: # frobnitz +### `procedures_are()` ### + + SELECT ### `procedures_are()` ### + + SELECT procedures_are( :schema, :procedures, :description ); + SELECT procedures_are( :schema, :procedures ); + SELECT procedures_are( :procedures, :description ); + SELECT procedures_are( :procedures ); + +**Parameters** + +`:schema` +: Name of a schema in which to find procedures. + +`:procedures` +: An array of procedure names. + +`:description` +: A short description of the test. + +This function tests that all of the procedures in the named schema, +or that are visible in the search path, are only the procedures that *should* be +there. If the `:schema` argument is omitted, procedures will be sought in the +search path, excluding `pg_catalog` and `information_schema` If the description +is omitted, a generally useful default description will be generated. Example: + + SELECT procedures_are( + 'myschema', + ARRAY[ 'foo', 'bar', 'frobnitz' ] + ); + +In the event of a failure, you'll see diagnostics listing the extra and/or +missing procedures, like so: + + # Failed test 150: "Schema someschema should have the correct procedures" + # Extra procedures: + # schnauzify + # Missing procedures: + # frobnitz + ### `roles_are()` ### SELECT roles_are( :roles, :description ); diff --git a/sql/pgtap--1.3.4--1.4.0.sql b/sql/pgtap--1.3.4--1.4.0.sql new file mode 100644 index 00000000..ed9a4794 --- /dev/null +++ b/sql/pgtap--1.3.4--1.4.0.sql @@ -0,0 +1,55 @@ +-- procedures_are( schema, procedures[], description ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME, NAME[], TEXT ) +RETURNS TEXT AS $$ + SELECT _are( + 'procedures', + ARRAY( + SELECT name FROM tap_funky WHERE schema = $1 and prokind = 'p' + EXCEPT + SELECT $2[i] + FROM generate_series(1, array_upper($2, 1)) s(i) + ), + ARRAY( + SELECT $2[i] + FROM generate_series(1, array_upper($2, 1)) s(i) + EXCEPT + SELECT name FROM tap_funky WHERE schema = $1 and prokind = 'p' + ), + $3 + ); +$$ LANGUAGE SQL; + +-- procedures_are( schema, procedures[] ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME, NAME[] ) +RETURNS TEXT AS $$ + SELECT procedures_are( $1, $2, 'Schema ' || quote_ident($1) || ' should have the correct procedures' ); +$$ LANGUAGE SQL; + +-- procedures_are( procedures[], description ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME[], TEXT ) +RETURNS TEXT AS $$ + SELECT _are( + 'procedures', + ARRAY( + SELECT name FROM tap_funky WHERE is_visible and prokind = 'p' + AND schema NOT IN ('pg_catalog', 'information_schema') + EXCEPT + SELECT $1[i] + FROM generate_series(1, array_upper($1, 1)) s(i) + ), + ARRAY( + SELECT $1[i] + FROM generate_series(1, array_upper($1, 1)) s(i) + EXCEPT + SELECT name FROM tap_funky WHERE is_visible and prokind = 'p' + AND schema NOT IN ('pg_catalog', 'information_schema') + ), + $2 + ); +$$ LANGUAGE SQL; + +-- procedures_are( procedures[] ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME[] ) +RETURNS TEXT AS $$ + SELECT procedures_are( $1, 'Search path ' || pg_catalog.current_setting('search_path') || ' should have the correct procedures' ); +$$ LANGUAGE SQL; diff --git a/sql/pgtap.sql.in b/sql/pgtap.sql.in index c1c7ed92..e2ff04ac 100644 --- a/sql/pgtap.sql.in +++ b/sql/pgtap.sql.in @@ -4979,7 +4979,7 @@ RETURNS TEXT AS $$ SELECT _are( 'functions', ARRAY( - SELECT name FROM tap_funky WHERE schema = $1 + SELECT name FROM tap_funky WHERE schema = $1 and kind != 'p' EXCEPT SELECT $2[i] FROM generate_series(1, array_upper($2, 1)) s(i) @@ -4988,7 +4988,7 @@ RETURNS TEXT AS $$ SELECT $2[i] FROM generate_series(1, array_upper($2, 1)) s(i) EXCEPT - SELECT name FROM tap_funky WHERE schema = $1 + SELECT name FROM tap_funky WHERE schema = $1 and kind != 'p' ), $3 ); @@ -5006,7 +5006,7 @@ RETURNS TEXT AS $$ SELECT _are( 'functions', ARRAY( - SELECT name FROM tap_funky WHERE is_visible + SELECT name FROM tap_funky WHERE is_visible and kind != 'p' AND schema NOT IN ('pg_catalog', 'information_schema') EXCEPT SELECT $1[i] @@ -5016,7 +5016,7 @@ RETURNS TEXT AS $$ SELECT $1[i] FROM generate_series(1, array_upper($1, 1)) s(i) EXCEPT - SELECT name FROM tap_funky WHERE is_visible + SELECT name FROM tap_funky WHERE is_visible and kind != 'p' AND schema NOT IN ('pg_catalog', 'information_schema') ), $2 @@ -5029,6 +5029,62 @@ RETURNS TEXT AS $$ SELECT functions_are( $1, 'Search path ' || pg_catalog.current_setting('search_path') || ' should have the correct functions' ); $$ LANGUAGE SQL; +-- procedures_are( schema, procedures[], description ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME, NAME[], TEXT ) +RETURNS TEXT AS $$ + SELECT _are( + 'procedures', + ARRAY( + SELECT name FROM tap_funky WHERE schema = $1 and kind = 'p' + EXCEPT + SELECT $2[i] + FROM generate_series(1, array_upper($2, 1)) s(i) + ), + ARRAY( + SELECT $2[i] + FROM generate_series(1, array_upper($2, 1)) s(i) + EXCEPT + SELECT name FROM tap_funky WHERE schema = $1 and kind = 'p' + ), + $3 + ); +$$ LANGUAGE SQL; + +-- procedures_are( schema, procedures[] ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME, NAME[] ) +RETURNS TEXT AS $$ + SELECT procedures_are( $1, $2, 'Schema ' || quote_ident($1) || ' should have the correct procedures' ); +$$ LANGUAGE SQL; + +-- procedures_are( procedures[], description ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME[], TEXT ) +RETURNS TEXT AS $$ + SELECT _are( + 'procedures', + ARRAY( + SELECT name FROM tap_funky WHERE is_visible and kind = 'p' + AND schema NOT IN ('pg_catalog', 'information_schema') + EXCEPT + SELECT $1[i] + FROM generate_series(1, array_upper($1, 1)) s(i) + ), + ARRAY( + SELECT $1[i] + FROM generate_series(1, array_upper($1, 1)) s(i) + EXCEPT + SELECT name FROM tap_funky WHERE is_visible and kind = 'p' + AND schema NOT IN ('pg_catalog', 'information_schema') + ), + $2 + ); +$$ LANGUAGE SQL; + +-- procedures_are( procedures[] ) +CREATE OR REPLACE FUNCTION procedures_are ( NAME[] ) +RETURNS TEXT AS $$ + SELECT procedures_are( $1, 'Search path ' || pg_catalog.current_setting('search_path') || ' should have the correct procedures' ); +$$ LANGUAGE SQL; + -- indexes_are( schema, table, indexes[], description ) CREATE OR REPLACE FUNCTION indexes_are( NAME, NAME, NAME[], TEXT ) RETURNS TEXT AS $$ diff --git a/test/expected/aretap.out b/test/expected/aretap.out index 1e9dfeda..176f95ca 100644 --- a/test/expected/aretap.out +++ b/test/expected/aretap.out @@ -1,5 +1,5 @@ \unset ECHO -1..459 +1..488 ok 1 - tablespaces_are(tablespaces, desc) should pass ok 2 - tablespaces_are(tablespaces, desc) should have the proper description ok 3 - tablespaces_are(tablespaces, desc) should have the proper diagnostics @@ -459,3 +459,32 @@ ok 456 - foreign_tables_are(schema, tables) extra and missing should have the pr ok 457 - foreign_tables_are(tables) extra and missing should fail ok 458 - foreign_tables_are(tables) extra and missing should have the proper description ok 459 - foreign_tables_are(tables) extra and missing should have the proper diagnostics +ok 460 - procedures_are(schema, procedures, desc) should pass +ok 461 - procedures_are(schema, procedures, desc) should have the proper description +ok 462 - procedures_are(schema, procedures, desc) should have the proper diagnostics +ok 463 - procedures_are(schema, procedures) should pass +ok 464 - procedures_are(schema, procedures) should have the proper description +ok 465 - procedures_are(schema, procedures, desc) + missing should fail +ok 466 - procedures_are(schema, procedures, desc) + missing should have the proper description +ok 467 - procedures_are(schema, procedures, desc) + missing should have the proper diagnostics +ok 468 - procedures_are(schema, procedures, desc) + extra should fail +ok 469 - procedures_are(schema, procedures, desc) + extra should have the proper description +ok 470 - procedures_are(schema, procedures, desc) + extra should have the proper diagnostics +ok 471 - procedures_are(schema, procedures, desc) + extra & missing should fail +ok 472 - procedures_are(schema, procedures, desc) + extra & missing should have the proper description +ok 473 - procedures_are(schema, procedures, desc) + extra & missing should have the proper diagnostics +ok 474 - procedures_are(procedures, desc) should pass +ok 475 - procedures_are(procedures, desc) should have the proper description +ok 476 - procedures_are(procedures, desc) should have the proper diagnostics +ok 477 - procedures_are(procedures) should pass +ok 478 - procedures_are(procedures) should have the proper description +ok 479 - procedures_are(procedures) should have the proper diagnostics +ok 480 - procedures_are(procedures, desc) + missing should fail +ok 481 - procedures_are(procedures, desc) + missing should have the proper description +ok 482 - procedures_are(procedures, desc) + missing should have the proper diagnostics +ok 483 - procedures_are(procedures, desc) + extra should fail +ok 484 - procedures_are(procedures, desc) + extra should have the proper description +ok 485 - procedures_are(procedures, desc) + extra should have the proper diagnostics +ok 486 - procedures_are(procedures, desc) + extra & missing should fail +ok 487 - procedures_are(procedures, desc) + extra & missing should have the proper description +ok 488 - procedures_are(procedures, desc) + extra & missing should have the proper diagnostics diff --git a/test/sql/aretap.sql b/test/sql/aretap.sql index c2b41ccc..20a8e4c7 100644 --- a/test/sql/aretap.sql +++ b/test/sql/aretap.sql @@ -2,7 +2,7 @@ \i test/setup.sql -- \i sql/pgtap.sql -SELECT plan(459); +SELECT plan(488); --SELECT * FROM no_plan(); -- This will be rolled back. :-) @@ -83,15 +83,27 @@ CREATE TYPE someschema."myType" AS ( foo INT ); --- Create a procedure. +-- Create some procedures. DO $$ BEGIN IF pg_version_num() >= 110000 THEN EXECUTE 'CREATE PROCEDURE someschema.someproc(int) LANGUAGE SQL AS '''''; + EXECUTE 'CREATE PROCEDURE someschema.someotherproc(int) LANGUAGE SQL AS '''''; + EXECUTE 'CREATE PROCEDURE public.yiprok(int) LANGUAGE SQL AS '''''; + EXECUTE 'CREATE PROCEDURE public.yaprok(int) LANGUAGE SQL AS '''''; ELSE CREATE FUNCTION someschema.someproc(int) RETURNS void AS '' LANGUAGE SQL; + CREATE FUNCTION someschema.someotherproc(int) + RETURNS void AS '' + LANGUAGE SQL; + CREATE FUNCTION public.yiprok(int) + RETURNS void AS '' + LANGUAGE SQL; + CREATE FUNCTION public.yaprok(int) + RETURNS void AS '' + LANGUAGE SQL; END IF; END; $$; @@ -511,7 +523,7 @@ SELECT * FROM check_test( -- Test functions_are(). SELECT * FROM check_test( - functions_are( 'someschema', ARRAY['yip', 'yap', 'someproc'], 'whatever' ), + functions_are( 'someschema', ARRAY['yip', 'yap'], 'whatever' ), true, 'functions_are(schema, functions, desc)', 'whatever', @@ -519,7 +531,7 @@ SELECT * FROM check_test( ); SELECT * FROM check_test( - functions_are( 'someschema', ARRAY['yip', 'yap', 'someproc'] ), + functions_are( 'someschema', ARRAY['yip', 'yap'] ), true, 'functions_are(schema, functions)', 'Schema someschema should have the correct functions' @@ -527,7 +539,7 @@ SELECT * FROM check_test( ); SELECT * FROM check_test( - functions_are( 'someschema', ARRAY['yip', 'yap', 'someproc', 'yop'], 'whatever' ), + functions_are( 'someschema', ARRAY['yip', 'yap', 'yop'], 'whatever' ), false, 'functions_are(schema, functions, desc) + missing', 'whatever', @@ -536,7 +548,7 @@ SELECT * FROM check_test( ); SELECT * FROM check_test( - functions_are( 'someschema', ARRAY['yip', 'someproc'], 'whatever' ), + functions_are( 'someschema', ARRAY['yip'], 'whatever' ), false, 'functions_are(schema, functions, desc) + extra', 'whatever', @@ -545,7 +557,7 @@ SELECT * FROM check_test( ); SELECT * FROM check_test( - functions_are( 'someschema', ARRAY['yap', 'yop', 'someproc'], 'whatever' ), + functions_are( 'someschema', ARRAY['yap', 'yop'], 'whatever' ), false, 'functions_are(schema, functions, desc) + extra & missing', 'whatever', @@ -563,6 +575,7 @@ CREATE FUNCTION ___myfunk(ex text) RETURNS NAME[] AS $$ WHERE pg_catalog.pg_function_is_visible(p.oid) AND n.nspname NOT IN ('pg_catalog', 'information_schema') AND p.proname <> $1 + AND p.prokind <> 'p' ); $$ LANGUAGE SQL; @@ -1948,6 +1961,111 @@ $$ LANGUAGE PLPGSQL; SELECT * FROM test_foreign_tables_are(); +/****************************************************************************/ +-- Test procedures_are(). + +SELECT * FROM check_test( + procedures_are( 'someschema', ARRAY['someotherproc', 'someproc'], 'whatever' ), + true, + 'procedures_are(schema, procedures, desc)', + 'whatever', + '' +); + +SELECT * FROM check_test( + procedures_are( 'someschema', ARRAY['someotherproc', 'someproc'] ), + true, + 'procedures_are(schema, procedures)', + 'Schema someschema should have the correct procedures' + '' +); + +SELECT * FROM check_test( + procedures_are( 'someschema', ARRAY['someotherproc', 'someproc', 'yop'], 'whatever' ), + false, + 'procedures_are(schema, procedures, desc) + missing', + 'whatever', + ' Missing procedures: + yop' +); + +SELECT * FROM check_test( + procedures_are( 'someschema', ARRAY['someproc'], 'whatever' ), + false, + 'procedures_are(schema, procedures, desc) + extra', + 'whatever', + ' Extra procedures: + someotherproc' +); + +SELECT * FROM check_test( + procedures_are( 'someschema', ARRAY['yop', 'someproc'], 'whatever' ), + false, + 'procedures_are(schema, procedures, desc) + extra & missing', + 'whatever', + ' Extra procedures: + someotherproc + Missing procedures: + yop' +); + +CREATE FUNCTION ___myprok(ex text) RETURNS NAME[] AS $$ + SELECT ARRAY( + SELECT p.proname + FROM pg_catalog.pg_namespace n + JOIN pg_catalog.pg_proc p ON n.oid = p.pronamespace + WHERE pg_catalog.pg_function_is_visible(p.oid) + AND n.nspname NOT IN ('pg_catalog', 'information_schema') + AND p.proname <> $1 + AND p.prokind = 'p' + ); +$$ LANGUAGE SQL; + +SELECT * FROM check_test( + procedures_are( ___myprok(''), 'whatever' ), + true, + 'procedures_are(procedures, desc)', + 'whatever', + '' +); + +SELECT * FROM check_test( + procedures_are( ___myprok('') ), + true, + 'procedures_are(procedures)', + 'Search path ' || pg_catalog.current_setting('search_path') || ' should have the correct procedures', + '' +); + +SELECT * FROM check_test( + procedures_are( array_append(___myprok(''), '__booyah__'), 'whatever' ), + false, + 'procedures_are(procedures, desc) + missing', + 'whatever', + ' Missing procedures: + __booyah__' +); + +SELECT * FROM check_test( + procedures_are( ___myprok('yiprok'), 'whatever' ), + false, + 'procedures_are(procedures, desc) + extra', + 'whatever', + ' Extra procedures: + yiprok' +); + +SELECT * FROM check_test( + procedures_are( array_append(___myprok('yiprok'), '__booyah__'), 'whatever' ), + false, + 'procedures_are(procedures, desc) + extra & missing', + 'whatever', + ' Extra procedures: + yiprok + Missing procedures: + __booyah__' +); + /****************************************************************************/ -- Finish the tests and clean up.