From 473f9de901867e529d065c198195553f00e8047e Mon Sep 17 00:00:00 2001 From: Sehrope Sarkuni Date: Sun, 24 May 2026 12:19:35 -0400 Subject: [PATCH 2/6] Add test_scram module for direct unit tests of SCRAM helpers Adds src/test/modules/test_scram/, mirroring the layout of the existing test_saslprep module, with one SQL-callable wrapper around scram_parse_iterations() and a regression test that exercises the parser directly rather than only through a SCRAM authentication exchange. Coverage is organized by why the input is accepted or rejected. Wires the new module into src/test/modules/Makefile and src/test/modules/meson.build so it runs under check-world. --- src/test/modules/Makefile | 1 + src/test/modules/meson.build | 1 + src/test/modules/test_scram/Makefile | 23 ++++ src/test/modules/test_scram/README | 18 +++ .../test_scram/expected/test_scram.out | 122 ++++++++++++++++++ src/test/modules/test_scram/meson.build | 33 +++++ .../modules/test_scram/sql/test_scram.sql | 31 +++++ .../modules/test_scram/test_scram--1.0.sql | 16 +++ src/test/modules/test_scram/test_scram.c | 45 +++++++ .../modules/test_scram/test_scram.control | 5 + 10 files changed, 295 insertions(+) create mode 100644 src/test/modules/test_scram/Makefile create mode 100644 src/test/modules/test_scram/README create mode 100644 src/test/modules/test_scram/expected/test_scram.out create mode 100644 src/test/modules/test_scram/meson.build create mode 100644 src/test/modules/test_scram/sql/test_scram.sql create mode 100644 src/test/modules/test_scram/test_scram--1.0.sql create mode 100644 src/test/modules/test_scram/test_scram.c create mode 100644 src/test/modules/test_scram/test_scram.control diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 0a74ab5c86f..dc010fb111f 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -49,6 +49,7 @@ SUBDIRS = \ test_resowner \ test_rls_hooks \ test_saslprep \ + test_scram \ test_shmem \ test_shm_mq \ test_slru \ diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build index 4bca42bb370..a8019538ef0 100644 --- a/src/test/modules/meson.build +++ b/src/test/modules/meson.build @@ -50,6 +50,7 @@ subdir('test_regex') subdir('test_resowner') subdir('test_rls_hooks') subdir('test_saslprep') +subdir('test_scram') subdir('test_shmem') subdir('test_shm_mq') subdir('test_slru') diff --git a/src/test/modules/test_scram/Makefile b/src/test/modules/test_scram/Makefile new file mode 100644 index 00000000000..b597ca5035a --- /dev/null +++ b/src/test/modules/test_scram/Makefile @@ -0,0 +1,23 @@ +# src/test/modules/test_scram/Makefile + +MODULE_big = test_scram +OBJS = \ + $(WIN32RES) \ + test_scram.o +PGFILEDESC = "test_scram - test SCRAM helpers in src/common" + +EXTENSION = test_scram +DATA = test_scram--1.0.sql + +REGRESS = test_scram + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_scram +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_scram/README b/src/test/modules/test_scram/README new file mode 100644 index 00000000000..549571245e3 --- /dev/null +++ b/src/test/modules/test_scram/README @@ -0,0 +1,18 @@ +src/test/modules/test_scram + +Direct tests for SCRAM helpers +============================== + +This module provides SQL-callable wrappers around helper functions in +src/common/scram-common.c so they can be exercised in isolation by a +regression test, without going through a real SCRAM authentication +exchange. + +Currently it covers scram_parse_iterations(). + +Running the tests +================= + + make check +or + make installcheck diff --git a/src/test/modules/test_scram/expected/test_scram.out b/src/test/modules/test_scram/expected/test_scram.out new file mode 100644 index 00000000000..70a6e5bdadf --- /dev/null +++ b/src/test/modules/test_scram/expected/test_scram.out @@ -0,0 +1,122 @@ +-- +-- Direct tests for scram_parse_iterations() in src/common/scram-common.c. +-- +CREATE EXTENSION test_scram; +-- Accepted: non-empty digit-only strings whose value is in [1, INT_MAX]. +SELECT test_scram_parse_iterations('1'); + test_scram_parse_iterations +----------------------------- + 1 +(1 row) + +SELECT test_scram_parse_iterations('4096'); + test_scram_parse_iterations +----------------------------- + 4096 +(1 row) + +SELECT test_scram_parse_iterations('999999999'); + test_scram_parse_iterations +----------------------------- + 999999999 +(1 row) + +SELECT test_scram_parse_iterations('2147483647'); -- INT_MAX on every supported platform + test_scram_parse_iterations +----------------------------- + 2147483647 +(1 row) + +SELECT test_scram_parse_iterations('0042'); -- leading zeros are still digits + test_scram_parse_iterations +----------------------------- + 42 +(1 row) + +-- Rejected: value below 1 (RFC 5802 requires a positive count). +SELECT test_scram_parse_iterations('0'); + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +-- Rejected: anything that is not a pure digit string. +SELECT test_scram_parse_iterations(''); -- empty string + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('-1'); -- sign character + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('+1'); -- sign character + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations(' 1'); -- leading whitespace + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('1 '); -- trailing whitespace + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('1.0'); -- decimal point + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('1a'); -- digit then letter + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('a1'); -- letter then digit + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('abc'); -- all letters + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('0x10'); -- hex prefix is not digits-only + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +-- Rejected: out-of-range values that would otherwise narrow to a bogus int. +SELECT test_scram_parse_iterations('2147483648'); -- INT_MAX + 1 + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('9999999999'); -- > INT_MAX on every supported platform + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + +SELECT test_scram_parse_iterations('99999999999999999999'); -- overflows long on every supported platform + test_scram_parse_iterations +----------------------------- + invalid +(1 row) + diff --git a/src/test/modules/test_scram/meson.build b/src/test/modules/test_scram/meson.build new file mode 100644 index 00000000000..4039ae5b2af --- /dev/null +++ b/src/test/modules/test_scram/meson.build @@ -0,0 +1,33 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +test_scram_sources = files( + 'test_scram.c', +) + +if host_system == 'windows' + test_scram_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'test_scram', + '--FILEDESC', 'test_scram - test SCRAM helpers in src/common',]) +endif + +test_scram = shared_module('test_scram', + test_scram_sources, + kwargs: pg_test_mod_args, +) +test_install_libs += test_scram + +test_install_data += files( + 'test_scram.control', + 'test_scram--1.0.sql', +) + +tests += { + 'name': 'test_scram', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'test_scram', + ], + }, +} diff --git a/src/test/modules/test_scram/sql/test_scram.sql b/src/test/modules/test_scram/sql/test_scram.sql new file mode 100644 index 00000000000..113b92a9736 --- /dev/null +++ b/src/test/modules/test_scram/sql/test_scram.sql @@ -0,0 +1,31 @@ +-- +-- Direct tests for scram_parse_iterations() in src/common/scram-common.c. +-- +CREATE EXTENSION test_scram; + +-- Accepted: non-empty digit-only strings whose value is in [1, INT_MAX]. +SELECT test_scram_parse_iterations('1'); +SELECT test_scram_parse_iterations('4096'); +SELECT test_scram_parse_iterations('999999999'); +SELECT test_scram_parse_iterations('2147483647'); -- INT_MAX on every supported platform +SELECT test_scram_parse_iterations('0042'); -- leading zeros are still digits + +-- Rejected: value below 1 (RFC 5802 requires a positive count). +SELECT test_scram_parse_iterations('0'); + +-- Rejected: anything that is not a pure digit string. +SELECT test_scram_parse_iterations(''); -- empty string +SELECT test_scram_parse_iterations('-1'); -- sign character +SELECT test_scram_parse_iterations('+1'); -- sign character +SELECT test_scram_parse_iterations(' 1'); -- leading whitespace +SELECT test_scram_parse_iterations('1 '); -- trailing whitespace +SELECT test_scram_parse_iterations('1.0'); -- decimal point +SELECT test_scram_parse_iterations('1a'); -- digit then letter +SELECT test_scram_parse_iterations('a1'); -- letter then digit +SELECT test_scram_parse_iterations('abc'); -- all letters +SELECT test_scram_parse_iterations('0x10'); -- hex prefix is not digits-only + +-- Rejected: out-of-range values that would otherwise narrow to a bogus int. +SELECT test_scram_parse_iterations('2147483648'); -- INT_MAX + 1 +SELECT test_scram_parse_iterations('9999999999'); -- > INT_MAX on every supported platform +SELECT test_scram_parse_iterations('99999999999999999999'); -- overflows long on every supported platform diff --git a/src/test/modules/test_scram/test_scram--1.0.sql b/src/test/modules/test_scram/test_scram--1.0.sql new file mode 100644 index 00000000000..363e2a267b9 --- /dev/null +++ b/src/test/modules/test_scram/test_scram--1.0.sql @@ -0,0 +1,16 @@ +/* src/test/modules/test_scram/test_scram--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_scram" to load this file. \quit + +-- +-- test_scram_parse_iterations(text) -> text +-- +-- Returns the parsed iteration count as text, or 'invalid' if the +-- parser rejects the input. Used to exercise scram_parse_iterations() +-- in src/common/scram-common.c. +-- +CREATE FUNCTION test_scram_parse_iterations(IN input text) +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; diff --git a/src/test/modules/test_scram/test_scram.c b/src/test/modules/test_scram/test_scram.c new file mode 100644 index 00000000000..681536e70d3 --- /dev/null +++ b/src/test/modules/test_scram/test_scram.c @@ -0,0 +1,45 @@ +/*-------------------------------------------------------------------------- + * + * test_scram.c + * Test harness for SCRAM helpers in src/common/scram-common.c. + * + * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_scram/test_scram.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "common/scram-common.h" +#include "fmgr.h" +#include "utils/builtins.h" + +PG_MODULE_MAGIC; + +/* + * Wrapper around scram_parse_iterations() for direct testing. + * + * Returns 'invalid' when the parser rejects the input, otherwise the + * parsed integer value as text. Returning text rather than a record + * keeps the regression output one column wide and easy to read. + */ +PG_FUNCTION_INFO_V1(test_scram_parse_iterations); +Datum +test_scram_parse_iterations(PG_FUNCTION_ARGS) +{ + text *input = PG_GETARG_TEXT_PP(0); + char *cstr = text_to_cstring(input); + int iter = -1; + char buf[32]; + + if (scram_parse_iterations(cstr, &iter)) + snprintf(buf, sizeof(buf), "%d", iter); + else + snprintf(buf, sizeof(buf), "invalid"); + + pfree(cstr); + PG_RETURN_TEXT_P(cstring_to_text(buf)); +} diff --git a/src/test/modules/test_scram/test_scram.control b/src/test/modules/test_scram/test_scram.control new file mode 100644 index 00000000000..c25017b9d81 --- /dev/null +++ b/src/test/modules/test_scram/test_scram.control @@ -0,0 +1,5 @@ +# test_scram extension +comment = 'Test SCRAM helpers in src/common' +default_version = '1.0' +module_pathname = '$libdir/test_scram' +relocatable = true -- 2.43.0