####################################################################################################################################
# ConfigOptionTest.pm - Tests for command line options in Config.pm
####################################################################################################################################
package pgBackRestTest::Module::Config::ConfigOptionTest;
use parent 'pgBackRestTest::Env::ConfigEnvTest';

####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);

use Cwd qw(abs_path);

use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;

use pgBackRestTest::Common::RunTest;

####################################################################################################################################
# run
####################################################################################################################################
sub run
{
    my $self = shift;

    my @oyArray;

    if ($self->begin('backup with no stanza'))
    {
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_REQUIRED, cfgOptionName(CFGOPT_STANZA));
    }

    if ($self->begin('backup with boolean stanza'))
    {
        $self->optionTestSetBool(CFGOPT_STANZA);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_COMMAND_REQUIRED);
    }

    if ($self->begin('backup type defaults to ' . CFGOPTVAL_BACKUP_TYPE_INCR))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_TYPE, CFGOPTVAL_BACKUP_TYPE_INCR);
    }

    if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' invalid option ' . cfgOptionName(CFGOPT_ARCHIVE_ASYNC)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSetBool(CFGOPT_ARCHIVE_ASYNC);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_COMMAND, cfgOptionName(CFGOPT_ARCHIVE_ASYNC),
            cfgCommandName(CFGCMD_BACKUP));
    }

    if ($self->begin('backup type set to ' . CFGOPTVAL_BACKUP_TYPE_FULL))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_TYPE, CFGOPTVAL_BACKUP_TYPE_FULL);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_TYPE, CFGOPTVAL_BACKUP_TYPE_FULL);
    }

    if ($self->begin('backup type invalid'))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_TYPE, BOGUS);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_TYPE));
    }

    if ($self->begin('backup invalid force'))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSetBool(CFGOPT_FORCE);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID, cfgOptionName(CFGOPT_FORCE), 'no-' . cfgOptionName(CFGOPT_ONLINE));
    }

    if ($self->begin('backup valid force'))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSetBool(CFGOPT_ONLINE, false);
        $self->optionTestSetBool(CFGOPT_FORCE);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_ONLINE, false);
        $self->optionTestExpect(CFGOPT_FORCE, true);
    }

    if ($self->begin('backup invalid value for ' . cfgOptionName(CFGOPT_TEST_DELAY)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSetBool(CFGOPT_TEST);
        $self->optionTestSet(CFGOPT_TEST_DELAY, BOGUS);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_TEST_DELAY));
    }

    if ($self->begin('backup invalid ' . cfgOptionName(CFGOPT_TEST_DELAY)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_TEST_DELAY, 5);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID, cfgOptionName(CFGOPT_TEST_DELAY), cfgOptionName(CFGOPT_TEST));
    }

    if ($self->begin('backup check ' . cfgOptionName(CFGOPT_TEST_DELAY) . ' undef'))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_TEST_DELAY);
    }

    if ($self->begin('restore invalid ' . cfgOptionName(CFGOPT_TARGET)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_TYPE, cfgRuleOptionDefault(CFGCMD_RESTORE, CFGOPT_TYPE));
        $self->optionTestSet(CFGOPT_TARGET, BOGUS);

        @oyArray = (CFGOPTVAL_RESTORE_TYPE_NAME, CFGOPTVAL_RESTORE_TYPE_TIME, CFGOPTVAL_RESTORE_TYPE_XID);
        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID, cfgOptionName(CFGOPT_TARGET),
            cfgOptionName(CFGOPT_TYPE), \@oyArray);
    }

    if ($self->begin('restore ' . cfgOptionName(CFGOPT_TARGET)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_NAME);
        $self->optionTestSet(CFGOPT_TARGET, BOGUS);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE));
        $self->optionTestExpect(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_NAME);
        $self->optionTestExpect(CFGOPT_TARGET, BOGUS);
        $self->optionTestExpect(CFGOPT_TARGET_TIMELINE);
    }

    if ($self->begin('invalid string ' . cfgOptionName(CFGOPT_PROCESS_MAX)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_PROCESS_MAX, BOGUS);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_PROCESS_MAX));
    }

    if ($self->begin('invalid float ' . cfgOptionName(CFGOPT_PROCESS_MAX)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_PROCESS_MAX, '0.0');

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, '0.0', cfgOptionName(CFGOPT_PROCESS_MAX));
    }

    if ($self->begin('valid ' . cfgOptionName(CFGOPT_PROCESS_MAX)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_PROCESS_MAX, '2');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
    }

    if ($self->begin('valid thread-max'))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSetByName('thread-max', '2');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_PROCESS_MAX, 2);
    }

    if ($self->begin('valid float ' . cfgOptionName(CFGOPT_TEST_DELAY)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSetBool(CFGOPT_TEST);
        $self->optionTestSet(CFGOPT_TEST_DELAY, '0.25');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
    }

    if ($self->begin('valid int ' . cfgOptionName(CFGOPT_TEST_DELAY)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSetBool(CFGOPT_TEST);
        $self->optionTestSet(CFGOPT_TEST_DELAY, 3);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
    }

    if ($self->begin('restore valid ' . cfgOptionName(CFGOPT_TARGET_TIMELINE)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_TARGET_TIMELINE, 2);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE));
    }

    if ($self->begin('invalid ' . cfgOptionName(CFGOPT_COMPRESS_LEVEL)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_COMPRESS_LEVEL, '12');

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_RANGE, '12', cfgOptionName(CFGOPT_COMPRESS_LEVEL));
    }

    if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' invalid value ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RETENTION_ARCHIVE_TYPE, BOGUS);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE));
    }

    if ($self->begin(
        cfgCommandName(CFGCMD_BACKUP) . ' default value ' . cfgRuleOptionDefault(CFGCMD_BACKUP, CFGOPT_RETENTION_ARCHIVE_TYPE) .
        ' for ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE) . ' with valid ' . cfgOptionName(CFGOPT_RETENTION_FULL)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RETENTION_FULL, 1);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE, 1);
        $self->optionTestExpect(CFGOPT_RETENTION_FULL, 1);
        $self->optionTestExpect(CFGOPT_RETENTION_DIFF, undef);
        # Default is FULL so this test will fail if the default is changed, alerting to the need to update configLoad.
        $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_FULL);
    }

    if ($self->begin(
        cfgCommandName(CFGCMD_BACKUP) . ' valid value ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE) . ' for ' .
        cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE) . ' ' . CFGOPTVAL_BACKUP_TYPE_DIFF))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RETENTION_DIFF, 1);
        $self->optionTestSet(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_DIFF);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE, 1);
        $self->optionTestExpect(CFGOPT_RETENTION_DIFF, 1);
        $self->optionTestExpect(CFGOPT_RETENTION_FULL, undef);
        $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_DIFF);
    }

    if ($self->begin(
        cfgCommandName(CFGCMD_BACKUP) . ' warn no valid value ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE) . ' for ' .
        cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE) . ' ' . CFGOPTVAL_BACKUP_TYPE_INCR))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RETENTION_FULL, 2);
        $self->optionTestSet(CFGOPT_RETENTION_DIFF, 1);
        $self->optionTestSet(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_INCR);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE, undef);
        $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_INCR);
    }

    if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' invalid value ' . cfgOptionName(CFGOPT_PROTOCOL_TIMEOUT)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_DB_TIMEOUT, 5);
        $self->optionTestSet(CFGOPT_PROTOCOL_TIMEOUT, 4);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, 4, cfgOptionName(CFGOPT_PROTOCOL_TIMEOUT),
            "'protocol-timeout' option should be greater than 'db-timeout' option");
    }

    if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' invalid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RECOVERY_OPTION, '=');

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_PAIR, '=', cfgOptionName(CFGOPT_RECOVERY_OPTION));
    }

    if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' invalid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RECOVERY_OPTION, '=' . BOGUS);

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_PAIR, '=' . BOGUS, cfgOptionName(CFGOPT_RECOVERY_OPTION));
    }

    if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' invalid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RECOVERY_OPTION, BOGUS . '=');

        $self->configTestLoadExpect(
            cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_PAIR, BOGUS . '=', cfgOptionName(CFGOPT_RECOVERY_OPTION));
    }

    if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' valid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_RECOVERY_OPTION, 'primary-conn-info=db.domain.net');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE));
        $self->optionTestExpect(&CFGOPT_RECOVERY_OPTION, 'db.domain.net', 'primary-conn-info');
    }

    if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' values passed to ' . cfgCommandName(CFGCMD_ARCHIVE_GET)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db path/main');
        $self->optionTestSet(CFGOPT_REPO_PATH, '/repo');
        $self->optionTestSet(CFGOPT_BACKUP_HOST, 'db.mydomain.com');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE));

        my $strCommand = cfgCommandWrite(CFGCMD_ARCHIVE_GET);
        my $strExpectedCommand = abs_path($0) . " --backup-host=db.mydomain.com \"--db1-path=/db path/main\"" .
                                 " --repo-path=/repo --stanza=app " . cfgCommandName(CFGCMD_ARCHIVE_GET);

        if ($strCommand ne $strExpectedCommand)
        {
            confess "expected command '${strExpectedCommand}' but got '${strCommand}'";
        }
    }

    if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' default value ' . cfgOptionName(CFGOPT_DB_CMD)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_HOST, 'db');
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_DB_CMD, abs_path($0));
    }

    if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' missing option ' . cfgOptionName(CFGOPT_DB_PATH)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_REQUIRED, 'db1-path', 'does this stanza exist?');
    }

    if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' automatically increase ' . cfgOptionName(CFGOPT_PROTOCOL_TIMEOUT)))
    {
        $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
        $self->optionTestSet(CFGOPT_DB_PATH, '/db');
        $self->optionTestSet(CFGOPT_DB_TIMEOUT, cfgRuleOptionDefault(CFGCMD_BACKUP, CFGOPT_PROTOCOL_TIMEOUT) + 30);

        $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
        $self->optionTestExpect(CFGOPT_PROTOCOL_TIMEOUT, cfgRuleOptionDefault(CFGCMD_BACKUP, CFGOPT_PROTOCOL_TIMEOUT) + 60);
    }
}

####################################################################################################################################
# Getters
####################################################################################################################################
# Change this from the default so the same stanza is not used in all tests.
sub stanza {return 'app'};

1;
