1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/test/lib/pgBackRestTest/Module/Info/InfoUnitPerlTest.pm
David Steele 88a633da17 Divide tests into three types (unit, integration, performance).
Many options that were set per test can instead be inferred from the types, i.e. container, c, expect, and individual.

Also finish renaming Perl unit tests with the -perl suffix.
2018-04-24 09:12:25 -04:00

503 lines
26 KiB
Perl

####################################################################################################################################
# Unit tests for Info module
####################################################################################################################################
package pgBackRestTest::Module::Info::InfoUnitPerlTest;
use parent 'pgBackRestTest::Env::HostEnvTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use File::Basename qw(dirname);
use Storable qw(dclone);
use pgBackRest::Backup::Common;
use pgBackRest::Backup::Info;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Lock;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::DbVersion;
use pgBackRest::Info;
use pgBackRest::InfoCommon;
use pgBackRest::Manifest;
use pgBackRest::Protocol::Helper;
use pgBackRest::Protocol::Storage::Helper;
use pgBackRest::Storage::Local;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Env::ExpireEnvTest;
use pgBackRestTest::Common::FileTest;
use pgBackRestTest::Env::Host::HostBackupTest;
use pgBackRestTest::Env::HostEnvTest;
####################################################################################################################################
# initModule
####################################################################################################################################
use constant STANZA_ENCRYPT => 'encrypt';
####################################################################################################################################
# initModule
####################################################################################################################################
sub initModule
{
my $self = shift;
$self->{strRepoPath} = $self->testPath() . '/repo';
$self->{strArchivePath} = "$self->{strRepoPath}/archive/" . $self->stanza();
$self->{strBackupPath} = "$self->{strRepoPath}/backup/" . $self->stanza();
$self->{strDbPath} = $self->testPath() . '/db';
$self->{strArchivePathEncrypt} = "$self->{strRepoPath}/archive/" . STANZA_ENCRYPT;
$self->{strBackupPathEncrypt} = "$self->{strRepoPath}/backup/" . STANZA_ENCRYPT;
}
####################################################################################################################################
# initTest
####################################################################################################################################
sub initTest
{
my $self = shift;
# Create parent path for pg_control
storageTest()->pathCreate(($self->{strDbPath} . '/' . DB_PATH_GLOBAL), {bCreateParent => true});
# Create archive info path
storageTest()->pathCreate($self->{strArchivePath}, {bIgnoreExists => true, bCreateParent => true});
# Create backup info path
storageTest()->pathCreate($self->{strBackupPath}, {bIgnoreExists => true, bCreateParent => true});
}
####################################################################################################################################
# initStanzaCreate - initialize options and create the stanza object
####################################################################################################################################
sub initStanzaCreate
{
my $self = shift;
my $strStanza = shift;
my $bEncrypted = shift;
my $strDbVersion = shift;
# Set options for stanzaCreate
my $rhConfig = $self->configTestClear();
if (!defined($strStanza))
{
$strStanza = $self->stanza();
}
if (defined($bEncrypted) && $bEncrypted == true)
{
$self->optionTestSet(CFGOPT_REPO_CIPHER_TYPE, CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC);
$self->optionTestSet(CFGOPT_REPO_CIPHER_PASS, 'x');
}
if (!defined($strDbVersion))
{
$strDbVersion = PG_VERSION_94;
}
$self->optionTestSet(CFGOPT_STANZA, $strStanza);
$self->optionTestSet(CFGOPT_PG_PATH, $self->{strDbPath});
$self->optionTestSet(CFGOPT_REPO_PATH, $self->{strRepoPath});
$self->optionTestSet(CFGOPT_LOG_PATH, $self->testPath());
$self->optionTestSetBool(CFGOPT_ONLINE, false);
$self->optionTestSet(CFGOPT_DB_TIMEOUT, 5);
$self->optionTestSet(CFGOPT_PROTOCOL_TIMEOUT, 6);
$self->configTestLoad(CFGCMD_STANZA_CREATE);
$self->configTestSet($rhConfig);
my $oTestObject = "oExpireTest";
# Create the test object
if (defined($bEncrypted) && $bEncrypted == true)
{
$oTestObject = "oExpireTestEncrypt";
}
$self->{$oTestObject} = new pgBackRestTest::Env::ExpireEnvTest(undef, $self->backrestExe(), storageRepo(), undef, $self);
$self->{$oTestObject}->stanzaCreate($strStanza, $strDbVersion);
}
####################################################################################################################################
# initStanzaUpgrade - initialize options and create the stanza object
####################################################################################################################################
sub initStanzaUpgrade
{
my $self = shift;
# Set options for stanzaCreate
my $rhConfig = $self->configTestClear();
$self->optionTestSet(CFGOPT_STANZA, $self->stanza());
$self->optionTestSet(CFGOPT_PG_PATH, $self->{strDbPath});
$self->optionTestSet(CFGOPT_REPO_PATH, $self->{strRepoPath});
$self->optionTestSet(CFGOPT_LOG_PATH, $self->testPath());
$self->optionTestSetBool(CFGOPT_ONLINE, false);
$self->optionTestSet(CFGOPT_DB_TIMEOUT, 5);
$self->optionTestSet(CFGOPT_PROTOCOL_TIMEOUT, 6);
$self->configTestLoad(CFGCMD_STANZA_UPGRADE);
$self->configTestSet($rhConfig);
# Create the test object
$self->{oExpireTest} = new pgBackRestTest::Env::ExpireEnvTest(undef, $self->backrestExe(), storageRepo(), undef, $self);
$self->{oExpireTest}->stanzaUpgrade($self->stanza(), PG_VERSION_95);
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Used to create backups and WAL to test
use constant SECONDS_PER_DAY => 86400;
my $lBaseTime = 1486137448 - (60 * SECONDS_PER_DAY);
# Create the initial backup "day" number - backup day is incremented for each backup
my $iLastBackup = 5;
################################################################################################################################
if ($self->begin("Info"))
{
$self->configTestLoad(CFGCMD_INFO);
my $oInfo = new pgBackRest::Info();
# Output No stanzas exist in default text option
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oInfo->process()}, undef, 'No stanzas exist and default text option');
# Invalid option
#---------------------------------------------------------------------------------------------------------------------------
cfgOptionSet(CFGOPT_OUTPUT, BOGUS);
$self->testException(sub {$oInfo->process()}, ERROR_ASSERT, "invalid info output option '" . BOGUS . "'");
# Output json option with no stanza defined
#---------------------------------------------------------------------------------------------------------------------------
$self->optionTestSet(CFGOPT_OUTPUT, CFGOPTVAL_INFO_OUTPUT_JSON);
$self->configTestLoad(CFGCMD_INFO);
$self->testResult(sub {$oInfo->process()}, undef, 'json option');
# Add linefeed to JSON
#---------------------------------------------------------------------------------------------------------------------------
my $strJson = '[{"archive" : 1}]';
$self->testResult(sub {$oInfo->outputJSON($strJson)}, 1, 'add linefeed to json');
# Missing stanza path
#---------------------------------------------------------------------------------------------------------------------------
my $hyStanza = $oInfo->stanzaList(BOGUS);
$self->testResult(sub {$oInfo->formatText($hyStanza)},
"stanza: bogus\n status: error (missing stanza path)\n",
'missing stanza path');
# formatBackupText - coverage for certain conditions
#---------------------------------------------------------------------------------------------------------------------------
my $oBackupHash =
{
'archive' =>
{
'start' => 1,
'stop' => undef
},
'timestamp' =>
{
'start' => 1481039848,
'stop' => 1481039848,
},
'label' => 'BACKUPLABEL',
'reference' => ['BACKUPREFERENCE'],
'type' => 'BACKUPTYPE',
};
$self->testResult(sub {$oInfo->formatTextBackup($oBackupHash)},
" BACKUPTYPE backup: BACKUPLABEL\n" .
" timestamp start/stop: 2016-12-06 15:57:28 / 2016-12-06 15:57:28\n" .
" wal start/stop: n/a\n" .
" database size: , backup size: \n" .
" repository size: , repository backup size: \n" .
" backup reference list: BACKUPREFERENCE",
'formatTextBackup');
# Test !isRepoLocal branch
#---------------------------------------------------------------------------------------------------------------------------
cfgOptionSet(CFGOPT_REPO_HOST, false);
cfgOptionSet(CFGOPT_REPO_HOST_CONFIG, BOGUS);
$self->testException(sub {$oInfo->stanzaList(BOGUS)}, ERROR_ASSERT, "option repo1-host-cmd is required");
# dbArchiveSection() -- no archive
#---------------------------------------------------------------------------------------------------------------------------
my $hDbInfo =
{
&INFO_HISTORY_ID => 1,
&INFO_DB_VERSION => PG_VERSION_94,
&INFO_SYSTEM_ID => $self->dbSysId(PG_VERSION_94),
};
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, PG_VERSION_94 . '-1', $self->{strArchivePath}, PG_VERSION_94,
$self->dbSysId(PG_VERSION_94))}, "{database => {id => 1}, id => 9.4-1, max => [undef], min => [undef]}",
'no archive, db-ver match, db-sys match');
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, PG_VERSION_94 . '-1', $self->{strArchivePath}, PG_VERSION_94,
$self->dbSysId(PG_VERSION_95))}, undef, 'no archive, db-ver match, db-sys mismatch');
$hDbInfo->{&INFO_DB_VERSION} = PG_VERSION_95;
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, PG_VERSION_94 . '-1', $self->{strArchivePath}, PG_VERSION_94,
$self->dbSysId(PG_VERSION_94))}, undef, 'no archive, db-ver mismatch, db-sys match');
$hDbInfo->{&INFO_SYSTEM_ID} = $self->dbSysId(PG_VERSION_95);
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, PG_VERSION_94 . '-1', $self->{strArchivePath}, PG_VERSION_94,
$self->dbSysId(PG_VERSION_94))}, undef, 'no archive, db-ver mismatch, db-sys mismatch');
# Create more than one stanza but no data
#---------------------------------------------------------------------------------------------------------------------------
$self->optionTestSet(CFGOPT_STANZA, $self->stanza());
$self->optionTestSet(CFGOPT_REPO_PATH, $self->{strRepoPath});
$self->configTestLoad(CFGCMD_INFO);
# Create archive and backup paths but no info files
storageTest()->pathCreate("$self->{strRepoPath}/archive/" . BOGUS, {bIgnoreExists => true, bCreateParent => true});
storageTest()->pathCreate("$self->{strRepoPath}/backup/" . BOGUS, {bIgnoreExists => true, bCreateParent => true});
# Get a list of all stanzas in the repo
$hyStanza = $oInfo->stanzaList();
$self->testResult(sub {$oInfo->formatText($hyStanza)},
"stanza: bogus\n status: error (missing stanza data)\n\n" .
"stanza: db\n status: error (missing stanza data)\n",
'fomatText() multiple stanzas missing data');
# Define the stanza option
#---------------------------------------------------------------------------------------------------------------------------
$self->optionTestSet(CFGOPT_STANZA, $self->stanza());
$self->optionTestClear(CFGOPT_OUTPUT);
$self->configTestLoad(CFGCMD_INFO);
$self->testResult(sub {$oInfo->process()}, undef, 'stanza set');
# Create the stanza - no WAL or backups
#---------------------------------------------------------------------------------------------------------------------------
$self->initStanzaCreate();
$self->configTestLoad(CFGCMD_INFO);
$self->testResult(sub {$oInfo->formatText($oInfo->stanzaList())},
"stanza: bogus\n status: error (missing stanza data)\n\n" .
"stanza: db\n status: error (no valid backups)\n\n" .
" db (current)\n wal archive min/max (9.4-1): none present\n",
"formatText() multiple stanzas, one missing data");
$hyStanza = $oInfo->stanzaList($self->stanza());
$self->testResult(sub {$oInfo->formatText($hyStanza)},
"stanza: db\n status: error (no valid backups)\n\n" .
" db (current)\n wal archive min/max (9.4-1): none present\n",
"formatText() one stanza");
# Create a backup and list backup for just one stanza
#---------------------------------------------------------------------------------------------------------------------------
$self->{oExpireTest}->backupCreate($self->stanza(), CFGOPTVAL_BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY, -1, -1);
$hyStanza = $oInfo->stanzaList($self->stanza());
$self->testResult(sub {$oInfo->formatText($hyStanza)},
"stanza: db\n status: ok\n\n db (current)\n wal archive min/max (9.4-1): none present\n\n" .
" full backup: 20161206-155728F\n" .
" timestamp start/stop: 2016-12-06 15:57:28 / 2016-12-06 15:57:28\n" .
" wal start/stop: n/a\n database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n",
"formatText() one stanza");
# Coverage for major WAL paths with no WAL
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->pathCreate($self->{strArchivePath} . "/9.4-1/0000000100000000", {bIgnoreExists => true});
storageTest()->pathCreate($self->{strArchivePath} . "/9.4-1/0000000200000000", {bIgnoreExists => true});
$hyStanza = $oInfo->stanzaList($self->stanza());
$self->testResult(sub {$oInfo->formatText($hyStanza)},
"stanza: db\n status: ok\n\n db (current)\n wal archive min/max (9.4-1): none present\n\n" .
" full backup: 20161206-155728F\n" .
" timestamp start/stop: 2016-12-06 15:57:28 / 2016-12-06 15:57:28\n" .
" wal start/stop: n/a\n database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n",
"formatText() major WAL paths with no WAL");
# Upgrade postgres version and backup with WAL
#---------------------------------------------------------------------------------------------------------------------------
undef($self->{oExpireTest});
$self->initStanzaUpgrade();
$self->configTestLoad(CFGCMD_INFO);
$self->{oExpireTest}->backupCreate($self->stanza(), CFGOPTVAL_BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY, 1, 1);
$self->{oExpireTest}->backupCreate($self->stanza(), CFGOPTVAL_BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY, 1, 1);
# Remove the 9.4-1 path for condition test coverage
storageTest()->remove($self->{strArchivePath} . "/9.4-1/", {bRecurse => true});
$hyStanza = $oInfo->stanzaList($self->stanza());
$self->testResult(sub {$oInfo->formatText($hyStanza)},
"stanza: db\n status: ok\n" .
"\n db (current)\n" .
" wal archive min/max (9.5-2): 000000010000000000000000 / 000000010000000000000003\n\n" .
" full backup: 20161207-155728F\n" .
" timestamp start/stop: 2016-12-07 15:57:28 / 2016-12-07 15:57:28\n" .
" wal start/stop: 000000010000000000000000 / 000000010000000000000000\n" .
" database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n\n" .
" diff backup: 20161207-155728F_20161208-155728D\n" .
" timestamp start/stop: 2016-12-08 15:57:28 / 2016-12-08 15:57:28\n" .
" wal start/stop: 000000010000000000000002 / 000000010000000000000002\n" .
" database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n" .
"\n db (prior)\n" .
" full backup: 20161206-155728F\n" .
" timestamp start/stop: 2016-12-06 15:57:28 / 2016-12-06 15:57:28\n" .
" wal start/stop: n/a\n" .
" database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n",
"formatText() multiple DB versions");
# Remove backup from db (prior)
#---------------------------------------------------------------------------------------------------------------------------
# Load the backup.info file
my $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath});
my @stryPath = $oBackupInfo->list(backupRegExpGet(true));
# Remove the db prior full backup from the info file and save it
$oBackupInfo->delete($stryPath[0]);
$oBackupInfo->save();
# Remove the backup directory
storageTest()->remove($self->{strBackupPath} . "/" . $stryPath[0], {bRecurse => true});
$hyStanza = $oInfo->stanzaList($self->stanza());
$self->testResult(sub {$oInfo->formatText($hyStanza)},
"stanza: db\n status: ok\n" .
"\n db (current)\n" .
" wal archive min/max (9.5-2): 000000010000000000000000 / 000000010000000000000003\n\n" .
" full backup: 20161207-155728F\n" .
" timestamp start/stop: 2016-12-07 15:57:28 / 2016-12-07 15:57:28\n" .
" wal start/stop: 000000010000000000000000 / 000000010000000000000000\n" .
" database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n\n" .
" diff backup: 20161207-155728F_20161208-155728D\n" .
" timestamp start/stop: 2016-12-08 15:57:28 / 2016-12-08 15:57:28\n" .
" wal start/stop: 000000010000000000000002 / 000000010000000000000002\n" .
" database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n",
"db (prior) removed");
# dbArchiveSection() -- with archive
#---------------------------------------------------------------------------------------------------------------------------
$hDbInfo->{&INFO_HISTORY_ID} = 2;
$hDbInfo->{&INFO_DB_VERSION} = PG_VERSION_95;
$hDbInfo->{&INFO_SYSTEM_ID} = $self->dbSysId(PG_VERSION_95);
my $strArchiveId = PG_VERSION_95 . '-2';
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, $strArchiveId, "$self->{strArchivePath}/$strArchiveId",
PG_VERSION_95, $self->dbSysId(PG_VERSION_95))},
"{database => {id => 2}, id => 9.5-2, max => 000000010000000000000003, min => 000000010000000000000000}",
'archive, db-ver match, db-sys-id match');
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, $strArchiveId, "$self->{strArchivePath}/$strArchiveId",
PG_VERSION_95, $self->dbSysId(PG_VERSION_94))},
"{database => {id => 2}, id => 9.5-2, max => 000000010000000000000003, min => 000000010000000000000000}",
'archive, db-ver match, db-sys-id mismatch');
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, $strArchiveId, "$self->{strArchivePath}/$strArchiveId",
PG_VERSION_94, $self->dbSysId(PG_VERSION_95))},
"{database => {id => 2}, id => 9.5-2, max => 000000010000000000000003, min => 000000010000000000000000}",
'archive, db-ver mismatch, db-sys-id match');
$self->testResult(sub {$oInfo->dbArchiveSection($hDbInfo, $strArchiveId, "$self->{strArchivePath}/$strArchiveId",
PG_VERSION_94, $self->dbSysId(PG_VERSION_94))},
"{database => {id => 2}, id => 9.5-2, max => 000000010000000000000003, min => 000000010000000000000000}",
'archive, db-ver mismatch, db-sys-id mismatch');
# Set last backup run in this test set
$iLastBackup = 8;
}
################################################################################################################################
if ($self->begin("Info - encryption"))
{
# create an unencrypted and an encrypted repo and run a backup in each
#--------------------------------------------------------------------------------------------------------------------------
$self->initStanzaCreate();
$self->{oExpireTest}->backupCreate($self->stanza(), CFGOPTVAL_BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY, -1, -1);
$iLastBackup++;
my $strUnencryptBackup = ($iLastBackup < 10) ? "0$iLastBackup" : $iLastBackup;
$self->initStanzaCreate(STANZA_ENCRYPT, true);
$self->{oExpireTestEncrypt}->backupCreate(
STANZA_ENCRYPT, CFGOPTVAL_BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY, -1, -1);
$iLastBackup++;
my $strEncryptBackup = ($iLastBackup < 10) ? "0$iLastBackup" : $iLastBackup;;
# Clear the main storage repo settings
storageRepoCacheClear(STORAGE_REPO);
# Clear the stanza setting and set the main storage repo basePath to the testing repo path
$self->optionTestClear(CFGOPT_STANZA);
$self->optionTestSet(CFGOPT_REPO_PATH, $self->{strRepoPath});
# Recreate the main storage repo with default unencrypted repo setting and test basePath
storageRepo();
# Test the info command without option --stanza being configured
#--------------------------------------------------------------------------------------------------------------------------
$self->configTestLoad(CFGCMD_INFO);
my $oInfo = new pgBackRest::Info();
$self->testException(sub {$oInfo->stanzaList()}, ERROR_CIPHER,
"unable to parse '" . $self->{strBackupPathEncrypt} . "/backup.info'" .
"\nHINT: Is or was the repo encrypted?" .
"\nHINT: use option --stanza if encryption settings are different for the stanza than the global settings");
# Test the info command with option --stanza configured
#--------------------------------------------------------------------------------------------------------------------------
$self->optionTestSet(CFGOPT_STANZA, $self->stanza());
$self->configTestLoad(CFGCMD_INFO);
$self->testResult(sub {$oInfo->formatText($oInfo->stanzaList($self->stanza()))},
"stanza: db\n status: ok\n\n db (current)\n wal archive min/max (9.4-1): none present\n\n" .
" full backup: 201612" . $strUnencryptBackup . "-155728F\n" .
" timestamp start/stop: 2016-12-" . $strUnencryptBackup . " 15:57:28 / 2016-12-" . $strUnencryptBackup .
" 15:57:28\n" .
" wal start/stop: n/a\n database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n",
"formatText() unencrypted stanza");
$self->optionTestSet(CFGOPT_STANZA, STANZA_ENCRYPT);
$self->configTestLoad(CFGCMD_INFO);
$self->testResult(sub {$oInfo->formatText($oInfo->stanzaList(STANZA_ENCRYPT))},
"stanza: " . STANZA_ENCRYPT ."\n status: ok\n\n db (current)" .
"\n wal archive min/max (9.4-1): none present\n\n" .
" full backup: 201612" . $strEncryptBackup . "-155728F\n" .
" timestamp start/stop: 2016-12-" . $strEncryptBackup . " 15:57:28 / 2016-12-" . $strEncryptBackup .
" 15:57:28\n" .
" wal start/stop: n/a\n database size: 0B, backup size: 0B\n" .
" repository size: 0B, repository backup size: 0B\n",
"formatText() encrypted stanza");
# Change the permissions of the backup file so it cannot be read and confirm correct error reported
#---------------------------------------------------------------------------------------------------------------------------
forceStorageMode(
storageRepo({strStanza => STANZA_ENCRYPT}), $self->{strBackupPathEncrypt} . "/backup.inf*", '220');
$self->testException(sub {$oInfo->backupList(STANZA_ENCRYPT)}, ERROR_FILE_OPEN,
"unable to open '" . $self->{strBackupPathEncrypt} . "/backup.info': Permission denied");
}
}
1;