Viewing File: /usr/local/cpanel/base/3rdparty/roundcube/plugins/carddav/tests/Unit/AdminSettingsWithDataTest.php

<?php

/*
 * RCMCardDAV - CardDAV plugin for Roundcube webmail
 *
 * Copyright (C) 2011-2022 Benjamin Schieder <rcmcarddav@wegwerf.anderdonau.de>,
 *                         Michael Stilkerich <ms@mike2k.de>
 *
 * This file is part of RCMCardDAV.
 *
 * RCMCardDAV is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * RCMCardDAV is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with RCMCardDAV. If not, see <https://www.gnu.org/licenses/>.
 */

declare(strict_types=1);

namespace MStilkerich\Tests\RCMCardDAV\Unit;

use Exception;
use MStilkerich\CardDavClient\{AddressbookCollection,WebDavResource};
use MStilkerich\RCMCardDAV\Frontend\AddressbookManager;
use MStilkerich\Tests\RCMCardDAV\TestInfrastructure;
use PHPUnit\Framework\TestCase;

/**
 * Tests parts of the AdminSettings class using test data in JsonDatabase.
 */
final class AdminSettingsWithDataTest extends TestCase
{
    /** @var JsonDatabase */
    private $db;

    public static function setUpBeforeClass(): void
    {
        $_SESSION['user_id'] = 105;
        $_SESSION['username'] = 'johndoe';
    }

    public function setUp(): void
    {
    }

    public function tearDown(): void
    {
        TestInfrastructure::logger()->reset();
    }

    /**
     * @return array<string, list{string, list{?string,?string, ?string}}>
     */
    public function specialAbookTestProvider(): array
    {
        $base = 'tests/Unit/data/adminSettingsWithDataTest';

        return [
            'Two matchers, one match' => [ "$base/matchBoth.php", ['43', '43', '43'] ],
            'No matches (invalid preset, AND condition eval)' => [
                "$base/noMatch.php",
                [
                    'Setting for collected_recipients must include a valid preset attribute',
                    'Cannot set special addressbook collected_senders, there are 0 candidates (need: 1)',
                    'Cannot set special addressbook default_addressbook, there are 0 candidates (need: 1)'
                ]
            ],
            'Multiple matches' => [
                "$base/matchMultiple.php",
                [
                    'Cannot set special addressbook collected_recipients, there are 2 candidates (need: 1)',
                    'Cannot set special addressbook collected_senders, there are 2 candidates (need: 1)',
                    'Cannot set special addressbook default_addressbook, there are 2 candidates (need: 1)'
                ]
            ],
            'Preset not yet in DB' => [ "$base/presetNotInDbYet.php", [null, null, null] ],
        ];
    }

    /**
     * @param string $admSettingsPath Path name of config.inc.php file
     * @param list{?string,?string,?string} $expIds Expected abook IDs for 0: collected_recipients,
     *                                              1: collected_senders, 2: default_addressbook
     * @dataProvider specialAbookTestProvider
     */
    public function testSpecialAddressbooksReturnedCorrectly(string $admSettingsPath, array $expIds): void
    {
        $this->db = new JsonDatabase(['tests/Unit/data/adminSettingsWithDataTest/db.json']);
        $logger = TestInfrastructure::logger();
        TestInfrastructure::init($this->db, $admSettingsPath);

        $infra = TestInfrastructure::$infra;
        $admPrefs = $infra->admPrefs();
        $abMgr = new AddressbookManager();

        $specialAbooks = $admPrefs->getSpecialAddressbooks($abMgr, $infra);

        $i = 0;
        foreach (['collected_recipients', 'collected_senders', 'default_addressbook'] as $abookType) {
            if (isset($expIds[$i])) {
                if (strpos($expIds[$i], ' ') === false) {
                    $this->assertArrayHasKey($abookType, $specialAbooks);
                    $this->assertSame($specialAbooks[$abookType], $expIds[$i]);
                } else {
                    $this->assertArrayNotHasKey($abookType, $specialAbooks);
                    $logger->expectMessage("error", $expIds[$i]);
                }
            } else {
                $this->assertArrayNotHasKey($abookType, $specialAbooks);
            }

            $i++;
        }
    }

    /**
     * Test for AdminSettings::getAddressbookTemplate()
     *
     * Situations to test:
     * - DB template addressbook has different value for a fixed setting than the preset - preset value is taken
     *    - account 42, refresh_time
     * - DB template addressbook has different value for a non-fixed setting than the preset - DB value is taken
     *    - account 42, name
     * - Template addressbook for a non-preset account is queried and DB entry exists - DB entry is provided
     *    - account 43
     * - Template addressbook for a non-preset account is queried and no DB entry exists - empty array returned
     *    - account 44
     * - Template addressbook for a preset account is queried and no DB entry exists - values from preset provided
     *    - account 45
     */
    public function testTemplateAddressbookProvidedCorrectly(): void
    {
        $this->db = new JsonDatabase(['tests/Unit/data/adminSettingsWithDataTest/templateAbookDb.json']);
        TestInfrastructure::init($this->db, 'tests/Unit/data/adminSettingsWithDataTest/templateAbook.php');

        $infra = TestInfrastructure::$infra;
        $admPrefs = $infra->admPrefs();
        $abMgr = new AddressbookManager();

        //  Test fixed vs. non-fixed values and user overrides
        $tmpl = $admPrefs->getAddressbookTemplate($abMgr, '42');

        $this->assertSame('1800', $tmpl['refresh_time'] ?? '');
        $this->assertSame('New abook (%N)', $tmpl['name'] ?? '');
        $this->assertSame('0', $tmpl['active'] ?? '');
        $this->assertSame('1', $tmpl['use_categories'] ?? '');
        $this->assertSame('0', $tmpl['discovered'] ?? '');
        $this->assertSame('1', $tmpl['readonly'] ?? '');
        $this->assertSame('1', $tmpl['require_always_email'] ?? '');


        // Test user-defined account with template addressbook in DB
        $tmpl = $admPrefs->getAddressbookTemplate($abMgr, '43');

        $this->assertSame('60', $tmpl['refresh_time'] ?? '');
        $this->assertSame('%D', $tmpl['name'] ?? '');
        $this->assertSame('0', $tmpl['active'] ?? '');
        $this->assertSame('1', $tmpl['use_categories'] ?? '');
        $this->assertSame('0', $tmpl['discovered'] ?? '');
        $this->assertSame('0', $tmpl['readonly'] ?? '');
        $this->assertSame('0', $tmpl['require_always_email'] ?? '');

        // Test user-defined account with NO template addressbook in DB
        $tmpl = $admPrefs->getAddressbookTemplate($abMgr, '44');
        $this->assertCount(0, $tmpl);

        // Test preset account with NO template addressbook in DB
        // The addressbook template must not contain any account fields, only addressbook attributes
        $tmpl = $admPrefs->getAddressbookTemplate($abMgr, '45');

        $this->assertArrayNotHasKey('accountname', $tmpl);
        $this->assertArrayNotHasKey('username', $tmpl);
        $this->assertArrayNotHasKey('password', $tmpl);
        $this->assertArrayNotHasKey('discovery_url', $tmpl);
        $this->assertArrayNotHasKey('rediscover_time', $tmpl);
        $this->assertArrayNotHasKey('hide', $tmpl);
        $this->assertArrayNotHasKey('fixed', $tmpl);
        $this->assertArrayNotHasKey('extra_addressbooks', $tmpl);

        $this->assertSame('%N - %D', $tmpl['name'] ?? '');
        $this->assertSame('1', $tmpl['active'] ?? '');
        // readonly is not part of preset
        $this->assertArrayNotHasKey('readonly', $tmpl);
        $this->assertSame('600', $tmpl['refresh_time'] ?? '');
        $this->assertSame('0', $tmpl['use_categories'] ?? '');
        // discovered is not part of preset
        $this->assertArrayNotHasKey('discovered', $tmpl);
        $this->assertSame('0', $tmpl['require_always_email'] ?? '');
        // template is not part of preset
        $this->assertArrayNotHasKey('template', $tmpl);
    }

    /**
     * Tests that presets are initialized and updated properly by AdminSettings::initPresets().
     *
     * The following is tested:
     * - A new preset is added
     *   - Account is added to the DB with the proper settings
     *   - Extra addressbooks of the account are added to the DB, but no sync is attempted
     * - A preset account existing in the DB is removed - RemovedPreset
     *   - The account and all its addressbooks are purged from the database.
     * - A preset account existing in the DB is updated - UpdatedPreset
     *   - A new extra addressbook is added [NewXBook]
     *   - An extra addressbook existing in the DB is removed [RemovedBook]
     *   - Fixed settings for the account are updated [preemptive_basic_auth, ssl_noverify]
     *   - Fixed settings for existing addressbooks are updated [username, refresh_time, require_always_email]
     *     - For a fixed name w/ server-side vars, the updated value is fetched from the server [Discovered Addressbook]
     *     - For an extra addressbook, its specific settings are taken if available [UpdatedXBook, require_always_email]
     *     - For an extra addressbook, main settings are taken if no specific  available [UpdatedXBook, refresh_time]
     *   - An extra addressbook with invalid URL exists in the admin config, error is logged and remainder of the
     *     preset is properly processed [InvalidXBook]
     *   - A discovered addressbook does not exist on server anymore and server-side query is required to update name;
     *     it is not updated in the DB except for the name, and the rest of the preset is properly processed [book55]
     *   - Non-fixed settings with values different from the preset are retained [rediscover_time, use_categories]
     *
     * Note that the discovery of addressbooks belonging to a preset is out of scope of the AdminSettings::initPresets()
     * function and therefore this test.
     */
    public function testPresetsAreInitializedProperly(): void
    {
        $db = new JsonDatabase(['tests/Unit/data/adminSettingsWithDataTest/initPresetsDb.json']);
        $this->db = $db;
        $dbAfter  = new JsonDatabase(['tests/Unit/data/adminSettingsWithDataTest/initPresetsDbAfter.json']);
        $logger = TestInfrastructure::logger();
        TestInfrastructure::init($this->db, 'tests/Unit/data/adminSettingsWithDataTest/initPresets.php');

        $infra = TestInfrastructure::$infra;
        $invalidXBookUrl = 'https://carddav.example.com/global/InvalidXBook/';
        $infra->webDavResources = [
            'https://carddav.example.com/books/johndoe/book42/' => $this->makeAbookCollStub(
                'Book 42',
                'https://carddav.example.com/books/johndoe/book42/',
                "Hitchhiker's Guide"
            ),
            'https://carddav.example.com/global/UpdatedXBook/' => $this->makeAbookCollStub(
                'Updated XAbook',
                'https://carddav.example.com/global/UpdatedXBook/',
                'Public directory'
            ),
            'https://carddav.example.com/global/NewXBook/' => $this->makeAbookCollStub(
                'Added XAbook',
                'https://carddav.example.com/global/NewXBook/',
                'Newly added extra addressbook'
            ),
            'https://carddav.example.com/global/RemovedBook' => new Exception('RemovedBook not on server anymore'),
            $invalidXBookUrl => new Exception('InvalidXBook'),
            "https://carddav.example.com/books/johndoe/book55/" => $this->createStub(WebDavResource::class),

            'https://newcard.example.com/global/PublicAddrs' => $this->makeAbookCollStub(
                'New public addrs',
                'https://newcard.example.com/global/PublicAddrs',
                'New Public directory'
            ),

        ];
        $admPrefs = $infra->admPrefs();
        $abMgr = new AddressbookManager();

        $admPrefs->initPresets($abMgr, $infra);

        $dbAfter->compareTables('accounts', $db);
        $dbAfter->compareTables('addressbooks', $db);

        $logger->expectMessage(
            "error",
            "Failed to add extra addressbook $invalidXBookUrl for preset UpdatedPreset: InvalidXBook"
        );
        $logger->expectMessage("error", "Cannot update name of addressbook 55: no addressbook collection at given URL");
    }

    /**
     * Creates an AddressbookCollection stub that implements getUri() and getName().
     */
    private function makeAbookCollStub(?string $name, string $url, ?string $desc): AddressbookCollection
    {
        $davobj = $this->createStub(AddressbookCollection::class);
        $urlComp = explode('/', rtrim($url, '/'));
        $baseName = $urlComp[count($urlComp) - 1];
        $davobj->method('getName')->will($this->returnValue($name ?? $baseName));
        $davobj->method('getBasename')->will($this->returnValue($baseName));
        $davobj->method('getDisplayname')->will($this->returnValue($name));
        $davobj->method('getDescription')->will($this->returnValue($desc));
        $davobj->method('getUri')->will($this->returnValue($url));
        return $davobj;
    }
}

// vim: ts=4:sw=4:expandtab:fenc=utf8:ff=unix:tw=120
Back to Directory File Manager