From 66362ceec2359db43e42251787e233c384078483 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sun, 29 Oct 2023 16:48:16 +0100
Subject: [PATCH v4 3/3] pg_resetwal: Add more tests and test coverage

Discussion: https://www.postgresql.org/message-id/flat/0f3ab4a1-ae80-56e8-3426-6b4a02507687@eisentraut.org
---
 src/bin/pg_resetwal/t/001_basic.pl     | 120 +++++++++++++++++++++++++
 src/bin/pg_resetwal/t/002_corrupted.pl |   4 +
 2 files changed, 124 insertions(+)

diff --git a/src/bin/pg_resetwal/t/001_basic.pl b/src/bin/pg_resetwal/t/001_basic.pl
index 7e5efbf56b..6c02c4ae74 100644
--- a/src/bin/pg_resetwal/t/001_basic.pl
+++ b/src/bin/pg_resetwal/t/001_basic.pl
@@ -14,6 +14,7 @@ program_options_handling_ok('pg_resetwal');
 
 my $node = PostgreSQL::Test::Cluster->new('main');
 $node->init;
+$node->append_conf('postgresql.conf', 'track_commit_timestamp = on');
 
 command_like([ 'pg_resetwal', '-n', $node->data_dir ],
 	qr/checkpoint/, 'pg_resetwal -n produces output');
@@ -29,4 +30,123 @@ SKIP:
 		'check PGDATA permissions');
 }
 
+command_ok([ 'pg_resetwal', '-D', $node->data_dir ], 'pg_resetwal runs');
+$node->start;
+is($node->safe_psql("postgres", "SELECT 1;"), 1, 'server running and working after reset');
+
+command_fails_like([ 'pg_resetwal', $node->data_dir ], qr/lock file .* exists/, 'fails if server running');
+
+$node->stop('immediate');
+command_fails_like([ 'pg_resetwal', $node->data_dir ], qr/database server was not shut down cleanly/, 'does not run after immediate shutdown');
+command_ok([ 'pg_resetwal', '-f', $node->data_dir ], 'runs after immediate shutdown with force');
+$node->start;
+is($node->safe_psql("postgres", "SELECT 1;"), 1, 'server running and working after forced reset');
+
+$node->stop;
+
+# check various command-line handling
+
+# Note: This test intends to check that a nonexistent data directory
+# gives a reasonable error message.  Because of the way the code is
+# currently structured, you get an error about readings permissions,
+# which is perhaps suboptimal, so feel free to update this test if
+# this gets improved.
+command_fails_like([ 'pg_resetwal', 'foo' ], qr/error: could not read permissions of directory/, 'fails with nonexistent data directory');
+
+command_fails_like([ 'pg_resetwal', 'foo', 'bar'], qr/too many command-line arguments/, 'fails with too many command-line arguments');
+
+$ENV{PGDATA} = $node->data_dir;  # not used
+command_fails_like([ 'pg_resetwal' ], qr/no data directory specified/, 'fails with too few command-line arguments');
+
+# error cases
+# -c
+command_fails_like([ 'pg_resetwal', '-c', 'foo', $node->data_dir], qr/error: invalid argument for option -c/, 'fails with incorrect -c option');
+command_fails_like([ 'pg_resetwal', '-c', '10,bar', $node->data_dir], qr/error: invalid argument for option -c/, 'fails with incorrect -c option part 2');
+command_fails_like([ 'pg_resetwal', '-c', '1,10', $node->data_dir], qr/greater than/, 'fails with -c value 1 part 1');
+command_fails_like([ 'pg_resetwal', '-c', '10,1', $node->data_dir], qr/greater than/, 'fails with -c value 1 part 2');
+# -e
+command_fails_like([ 'pg_resetwal', '-e', 'foo', $node->data_dir], qr/error: invalid argument for option -e/, 'fails with incorrect -e option');
+command_fails_like([ 'pg_resetwal', '-e', '-1', $node->data_dir], qr/must not be -1/, 'fails with -e value -1');
+# -l
+command_fails_like([ 'pg_resetwal', '-l', 'foo', $node->data_dir], qr/error: invalid argument for option -l/, 'fails with incorrect -l option');
+# -m
+command_fails_like([ 'pg_resetwal', '-m', 'foo', $node->data_dir], qr/error: invalid argument for option -m/, 'fails with incorrect -m option');
+command_fails_like([ 'pg_resetwal', '-m', '10,bar', $node->data_dir], qr/error: invalid argument for option -m/, 'fails with incorrect -m option part 2');
+command_fails_like([ 'pg_resetwal', '-m', '0,10', $node->data_dir], qr/must not be 0/, 'fails with -m value 0 part 1');
+command_fails_like([ 'pg_resetwal', '-m', '10,0', $node->data_dir], qr/must not be 0/, 'fails with -m value 0 part 2');
+# -o
+command_fails_like([ 'pg_resetwal', '-o', 'foo', $node->data_dir], qr/error: invalid argument for option -o/, 'fails with incorrect -o option');
+command_fails_like([ 'pg_resetwal', '-o', '0', $node->data_dir], qr/must not be 0/, 'fails with -o value 0');
+# -O
+command_fails_like([ 'pg_resetwal', '-O', 'foo', $node->data_dir], qr/error: invalid argument for option -O/, 'fails with incorrect -O option');
+command_fails_like([ 'pg_resetwal', '-O', '-1', $node->data_dir], qr/must not be -1/, 'fails with -O value -1');
+# --wal-segsize
+command_fails_like([ 'pg_resetwal', '--wal-segsize', 'foo', $node->data_dir], qr/error: invalid value/, 'fails with incorrect --wal-segsize option');
+command_fails_like([ 'pg_resetwal', '--wal-segsize', '13', $node->data_dir], qr/must be a power/, 'fails with invalid --wal-segsize value');
+# -u
+command_fails_like([ 'pg_resetwal', '-u', 'foo', $node->data_dir], qr/error: invalid argument for option -u/, 'fails with incorrect -u option');
+command_fails_like([ 'pg_resetwal', '-u', '1', $node->data_dir], qr/must be greater than/, 'fails with -u value too small');
+# -x
+command_fails_like([ 'pg_resetwal', '-x', 'foo', $node->data_dir], qr/error: invalid argument for option -x/, 'fails with incorrect -x option');
+command_fails_like([ 'pg_resetwal', '-x', '1', $node->data_dir], qr/must be greater than/, 'fails with -x value too small');
+
+# run with control override options
+
+my $out = (run_command([ 'pg_resetwal', '-n', $node->data_dir ]))[0];
+$out =~ /^Database block size: *(\d+)$/m or die;
+my $blcksz = $1;
+
+my @cmd = ('pg_resetwal', '-D', $node->data_dir);
+
+# some not-so-critical hardcoded values
+push @cmd, '-e', 1;
+push @cmd, '-l', '00000001000000320000004B';
+push @cmd, '-o', 100_000;
+push @cmd, '--wal-segsize', 1;
+
+# these use the guidance from the documentation
+
+sub get_slru_files
+{
+	opendir(my $dh, $node->data_dir . '/' . $_[0]) or die $!;
+	my @files = sort grep { /[0-9A-F]+/ } readdir $dh;
+	closedir $dh;
+	return @files;
+}
+
+my (@files, $mult);
+
+@files = get_slru_files('pg_commit_ts');
+# XXX: Should there be a multiplier, similar to the other options?
+# -c argument is "old,new"
+push @cmd,
+  '-c', sprintf("%d,%d",
+	  hex($files[0]) == 0 ? 3 : hex($files[0]), hex($files[-1]));
+
+@files = get_slru_files('pg_multixact/offsets');
+$mult = 32 * $blcksz / 4;
+# -m argument is "new,old"
+push @cmd,
+  '-m', sprintf("%d,%d",
+	  (hex($files[-1]) + 1) * $mult,
+	  hex($files[0]) == 0 ? 1 : hex($files[0] * $mult));
+
+@files = get_slru_files('pg_multixact/members');
+$mult = 32 * int($blcksz/20) * 4;
+push @cmd,
+  '-O', (hex($files[-1]) + 1) * $mult;
+
+@files = get_slru_files('pg_xact');
+$mult = 32 * $blcksz * 4;
+push @cmd,
+  '-u', (hex($files[0]) == 0 ? 3 : hex($files[0]) * $mult),
+  '-x', ((hex($files[-1]) + 1) * $mult);
+
+command_ok([@cmd, '-n'], 'runs with control override options, dry run');
+command_ok(\@cmd, 'runs with control override options');
+command_like([ 'pg_resetwal', '-n', $node->data_dir ], qr/^Latest checkpoint's NextOID: *100000$/m, 'spot check that control changes were applied');
+
+$node->start;
+ok(1, 'server started after reset');
+
 done_testing();
diff --git a/src/bin/pg_resetwal/t/002_corrupted.pl b/src/bin/pg_resetwal/t/002_corrupted.pl
index b3a37728a4..ee88850473 100644
--- a/src/bin/pg_resetwal/t/002_corrupted.pl
+++ b/src/bin/pg_resetwal/t/002_corrupted.pl
@@ -55,4 +55,8 @@ command_checks_all(
 	],
 	'processes zero WAL segment size');
 
+# now try to run it
+command_fails_like([ 'pg_resetwal', $node->data_dir ], qr/not proceeding because control file values were guessed/, 'does not run when control file values were guessed');
+command_ok([ 'pg_resetwal', '-f', $node->data_dir ], 'runs with force when control file values were guessed');
+
 done_testing();
-- 
2.42.0

