, * Michael Stilkerich * * 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 . */ 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 */ public static 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')->willReturn($name ?? $baseName); $davobj->method('getBasename')->willReturn($baseName); $davobj->method('getDisplayname')->willReturn($name); $davobj->method('getDescription')->willReturn($desc); $davobj->method('getUri')->willReturn($url); return $davobj; } } // vim: ts=4:sw=4:expandtab:fenc=utf8:ff=unix:tw=120