#!/usr/local/cpanel/3rdparty/bin/perl -w

# cpanel - t/medium/Cpanel-Koality-Auth.t          Copyright 2023 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

package t::medium::Cpanel::Koality::Auth;

use cPstrict;
use Test::Cpanel::Policy;

use Test2::V0;
use Test2::Plugin::NoWarnings;
use Test2::Tools::Exception;

use Test::MockModule          ();
use Test::MockObject::Extends ();
use Test::MockFile            ();

use Cpanel::Koality::Auth ();

use base qw(Test::Class);

my $mock_stage_file = Test::MockFile->file('/var/cpanel/use_koality_stage');

my $mock_privs = Test::MockModule->new('Cpanel::AccessIds::ReducedPrivileges');
$mock_privs->redefine( 'new' => sub { return 1 } );

my $mock_validate = Test::MockModule->new('Cpanel::Koality::Validate');
$mock_validate->redefine( 'valid_api_object' => sub { return 1 } );

my $mock_username = Test::MockModule->new('Cpanel::Validate::Username');
$mock_username->redefine( 'user_exists_or_die' => sub { return 1 } );

my $mock_serverid = Test::MockModule->new('Cpanel::Plugins::ServerId');
$mock_serverid->redefine( 'get_server_id' => sub { return '101010' } );

my $mock_user = Test::MockModule->new('Cpanel::Koality::User');
$mock_user->redefine( 'save_user_info'     => sub { return 1 } );
$mock_user->redefine( 'disable_onboarding' => sub { return 1 } );
$mock_user->redefine( '_build__conf_dir'   => sub { return 1 } );
$mock_user->redefine( '_build__conf_file'  => sub { return 1 } );
$mock_user->redefine( '_ensure_conf_dir'   => sub { return 1 } );

my $mock_companyid = Test::MockModule->new('Cpanel::License::CompanyID');
$mock_companyid->redefine( 'get_company_id' => sub { return '42' } );

my $expected_user_config = {};
$mock_user->redefine( '_build_user_config' => sub { return $expected_user_config } );

my $mock_locale = Test::MockModule->new('Cpanel::Locale::Utils::User');
$mock_locale->redefine( 'get_user_locale' => sub { return 'en' } );

my $mock_uuid = Test::MockModule->new('Cpanel::Plugins::UUID');
$mock_uuid->redefine('new' => sub { return bless { uuid => '7bde24ae-d42d-4d50-bd85-9e3d733424c1' }, shift });

my $mock_uuid_val = Test::MockModule->new('Cpanel::Validate::UUID');
$mock_uuid_val->redefine('validate_uuid_or_die' => 1);


__PACKAGE__->new()->runtests() if !caller;

sub test_constructor : Tests(2) {

    my $tests = {
        auth_url   => 'notgood.com',
        app360_url => 'alsonotgood.com'
    };

    foreach my $test ( sort keys $tests->%* ) {
        ok( dies { Cpanel::Koality::User->new( $test => $tests->{$test} ) }, "new: Dies with invalid $test" );
    }
}

sub create_user : Tests(4) {

    my $expected_user = 'cpanel_user';
    local $Cpanel::user = $expected_user;

    my $api = Cpanel::Koality::ApiClient->new( 'http_client' => sub { } );
    $api = Test::MockObject::Extends->new($api);

    my $app_token_return;
    my $auth_mock = Test::MockModule->new('Cpanel::Koality::Auth');
    $auth_mock->redefine( '_get_app_token' => sub { return $app_token_return } );

    my $expected_api_response = {};
    $api->mock( 'run'         => sub { return $expected_api_response } );
    $api->mock( 'http_client' => sub { return 1 } );

    my $auth = Cpanel::Koality::Auth->new( api => $api );
    $expected_api_response = { token => 'abc123' };

    my $expected_email = 'user@domain.com';
    my $user_obj       = $auth->create_user($expected_email);

    is( $user_obj->{app_token},        $expected_api_response->{token}, "create_user: Got the expected app_token." );
    is( $user_obj->{cpanel_username},  $expected_user,                  "create_user: Got the expected cpanel username." );
    is( $user_obj->{koality_username}, $expected_email,                 "create_user: Got the expected koality username." );

    $app_token_return = 1;
    ok( dies { $auth->create_user($expected_email) }, "create_user: Dies when an app_token already exists." );

    return;
}

sub auth_session : Tests(9) {

    my $expected_user = 'cpanel_user';
    local $Cpanel::user = $expected_user;

    my $api = Cpanel::Koality::ApiClient->new( 'http_client' => sub { } );
    $api = Test::MockObject::Extends->new($api);

    my $expected_api_response = {};
    $api->mock( 'run'         => sub { return $expected_api_response } );
    $api->mock( 'http_client' => sub { return 1 } );

    my $auth = Cpanel::Koality::Auth->new( api => $api );

    ok( dies { $auth->auth_session() }, "auth_session: Dies when an app token does not exist." );

    $expected_user_config  = { app_token => 'app_token123' };
    $expected_api_response = {
        data => {
            token         => 'session_token123',
            refresh_token => 'refresh_token123',
            user          => {
                email    => 'mail@domain.com',
                id       => '9999',
                enabled  => '1',
                userName => 'koality_username',

            },
            companies => [
                {
                    cluster => { apiEndpoint => 'https://mycluster1.com/api/' },
                }
            ],
        }
    };

    my $user_obj = $auth->auth_session();

    is( $user_obj->{app_token},        $expected_user_config->{app_token},                                         "auth_session: Got the expected app_token." );
    is( $user_obj->{session_token},    $expected_api_response->{data}{token},                                      "auth_session: Got the expected session_token." );
    is( $user_obj->{refresh_token},    $expected_api_response->{data}{refresh_token},                              "auth_session: Got the expected session_token." );
    is( $user_obj->{cpanel_username},  $expected_user,                                                             "auth_session: Got the expected cPanel username." );
    is( $user_obj->{user_id},          $expected_api_response->{data}{user}{id},                                   "auth_session: Got the expected user id." );
    is( $user_obj->{enabled},          $expected_api_response->{data}{user}{enabled},                              "auth_session: Got the expected value for if the user is enabled." );
    is( $user_obj->{cluster_endpoint}, $expected_api_response->{data}{companies}[0]{cluster}{apiEndpoint} . 'v1/', "auth_session: Got the expected api endpoint." );
    is( $user_obj->{koality_username}, $expected_api_response->{data}{user}{email},                                "auth_session: Got the expected koality username." );

    return;
}

sub test_delete_user : Tests(4) {

    my $expected_user = 'cpanel_user';
    local $Cpanel::user = $expected_user;

    my $api = Cpanel::Koality::ApiClient->new( 'http_client' => sub { } );
    $api = Test::MockObject::Extends->new($api);

    my $conf_dir  = Test::MockFile->dir('/mock_conf_dir');
    my $conf_file = Test::MockFile->file('/mock_conf_dir/config');
    $conf_file->contents('123');

    my $user_mock = Test::MockModule->new('Cpanel::Koality::User');
    $user_mock->redefine( reset_config => sub { return } );
    $user_mock->redefine( _conf_file   => sub { return '/mock_conf_dir/config' } );

    my $auth_mock = Test::MockModule->new('Cpanel::Koality::Auth');
    $auth_mock->redefine(
        get_user => sub {
            return bless { cluster_endpoint => 'https://api.com/', master_user_id => 0, auth_token => 'abc123', }, 'Cpanel::Koality::User';
        }
    );

    my $expected_api_response;
    $api->mock( 'run'         => sub { return $expected_api_response } );
    $api->mock( 'http_client' => sub { return 1 } );

    my $auth = Cpanel::Koality::Auth->new( api => $api, );

    $expected_api_response = { status => 'error' };
    ok( !$auth->delete_user(), "delete_user: When the deletion fails, return false." );

    $expected_api_response = { status => 'success' };
    ok( $auth->delete_user(), "delete_user: When the deletion succeeds, return true." );

    $auth_mock->redefine(
        get_user => sub {
            return bless { cluster_endpoint => undef, master_user_id => undef, auth_token => undef }, 'Cpanel::Koality::User';
        }
    );

    ok( !$auth->delete_user(), "delete_user: When the conf file exists but is empty, return false." );

    undef $conf_file;
    ok( dies { $auth->delete_user }, "delete_user: Dies when no config exists." );

    return;
}

sub test_verify_code : Tests(2) {

    my $expected_user = 'cpanel_user';
    local $Cpanel::user = $expected_user;

    my $api = Cpanel::Koality::ApiClient->new( 'http_client' => sub { } );
    $api = Test::MockObject::Extends->new($api);

    my $auth_mock = Test::MockModule->new('Cpanel::Koality::Auth');
    $auth_mock->redefine(
        get_user => sub {
            return bless { cluster_endpoint => 'https://api.com/', master_user_id => 0 }, 'Cpanel::Koality::User';
        }
    );

    my $expected_api_response;

    $api->mock( 'run'         => sub { return $expected_api_response } );
    $api->mock( 'http_client' => sub { return 1 } );

    my $auth = Cpanel::Koality::Auth->new( api => $api, );

    $expected_api_response = { status => 'error' };
    ok( !$auth->verify_code('123456'), "verify_code: When the code fails to verify, return false." );

    $expected_api_response = { status => 'success' };
    ok( $auth->verify_code('123456'), "verify_code: When the code successfully verifies, return true." );

    return;
}

sub test_send_activation_email : Tests(2) {

    my $expected_user = 'cpanel_user';
    local $Cpanel::user = $expected_user;

    my $api = Cpanel::Koality::ApiClient->new( 'http_client' => sub { } );
    $api = Test::MockObject::Extends->new($api);

    my $auth_mock = Test::MockModule->new('Cpanel::Koality::Auth');
    $auth_mock->redefine(
        get_user => sub {
            return bless { auth_url => 'https://api.com/v1/ok/', master_user_id => 1234 }, 'Cpanel::Koality::User';
        }
    );

    my $expected_api_response;

    $api->mock( 'run'         => sub { return $expected_api_response } );
    $api->mock( 'http_client' => sub { return 1 } );

    my $auth = Cpanel::Koality::Auth->new( api => $api, );

    $expected_api_response = { status => 'error' };
    ok( !$auth->send_activation_email(), "send_activation_email: When the request for the email fails, return false." );

    $expected_api_response = { status => 'success' };
    ok( $auth->send_activation_email(), "verify_code: When the request for the email is successful, return true." );

    return;
}

1;
