From 13327eff7a3dbeaa8ead5703af0ccfa081d9e1b1 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Wed, 5 Aug 2026 14:34:11 -0500 Subject: [PATCH] Schema-invariant pgTAP assertion descriptions in test/core/functions.sql pgTAP's auto-generated test descriptions schema-qualify via ncs() (e.g. "Function public.null_count(...) should return int"), which pins the committed expected-output file to whichever schema count_nulls happens to land in. Pass an explicit, schema-free description to every call instead - function_returns/isnt_strict/volatility_is/lives_ok/bag_eq/throws_ok all still resolve and execute against the real, %I-qualified, ncs()-derived name; only the visible description text drops the schema. This is a self-contained cleanup with no dependency on TEST_SCHEMA existing - it's what lets a later schema-switching mechanism reuse a single expected-output file across every schema the suite runs against, instead of needing one per schema value. Regenerated test/expected/extension_tests.out via make results. Co-Authored-By: Claude Sonnet 5 --- test/core/functions.sql | 50 +++++++++++++++--- test/expected/extension_tests.out | 86 +++++++++++++++---------------- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/test/core/functions.sql b/test/core/functions.sql index 10c90ce..33bc30b 100644 --- a/test/core/functions.sql +++ b/test/core/functions.sql @@ -73,19 +73,32 @@ BEGIN ); END IF; + /* + * Explicit descriptions below (not pgTAP's auto-generated default, + * which schema-qualifies via ncs()): this suite may run against + * count_nulls installed in ANY schema (see TEST_SCHEMA in the + * Makefile), and the description text is exact-matched by pg_regress + * against a single committed expected-output file - it must stay + * IDENTICAL no matter which schema the extension actually landed in. + * ncs() itself is still used to locate and call the real function; + * only the visible description text drops it. + */ RETURN NEXT function_returns( ncs(), f_name, f_args , 'int'::regtype::text -- Sanitize type name + , format('Function %s(%s) should return int', f_name, array_to_string(f_args, ',')) ); -- TODO: isnt_definer RETURN NEXT isnt_strict( ncs(), f_name, f_args + , format('Function %s(%s) should not be strict', f_name, array_to_string(f_args, ',')) ); RETURN NEXT volatility_is( ncs(), f_name, f_args , 'immutable' + , format('Function %s(%s) should be IMMUTABLE', f_name, array_to_string(f_args, ',')) ); END LOOP; END LOOP; @@ -95,16 +108,19 @@ BEGIN RETURN NEXT function_returns( ncs(), f_name, f_args , 'trigger' + , format('Function %s() should return trigger', f_name) ); -- TODO: isnt_definer RETURN NEXT isnt_strict( ncs(), f_name, f_args + , format('Function %s() should not be strict', f_name) ); RETURN NEXT volatility_is( ncs(), f_name, f_args , 'immutable' + , format('Function %s() should be IMMUTABLE', f_name) ); END LOOP; END @@ -120,6 +136,13 @@ CREATE FUNCTION pg_temp.test_trigger_raw( , exec text , errmsg text , errdesc text + /* + * Schema-free stand-in for exec, used ONLY in the visible description + * below - exec itself (schema-qualified via ncs(), by callers) is what + * actually runs. Keeps this suite's output identical no matter which + * schema count_nulls is installed in (see TEST_SCHEMA). + */ + , exec_desc text ) RETURNS SETOF text LANGUAGE plpgsql AS $body$ DECLARE @@ -130,8 +153,15 @@ DECLARE , exec ) ; + c_command_desc CONSTANT text := + format( $$CREATE TRIGGER %s %s INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE %s$$ + , trigger_name + , ba + , exec_desc + ) + ; BEGIN - RETURN NEXT lives_ok( c_command, c_command ); + RETURN NEXT lives_ok( c_command, c_command_desc ); RETURN NEXT throws_ok( $$INSERT INTO test_data VALUES (1,1,NULL)$$ , 'P0001' @@ -166,6 +196,11 @@ BEGIN ) , errmsg := coalesce( err, format( 'test_data must contain 1 %s fields', upper( replace( nn, '_', ' ' ) ) ) ) , errdesc := 'Test ' || c_trigger_name + , exec_desc := format( + $$%s_count_trigger(1, %L)$$ + , nn + , err + ) ) ; END @@ -193,7 +228,7 @@ BEGIN RETURN NEXT bag_eq( format($$SELECT a, b, c, %1$I.null_count( a, b, c ), %1$I.not_null_count( a, b, c ) FROM test_data$$, ncs()) , $$SELECT *, 3-null_count AS not_null_count FROM test_data$$ - , format('Test %I.null_count(a, b, c)', ncs()) + , 'Test null_count(a, b, c)' ); -- Test JSON versions @@ -201,12 +236,12 @@ BEGIN RETURN NEXT bag_eq( format($$SELECT a, b, c, %1$I.null_count( row_to_json( row(a, b, c) ) ), %1$I.not_null_count( row_to_json( row(a, b, c) ) ) FROM test_data$$, ncs()) , $$SELECT *, 3-null_count AS not_null_count FROM test_data$$ - , format('Test %I.null_count(json)', ncs()) + , 'Test null_count(json)' ); RETURN NEXT bag_eq( format($$SELECT a, b, c, %1$I.null_count( row_to_json( row(a, b, c) )::jsonb ), %1$I.not_null_count( row_to_json( row(a, b, c) )::jsonb ) FROM test_data$$, ncs()) , $$SELECT *, 3-null_count AS not_null_count FROM test_data$$ - , format('Test %I.null_count(jsonb)', ncs()) + , 'Test null_count(jsonb)' ); -- Doesn't work for array types @@ -228,10 +263,13 @@ BEGIN WHEN args = '' AND not_ = '' THEN $$test trigger usage: number of NULL columns[, error message]$$ ELSE 'test trigger: first argument must not be null' END - , 'Test ' || trig + , 'Test ' || trig_desc + , trig_desc ) FROM ( - SELECT *, format( '%I.%snull_count_trigger( %s )', ncs(), not_, args ) AS trig + SELECT * + , format( '%I.%snull_count_trigger( %s )', ncs(), not_, args ) AS trig + , format( '%snull_count_trigger( %s )', not_, args ) AS trig_desc FROM unnest( array['not_', ''] ) not_ , unnest( array['NULL', ''] ) args diff --git a/test/expected/extension_tests.out b/test/expected/extension_tests.out index 7bf8f0e..a0cab9d 100644 --- a/test/expected/extension_tests.out +++ b/test/expected/extension_tests.out @@ -5,75 +5,75 @@ ok 1 - _null_count_test.test__check_ncs # Subtest: _null_count_test.test__definition() ok 1 - ensure null_count({anyarray}) is not in search_path - ok 2 - Function public.null_count(anyarray) should return integer - ok 3 - Function public.null_count(anyarray) should not be strict - ok 4 - Function public.null_count(anyarray) should be IMMUTABLE + ok 2 - Function null_count(anyarray) should return int + ok 3 - Function null_count(anyarray) should not be strict + ok 4 - Function null_count(anyarray) should be IMMUTABLE ok 5 - ensure null_count({json}) is not in search_path - ok 6 - Function public.null_count(json) should return integer - ok 7 - Function public.null_count(json) should not be strict - ok 8 - Function public.null_count(json) should be IMMUTABLE + ok 6 - Function null_count(json) should return int + ok 7 - Function null_count(json) should not be strict + ok 8 - Function null_count(json) should be IMMUTABLE ok 9 - ensure null_count({jsonb}) is not in search_path - ok 10 - Function public.null_count(jsonb) should return integer - ok 11 - Function public.null_count(jsonb) should not be strict - ok 12 - Function public.null_count(jsonb) should be IMMUTABLE + ok 10 - Function null_count(jsonb) should return int + ok 11 - Function null_count(jsonb) should not be strict + ok 12 - Function null_count(jsonb) should be IMMUTABLE ok 13 - ensure not_null_count({anyarray}) is not in search_path - ok 14 - Function public.not_null_count(anyarray) should return integer - ok 15 - Function public.not_null_count(anyarray) should not be strict - ok 16 - Function public.not_null_count(anyarray) should be IMMUTABLE + ok 14 - Function not_null_count(anyarray) should return int + ok 15 - Function not_null_count(anyarray) should not be strict + ok 16 - Function not_null_count(anyarray) should be IMMUTABLE ok 17 - ensure not_null_count({json}) is not in search_path - ok 18 - Function public.not_null_count(json) should return integer - ok 19 - Function public.not_null_count(json) should not be strict - ok 20 - Function public.not_null_count(json) should be IMMUTABLE + ok 18 - Function not_null_count(json) should return int + ok 19 - Function not_null_count(json) should not be strict + ok 20 - Function not_null_count(json) should be IMMUTABLE ok 21 - ensure not_null_count({jsonb}) is not in search_path - ok 22 - Function public.not_null_count(jsonb) should return integer - ok 23 - Function public.not_null_count(jsonb) should not be strict - ok 24 - Function public.not_null_count(jsonb) should be IMMUTABLE - ok 25 - Function public.null_count_trigger() should return trigger - ok 26 - Function public.null_count_trigger() should not be strict - ok 27 - Function public.null_count_trigger() should be IMMUTABLE - ok 28 - Function public.not_null_count_trigger() should return trigger - ok 29 - Function public.not_null_count_trigger() should not be strict - ok 30 - Function public.not_null_count_trigger() should be IMMUTABLE + ok 22 - Function not_null_count(jsonb) should return int + ok 23 - Function not_null_count(jsonb) should not be strict + ok 24 - Function not_null_count(jsonb) should be IMMUTABLE + ok 25 - Function null_count_trigger() should return trigger + ok 26 - Function null_count_trigger() should not be strict + ok 27 - Function null_count_trigger() should be IMMUTABLE + ok 28 - Function not_null_count_trigger() should return trigger + ok 29 - Function not_null_count_trigger() should not be strict + ok 30 - Function not_null_count_trigger() should be IMMUTABLE 1..30 ok 2 - _null_count_test.test__definition # Subtest: _null_count_test.test__functionality() - ok 1 - Test public.null_count(a, b, c) - ok 2 - Test public.null_count(json) - ok 3 - Test public.null_count(jsonb) - ok 4 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.not_null_count_trigger( NULL ) - ok 5 - Test public.not_null_count_trigger( NULL ) + ok 1 - Test null_count(a, b, c) + ok 2 - Test null_count(json) + ok 3 - Test null_count(jsonb) + ok 4 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger( NULL ) + ok 5 - Test not_null_count_trigger( NULL ) ok 6 - DROP TRIGGER "test trigger" - ok 7 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.not_null_count_trigger( ) - ok 8 - Test public.not_null_count_trigger( ) + ok 7 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger( ) + ok 8 - Test not_null_count_trigger( ) ok 9 - DROP TRIGGER "test trigger" - ok 10 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.null_count_trigger( NULL ) - ok 11 - Test public.null_count_trigger( NULL ) + ok 10 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE null_count_trigger( NULL ) + ok 11 - Test null_count_trigger( NULL ) ok 12 - DROP TRIGGER "test trigger" - ok 13 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.null_count_trigger( ) - ok 14 - Test public.null_count_trigger( ) + ok 13 - CREATE TRIGGER "test trigger" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE null_count_trigger( ) + ok 14 - Test null_count_trigger( ) ok 15 - DROP TRIGGER "test trigger" - ok 16 - CREATE TRIGGER "null_BEFORE_error_message" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.null_count_trigger(1, 'error_message') + ok 16 - CREATE TRIGGER "null_BEFORE_error_message" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE null_count_trigger(1, 'error_message') ok 17 - Test "null_BEFORE_error_message" ok 18 - DROP TRIGGER "null_BEFORE_error_message" - ok 19 - CREATE TRIGGER "null_AFTER_error_message" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.null_count_trigger(1, 'error_message') + ok 19 - CREATE TRIGGER "null_AFTER_error_message" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE null_count_trigger(1, 'error_message') ok 20 - Test "null_AFTER_error_message" ok 21 - DROP TRIGGER "null_AFTER_error_message" - ok 22 - CREATE TRIGGER "not_null_BEFORE_error_message" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.not_null_count_trigger(1, 'error_message') + ok 22 - CREATE TRIGGER "not_null_BEFORE_error_message" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger(1, 'error_message') ok 23 - Test "not_null_BEFORE_error_message" ok 24 - DROP TRIGGER "not_null_BEFORE_error_message" - ok 25 - CREATE TRIGGER "not_null_AFTER_error_message" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.not_null_count_trigger(1, 'error_message') + ok 25 - CREATE TRIGGER "not_null_AFTER_error_message" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger(1, 'error_message') ok 26 - Test "not_null_AFTER_error_message" ok 27 - DROP TRIGGER "not_null_AFTER_error_message" - ok 28 - CREATE TRIGGER "null_BEFORE_" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.null_count_trigger(1, NULL) + ok 28 - CREATE TRIGGER "null_BEFORE_" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE null_count_trigger(1, NULL) ok 29 - Test "null_BEFORE_" ok 30 - DROP TRIGGER "null_BEFORE_" - ok 31 - CREATE TRIGGER "null_AFTER_" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.null_count_trigger(1, NULL) + ok 31 - CREATE TRIGGER "null_AFTER_" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE null_count_trigger(1, NULL) ok 32 - Test "null_AFTER_" ok 33 - DROP TRIGGER "null_AFTER_" - ok 34 - CREATE TRIGGER "not_null_BEFORE_" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.not_null_count_trigger(1, NULL) + ok 34 - CREATE TRIGGER "not_null_BEFORE_" BEFORE INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger(1, NULL) ok 35 - Test "not_null_BEFORE_" ok 36 - DROP TRIGGER "not_null_BEFORE_" - ok 37 - CREATE TRIGGER "not_null_AFTER_" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE public.not_null_count_trigger(1, NULL) + ok 37 - CREATE TRIGGER "not_null_AFTER_" AFTER INSERT ON test_data FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger(1, NULL) ok 38 - Test "not_null_AFTER_" ok 39 - DROP TRIGGER "not_null_AFTER_" 1..39