From 12fd8617dc1c1f3089c8395fdf0a265466a31e39 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Mon, 12 Oct 2026 11:09:02 +0200
Subject: [PATCH] Add replace_conf test utility function

A common pattern when starting a new test is to remove the configuration
and write a new one, this is especially prevalent in authn/authz tests.
This adds a new test function replace_conf() which overwrites the config
without the need for the test code to delete it first.

Author: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: ...
Discussion: ...
---
 contrib/dblink/t/001_auth_scram.pl            | 14 ++---
 contrib/postgres_fdw/t/001_auth_scram.pl      | 10 +--
 src/test/authentication/t/001_password.pl     |  3 +-
 src/test/authentication/t/002_saslprep.pl     |  3 +-
 src/test/authentication/t/003_peer.pl         |  6 +-
 src/test/kerberos/t/001_auth.pl               | 15 ++---
 src/test/ldap/t/001_auth.pl                   | 39 ++++--------
 src/test/ldap/t/002_bindpasswd.pl             |  6 +-
 .../t/001_mutated_bindpasswd.pl               |  9 +--
 .../modules/oauth_validator/t/001_server.pl   | 30 +++------
 .../modules/oauth_validator/t/002_client.pl   |  3 +-
 src/test/perl/PostgreSQL/Test/Cluster.pm      | 24 +++++++
 src/test/ssl/t/004_sni.pl                     | 63 +++++++------------
 src/test/subscription/t/027_nosuperuser.pl    |  3 +-
 14 files changed, 91 insertions(+), 137 deletions(-)

diff --git a/contrib/dblink/t/001_auth_scram.pl b/contrib/dblink/t/001_auth_scram.pl
index 8d6280656d5..5f6939bd112 100644
--- a/contrib/dblink/t/001_auth_scram.pl
+++ b/contrib/dblink/t/001_auth_scram.pl
@@ -69,16 +69,13 @@ my $rolpassword = $node1->safe_psql('postgres',
 	qq"SELECT rolpassword FROM pg_authid WHERE rolname = '$user';");
 $node2->safe_psql('postgres', qq"ALTER ROLE $user PASSWORD '$rolpassword'");
 
-unlink($node1->data_dir . '/pg_hba.conf');
-unlink($node2->data_dir . '/pg_hba.conf');
-
-$node1->append_conf(
+$node1->replace_conf(
 	'pg_hba.conf', qq{
 local   db0             all                                     scram-sha-256
 local   db1             all                                     scram-sha-256
 }
 );
-$node2->append_conf(
+$node2->replace_conf(
 	'pg_hba.conf', qq{
 local   db2             all                                     scram-sha-256
 }
@@ -120,16 +117,13 @@ test_fdw_auth_with_invalid_overwritten_require_auth($fdw_invalid_server);
 }
 
 # Ensure that trust connections fail without superuser opt-in.
-unlink($node1->data_dir . '/pg_hba.conf');
-unlink($node2->data_dir . '/pg_hba.conf');
-
-$node1->append_conf(
+$node1->replace_conf(
 	'pg_hba.conf', qq{
 local   db0             all                                     scram-sha-256
 local   db1             all                                     trust
 }
 );
-$node2->append_conf(
+$node2->replace_conf(
 	'pg_hba.conf', qq{
 local   all             all                                     password
 }
diff --git a/contrib/postgres_fdw/t/001_auth_scram.pl b/contrib/postgres_fdw/t/001_auth_scram.pl
index 972faf1f552..2875d40d952 100644
--- a/contrib/postgres_fdw/t/001_auth_scram.pl
+++ b/contrib/postgres_fdw/t/001_auth_scram.pl
@@ -105,16 +105,13 @@ SKIP:
 	skip "test requires Unix-domain sockets", 4 if !$use_unix_sockets;
 
 	# Ensure that trust connections fail without superuser opt-in.
-	unlink($node1->data_dir . '/pg_hba.conf');
-	unlink($node2->data_dir . '/pg_hba.conf');
-
-	$node1->append_conf(
+	$node1->replace_conf(
 		'pg_hba.conf', qq{
 local   db0             all                                     scram-sha-256
 local   db1             all                                     trust
 }
 	);
-	$node2->append_conf(
+	$node2->replace_conf(
 		'pg_hba.conf', qq{
 local   all             all                                     password
 }
@@ -182,8 +179,7 @@ sub setup_pghba
 {
 	my ($node) = @_;
 
-	unlink($node->data_dir . '/pg_hba.conf');
-	$node->append_conf(
+	$node->replace_conf(
 		'pg_hba.conf', qq{
 	local   all             all                                     scram-sha-256
 	host    all             all             $hostaddr/32            scram-sha-256
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index ac67935c258..7eeeb5f13c9 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -30,9 +30,8 @@ sub reset_pg_hba
 	my $role = shift;
 	my $hba_method = shift;
 
-	unlink($node->data_dir . '/pg_hba.conf');
 	# just for testing purposes, use a continuation line
-	$node->append_conf('pg_hba.conf',
+	$node->replace_conf('pg_hba.conf',
 		"local $database $role\\\n $hba_method");
 	$node->reload;
 	return;
diff --git a/src/test/authentication/t/002_saslprep.pl b/src/test/authentication/t/002_saslprep.pl
index c41c62873bb..10e35c85ec9 100644
--- a/src/test/authentication/t/002_saslprep.pl
+++ b/src/test/authentication/t/002_saslprep.pl
@@ -23,8 +23,7 @@ sub reset_pg_hba
 	my $node = shift;
 	my $hba_method = shift;
 
-	unlink($node->data_dir . '/pg_hba.conf');
-	$node->append_conf('pg_hba.conf', "local all all $hba_method");
+	$node->replace_conf('pg_hba.conf', "local all all $hba_method");
 	$node->reload;
 	return;
 }
diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl
index 686e409ce6a..7d91de8c67b 100644
--- a/src/test/authentication/t/003_peer.pl
+++ b/src/test/authentication/t/003_peer.pl
@@ -23,8 +23,7 @@ sub reset_pg_hba
 	my $node = shift;
 	my $hba_method = shift;
 
-	unlink($node->data_dir . '/pg_hba.conf');
-	$node->append_conf('pg_hba.conf', "local all all $hba_method");
+	$node->replace_conf('pg_hba.conf', "local all all $hba_method");
 	$node->reload;
 	return;
 }
@@ -38,8 +37,7 @@ sub reset_pg_ident
 	my $system_user = shift;
 	my $pg_user = shift;
 
-	unlink($node->data_dir . '/pg_ident.conf');
-	$node->append_conf('pg_ident.conf', "$map_name $system_user $pg_user");
+	$node->replace_conf('pg_ident.conf', "$map_name $system_user $pg_user");
 	$node->reload;
 	return;
 }
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index d4018acde88..6b0b131438d 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -168,8 +168,7 @@ sub test_query
 	return;
 }
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf',
 	qq{
 local all test2 scram-sha-256
@@ -313,8 +312,7 @@ test_query(
 	'gssencmode=require',
 	'testing system_user with parallel workers');
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf',
 	qq{
     local all test2 scram-sha-256
@@ -503,8 +501,7 @@ $node->connect_ok(
 	  . " user=test1 host=$host hostaddr=$hostaddr gssencmode=require require_auth=gss,scram-sha-256",
 	"multiple authentication types requested, works with GSS encryption");
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf',
 	qq{
     local all test2 scram-sha-256
@@ -595,8 +592,7 @@ like($psql_out, qr/^$/,
 );
 
 truncate($node->data_dir . '/pg_ident.conf', 0);
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf',
 	qq{
     local all test2 scram-sha-256
@@ -630,8 +626,7 @@ test_query(
 
 # Reset pg_hba.conf, and cause a usermap failure with an authentication
 # that has passed.
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf',
 	qq{
     local all test2 scram-sha-256
diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl
index 8afb88ed8cc..3e633cdf115 100644
--- a/src/test/ldap/t/001_auth.pl
+++ b/src/test/ldap/t/001_auth.pl
@@ -78,8 +78,7 @@ sub test_access
 
 note "simple bind";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="uid=" ldapsuffix=",dc=example,dc=net"}
 );
 $node->restart;
@@ -110,8 +109,7 @@ $node->connect_fails("user=test1 require_auth=scram-sha-256",
 
 note "search+bind";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn"}
 );
 $node->restart;
@@ -131,8 +129,7 @@ test_access(
 
 note "multiple servers";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver="$ldap_server $ldap_server" ldapport=$ldap_port ldapbasedn="$ldap_basedn"}
 );
 $node->restart;
@@ -147,8 +144,7 @@ test_access($node, 'test1', 0, 'search+bind authentication succeeds');
 
 note "LDAP URLs";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapurl="$ldap_url" ldapprefix="uid=" ldapsuffix=",dc=example,dc=net"}
 );
 $node->restart;
@@ -163,8 +159,7 @@ $ENV{"PGPASSWORD"} = 'secret1';
 test_access($node, 'test1', 0,
 	'simple bind with LDAP URL authentication succeeds');
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapurl="$ldap_url/$ldap_basedn?uid?sub"});
 $node->restart;
 
@@ -180,8 +175,7 @@ test_access($node, 'test1', 0,
 
 note "search filters";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(|(uid=\$username)(mail=\$username))"}
 );
 $node->restart;
@@ -205,8 +199,7 @@ test_access(
 
 note "search filters in LDAP URLs";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapurl="$ldap_url/$ldap_basedn??sub?(|(uid=\$username)(mail=\$username))"}
 );
 $node->restart;
@@ -219,8 +212,7 @@ test_access($node, 'test2@example.net', 0, 'search filter finds by mail');
 # This is not documented: You can combine ldapurl and other ldap*
 # settings.  ldapurl is always parsed first, then the other settings
 # override.  It might be useful in a case like this.
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapurl="$ldap_url/$ldap_basedn??sub" ldapsearchfilter="(|(uid=\$username)(mail=\$username))"}
 );
 $node->restart;
@@ -231,8 +223,7 @@ test_access($node, 'test1', 0, 'combined LDAP URL and search filter');
 note "diagnostic message";
 
 # note bad ldapprefix with a question mark that triggers a diagnostic message
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="?uid=" ldapsuffix=""}
 );
 $node->restart;
@@ -243,8 +234,7 @@ test_access($node, 'test1', 2, 'any attempt fails due to bad search pattern');
 note "TLS";
 
 # request StartTLS with ldaptls=1
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)" ldaptls=1}
 );
 $node->restart;
@@ -253,8 +243,7 @@ $ENV{"PGPASSWORD"} = 'secret1';
 test_access($node, 'test1', 0, 'StartTLS');
 
 # request LDAPS with ldapscheme=ldaps
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapscheme=ldaps ldapport=$ldaps_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)"}
 );
 $node->restart;
@@ -263,8 +252,7 @@ $ENV{"PGPASSWORD"} = 'secret1';
 test_access($node, 'test1', 0, 'LDAPS');
 
 # request LDAPS with ldapurl=ldaps://...
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)"}
 );
 $node->restart;
@@ -273,8 +261,7 @@ $ENV{"PGPASSWORD"} = 'secret1';
 test_access($node, 'test1', 0, 'LDAPS with URL');
 
 # bad combination of LDAPS and StartTLS
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)" ldaptls=1}
 );
 $node->restart;
diff --git a/src/test/ldap/t/002_bindpasswd.pl b/src/test/ldap/t/002_bindpasswd.pl
index 64cb2e412bb..4a4037c9df1 100644
--- a/src/test/ldap/t/002_bindpasswd.pl
+++ b/src/test/ldap/t/002_bindpasswd.pl
@@ -72,8 +72,7 @@ sub test_access
 
 note "use ldapbindpasswd";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn ldapbindpasswd=wrong}
 );
 $node->restart;
@@ -82,8 +81,7 @@ $ENV{"PGPASSWORD"} = 'secret1';
 test_access($node, 'test1', 2,
 	'search+bind authentication fails with wrong ldapbindpasswd');
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$ldap_rootpw"}
 );
 $node->restart;
diff --git a/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl b/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl
index 0f254939df5..6cbb641d4a1 100644
--- a/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl
+++ b/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl
@@ -74,8 +74,7 @@ note "use ldapbindpasswd";
 
 $ENV{"PGPASSWORD"} = 'secret1';
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd=wrong}
 );
 $node->restart;
@@ -83,8 +82,7 @@ $node->restart;
 test_access($node, 'test1', 2,
 	'search+bind authentication fails with wrong ldapbindpasswd');
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$clear_ldap_rootpw"}
 );
 $node->restart;
@@ -92,8 +90,7 @@ $node->restart;
 test_access($node, 'test1', 2,
 	'search+bind authentication fails with clear password');
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf('pg_hba.conf',
+$node->replace_conf('pg_hba.conf',
 	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$rot13_ldap_rootpw"}
 );
 $node->restart;
diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl
index 444de01ea5d..6373bcc04d7 100644
--- a/src/test/modules/oauth_validator/t/001_server.pl
+++ b/src/test/modules/oauth_validator/t/001_server.pl
@@ -77,8 +77,7 @@ END
 my $port = $webserver->port();
 my $issuer = "http://127.0.0.1:$port";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all test oauth issuer="$issuer" scope="openid postgres"
 });
@@ -111,8 +110,7 @@ $node->connect_fails(
 # Switch to HTTPS.
 $issuer = "https://127.0.0.1:$port";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all test      oauth issuer="$issuer"       scope="openid postgres"
 local all testalt   oauth issuer="$issuer/.well-known/oauth-authorization-server/alternate" scope="openid postgres alt"
@@ -677,15 +675,13 @@ $bgconn->query_safe("ALTER SYSTEM RESET oauth_validator.invalid_hba");
 #
 
 # Allow "user@example.com" to log in under the test role.
-unlink($node->data_dir . '/pg_ident.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_ident.conf', qq{
 oauthmap	user\@example.com	test
 });
 
 # test and testalt use the map; testparam uses ident delegation.
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all test      oauth issuer="$issuer" scope="" map=oauthmap
 local all testalt   oauth issuer="$issuer" scope="" map=oauthmap
@@ -746,8 +742,7 @@ $bgconn->quit;    # the tests below restart the server
 # Test validator-specific HBA options.
 #
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all test    oauth issuer="$issuer" scope="openid postgres" delegate_ident_mapping=1 \\
                         validator.authn_id="ignored" validator.authn_id="other-identity"
@@ -775,8 +770,7 @@ $node->connect_ok(
 	]);
 
 # bad syntax
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all testalt oauth issuer="$issuer" scope="openid postgres" validator.=1
 });
@@ -787,8 +781,7 @@ $node->log_check("empty HBA option name",
 	$log_start,
 	log_like => [qr/invalid OAuth validator option name: "validator\."/]);
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all testalt oauth issuer="$issuer" scope="openid postgres" validator.@@=1
 });
@@ -800,8 +793,7 @@ $node->log_check("invalid HBA option name",
 	log_like => [qr/invalid OAuth validator option name: "validator\.@@"/]);
 
 # unknown settings (validation is deferred to connect time)
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all testalt oauth issuer="$issuer" scope="openid postgres" \\
                         validator.log=ignored validator.bad=1
@@ -834,8 +826,7 @@ $log_start = $node->wait_for_log(
 	qr/authentication method "oauth" requires option "validator" to be set/,
 	$log_start);
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all test    oauth validator=validator      issuer="$issuer"           scope="openid postgres"
 local all testalt oauth validator=fail_validator issuer="$issuer/.well-known/oauth-authorization-server/alternate" scope="openid postgres alt"
@@ -865,8 +856,7 @@ $node->connect_fails(
 #
 $node->append_conf('postgresql.conf',
 	"oauth_validator_libraries = 'magic_validator'\n");
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all test    oauth validator=magic_validator      issuer="$issuer"           scope="openid postgres"
 });
diff --git a/src/test/modules/oauth_validator/t/002_client.pl b/src/test/modules/oauth_validator/t/002_client.pl
index dac684d7852..cdb136eeba6 100644
--- a/src/test/modules/oauth_validator/t/002_client.pl
+++ b/src/test/modules/oauth_validator/t/002_client.pl
@@ -42,8 +42,7 @@ $node->safe_psql('postgres', 'CREATE USER test;');
 my $issuer = "https://256.256.256.256";
 my $scope = "openid postgres";
 
-unlink($node->data_dir . '/pg_hba.conf');
-$node->append_conf(
+$node->replace_conf(
 	'pg_hba.conf', qq{
 local all test oauth issuer="$issuer" scope="$scope"
 });
diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 920d831be9e..f44d087a589 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -799,6 +799,30 @@ sub append_conf
 
 =pod
 
+=item $node->replace_conf(filename, str)
+
+A shortcut method for rewriting a configuration file.
+
+Does no validation or sanity checking. Does not reload the configuration
+after writing.
+
+A newline is automatically appended to the string
+
+=cut
+
+sub replace_conf
+{
+	my ($self, $filename, $str) = @_;
+
+	my $conffile = $self->data_dir . '/' . $filename;
+	unlink($conffile) or croak("unable to delete $conffile");
+	$self->append_conf($filename, $str);
+
+	return;
+}
+
+=pod
+
 =item $node->adjust_conf(filename, setting, value, skip_equals)
 
 Modify the named config file setting with the value. If the value is undefined,
diff --git a/src/test/ssl/t/004_sni.pl b/src/test/ssl/t/004_sni.pl
index 635e637bdd3..3d41169321c 100644
--- a/src/test/ssl/t/004_sni.pl
+++ b/src/test/ssl/t/004_sni.pl
@@ -80,8 +80,7 @@ my $connstr =
 		"$verify_connstr host=common-name.pg-ssltest.test",
 		'reload enables SNI with the per-host passphrase command');
 
-	ok(unlink($node->data_dir . '/pg_hosts.conf'));
-	$node->append_conf('pg_hosts.conf', '');
+	$node->replace_conf('pg_hosts.conf', '');
 	$node->append_conf('postgresql.conf', 'ssl_sni = off');
 	$ssl_server->switch_server_cert($node, certfile => 'server-cn-only');
 }
@@ -133,8 +132,7 @@ common-name.pg-ssltest.test server-cn-only.crt server-cn-only.key
 	$node->connect_ok("$verify_connstr host=192.0.2.1 sslsni=0",
 		'successful retry serves the configured certificate');
 
-	ok(unlink($node->data_dir . '/pg_hosts.conf'));
-	$node->append_conf('pg_hosts.conf', '');
+	$node->replace_conf('pg_hosts.conf', '');
 	$ssl_server->switch_server_cert($node, certfile => 'server-cn-only');
 }
 
@@ -225,8 +223,7 @@ $node->connect_ok(
 	"pg_hosts.conf: connect to default with sslmode=require");
 
 # Use multiple hostnames for a single configuration
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	"example.org,example.com,example.net server-cn-only+server_ca.crt server-cn-only.key root_ca.crt"
 );
 $node->reload;
@@ -246,8 +243,7 @@ $node->connect_fails(
 	expected_stderr => qr/unrecognized name/);
 
 # Test @-inclusion of hostnames.
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	'example.org,@hostnames.txt server-cn-only+server_ca.crt server-cn-only.key root_ca.crt'
 );
 $node->append_conf(
@@ -272,8 +268,7 @@ $node->connect_fails(
 	expected_stderr => qr/unrecognized name/);
 
 # Add an incorrect entry specifying a default entry combined with hostnames
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	"example.org,*,example.net server-cn-only+server_ca.crt server-cn-only.key root_ca.crt"
 );
 my $result = $node->restart(fail_ok => 1);
@@ -282,8 +277,7 @@ is($result, 0,
 );
 
 # Add incorrect duplicate entries.
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf(
+$node->replace_conf(
 	'pg_hosts.conf', qq{
 * server-cn-only.crt server-cn-only.key
 * server-cn-only.crt server-cn-only.key
@@ -291,8 +285,7 @@ $node->append_conf(
 $result = $node->restart(fail_ok => 1);
 is($result, 0, 'pg_hosts.conf: restart fails with two default entries');
 
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf(
+$node->replace_conf(
 	'pg_hosts.conf', qq{
 /no_sni/ server-cn-only.crt server-cn-only.key
 /no_sni/ server-cn-only.crt server-cn-only.key
@@ -300,8 +293,7 @@ $node->append_conf(
 $result = $node->restart(fail_ok => 1);
 is($result, 0, 'pg_hosts.conf: restart fails with two no_sni entries');
 
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf(
+$node->replace_conf(
 	'pg_hosts.conf', qq{
 example.org server-cn-only.crt server-cn-only.key
 example.net server-cn-only.crt server-cn-only.key
@@ -310,8 +302,7 @@ example.org server-cn-only.crt server-cn-only.key
 $result = $node->restart(fail_ok => 1);
 is($result, 0,
 	'pg_hosts.conf: restart fails with two identical hostname entries');
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf(
+$node->replace_conf(
 	'pg_hosts.conf', qq{
 example.org server-cn-only.crt server-cn-only.key
 example.net,example.com,Example.org server-cn-only.crt server-cn-only.key
@@ -322,8 +313,7 @@ is($result, 0,
 );
 
 # Modify pg_hosts.conf to no longer have the default host entry.
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	"example.org server-cn-only+server_ca.crt server-cn-only.key root_ca.crt"
 );
 $node->restart;
@@ -420,8 +410,7 @@ $node->reload;
 
 # Reconfigure with broken configuration for the key passphrase, the server
 # should not start up
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	'localhost server-cn-only.crt server-password.key root+client_ca.crt "echo wrongpassword" on'
 );
 $result = $node->restart(fail_ok => 1);
@@ -430,8 +419,7 @@ is($result, 0,
 );
 
 # Reconfigure again but with the correct passphrase set
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	'localhost server-cn-only.crt server-password.key root+client_ca.crt "echo secret1" on'
 );
 $result = $node->restart(fail_ok => 1);
@@ -460,8 +448,7 @@ $node->connect_ok(
 # Test reloading a passphrase protected key without reloading support in the
 # passphrase hook. Restarting should not give any errors in the log, but the
 # subsequent reload should fail with an error regarding reloading.
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	'localhost server-cn-only.crt server-password.key root+client_ca.crt "echo secret1" off'
 );
 $node_loglocation = -s $node->logfile;
@@ -505,8 +492,7 @@ SKIP:
 }
 
 # Configure with only non-SNI connections allowed
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	"/no_sni/ server-cn-only.crt server-cn-only.key");
 $node->restart;
 
@@ -521,13 +507,10 @@ $node->connect_fails(
 
 # Test client CAs
 
-# pg_hosts configuration
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-
-# Neither ssl_ca_file nor the default host should have any effect whatsoever on
-# the following tests.
+# pg_hosts configuration; Neither ssl_ca_file nor the default host should have
+# any effect whatsoever on the following tests.
 $node->append_conf('postgresql.conf', "ssl_ca_file = 'root+client_ca.crt'");
-$node->append_conf('pg_hosts.conf',
+$node->replace_conf('pg_hosts.conf',
 	'* server-cn-only.crt server-cn-only.key root+client_ca.crt');
 
 # example.org has an unconfigured CA.
@@ -591,18 +574,14 @@ $node->connect_fails(
 	"host: 'example.com', ca: 'root+client_ca.crt': connect fails with revoked client cert",
 	expected_stderr => qr/certificate revoked/);
 
-# pg_hosts configuration with useless data at EOL
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-# example.org has an unconfigured CA.
-$node->append_conf('pg_hosts.conf',
+# pg_hosts configuration with useless data at EOL, example.org has an unconfigured CA.
+$node->replace_conf('pg_hosts.conf',
 	'example.org server-cn-only.crt server-cn-only.key root+client_ca.crt "cmd" on TRAILING_TEXT MORE_TEXT'
 );
 $result = $node->restart(fail_ok => 1);
 is($result, 0, 'pg_hosts.conf: restart fails with extra data at EOL');
-# pg_hosts configuration with useless data at EOL
-ok(unlink($node->data_dir . '/pg_hosts.conf'));
-# example.org has an unconfigured CA.
-$node->append_conf('pg_hosts.conf',
+# pg_hosts configuration with useless data at EOL, example.org has an unconfigured CA.
+$node->replace_conf('pg_hosts.conf',
 	'example.org server-cn-only.crt server-cn-only.key root+client_ca.crt "cmd" notabooleanvalue'
 );
 $result = $node->restart(fail_ok => 1);
diff --git a/src/test/subscription/t/027_nosuperuser.pl b/src/test/subscription/t/027_nosuperuser.pl
index 322f5b4cc6a..5bba9bbb2f2 100644
--- a/src/test/subscription/t/027_nosuperuser.pl
+++ b/src/test/subscription/t/027_nosuperuser.pl
@@ -379,8 +379,7 @@ SKIP:
 
 	# Setup pg_hba configuration so that logical replication connection without
 	# password is not allowed.
-	unlink($node_publisher1->data_dir . '/pg_hba.conf');
-	$node_publisher1->append_conf('pg_hba.conf',
+	$node_publisher1->replace_conf('pg_hba.conf',
 		qq{local all 				regress_test_user 	md5});
 	$node_publisher1->reload;
 
-- 
2.39.3 (Apple Git-146)

