xref: /webtrees/tests/app/TreeTest.php (revision 5a8afed46297e8105e3e5a33ce37e6a8e88bc79d)
1a25f0a04SGreg Roach<?php
23976b470SGreg Roach
3a25f0a04SGreg Roach/**
4a25f0a04SGreg Roach * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
6a25f0a04SGreg Roach * This program is free software: you can redistribute it and/or modify
7a25f0a04SGreg Roach * it under the terms of the GNU General Public License as published by
8a25f0a04SGreg Roach * the Free Software Foundation, either version 3 of the License, or
9a25f0a04SGreg Roach * (at your option) any later version.
10a25f0a04SGreg Roach * This program is distributed in the hope that it will be useful,
11a25f0a04SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12a25f0a04SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13a25f0a04SGreg Roach * GNU General Public License for more details.
14a25f0a04SGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
16a25f0a04SGreg Roach */
17fcfa147eSGreg Roach
18e7f56f2aSGreg Roachdeclare(strict_types=1);
19e7f56f2aSGreg Roach
2084e2cf4eSGreg Roachnamespace Fisharebest\Webtrees;
21a25f0a04SGreg Roach
2269675509SGreg Roachuse Fisharebest\Webtrees\Contracts\CacheFactoryInterface;
231fe542e9SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface;
2469c05a6eSGreg Roachuse Fisharebest\Webtrees\Services\GedcomExportService;
252c685d76SGreg Roachuse Fisharebest\Webtrees\Services\GedcomImportService;
265afbc57aSGreg Roachuse Fisharebest\Webtrees\Services\TreeService;
27e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Services\UserService;
283cfcc809SGreg Roachuse InvalidArgumentException;
2916ecfcafSGreg Roachuse Nyholm\Psr7\Factory\Psr17Factory;
30*202c018bSGreg Roachuse PHPUnit\Framework\Attributes\CoversClass;
3169675509SGreg Roachuse Symfony\Component\Cache\Adapter\NullAdapter;
323976b470SGreg Roach
33ea517a3bSGreg Roachuse function fclose;
34ea517a3bSGreg Roachuse function file_get_contents;
35ea517a3bSGreg Roachuse function preg_replace;
3694026f20SGreg Roachuse function stream_get_contents;
3794026f20SGreg Roach
38*202c018bSGreg Roach#[CoversClass(Tree::class)]
39*202c018bSGreg Roach#[CoversClass(TreeService::class)]
40*202c018bSGreg Roach#[CoversClass(GedcomExportService::class)]
41e5a6b4d4SGreg Roachclass TreeTest extends TestCase
42c1010edaSGreg Roach{
43cd94ca66SGreg Roach    protected static bool $uses_database = true;
44126654d7SGreg Roach
4592a78a2fSGreg Roach    /**
4692a78a2fSGreg Roach     * Things to run before every test.
4792a78a2fSGreg Roach     */
4857b69958SGreg Roach    protected function setUp(): void
4969675509SGreg Roach    {
5069675509SGreg Roach        parent::setUp();
5169675509SGreg Roach
52cd94ca66SGreg Roach        $cache_factory = $this->createMock(CacheFactoryInterface::class);
5369675509SGreg Roach        $cache_factory->method('array')->willReturn(new Cache(new NullAdapter()));
546b9cb339SGreg Roach        Registry::cache($cache_factory);
5569675509SGreg Roach    }
5669675509SGreg Roach
57126654d7SGreg Roach    public function testConstructor(): void
58c1010edaSGreg Roach    {
592c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
602c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
611e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
62126654d7SGreg Roach
635e933c21SGreg Roach        self::assertSame('name', $tree->name());
645e933c21SGreg Roach        self::assertSame('title', $tree->title());
65126654d7SGreg Roach    }
66126654d7SGreg Roach
67af343084SGreg Roach    public function testTreePreferences(): void
68126654d7SGreg Roach    {
692c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
702c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
711e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
72126654d7SGreg Roach
73126654d7SGreg Roach        $tree->setPreference('foo', 'bar');
748a07c98eSGreg Roach        $pref = $tree->getPreference('foo');
755e933c21SGreg Roach        self::assertSame('bar', $pref);
76a25f0a04SGreg Roach    }
77af343084SGreg Roach
78af343084SGreg Roach    public function testUserTreePreferences(): void
79af343084SGreg Roach    {
80e5a6b4d4SGreg Roach        $user_service          = new UserService();
812c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
822c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
831e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
84e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
85af343084SGreg Roach
86af343084SGreg Roach        $pref = $tree->getUserPreference($user, 'foo', 'default');
875e933c21SGreg Roach        self::assertSame('default', $pref);
88af343084SGreg Roach
89af343084SGreg Roach        $tree->setUserPreference($user, 'foo', 'bar');
90af343084SGreg Roach        $pref = $tree->getUserPreference($user, 'foo', 'default');
915e933c21SGreg Roach        self::assertSame('bar', $pref);
927d76853dSGreg Roach    }
937d76853dSGreg Roach
947d76853dSGreg Roach    public function testCreateInvalidIndividual(): void
957d76853dSGreg Roach    {
963cfcc809SGreg Roach        $this->expectException(InvalidArgumentException::class);
973cfcc809SGreg Roach
98e5a6b4d4SGreg Roach        $user_service          = new UserService();
992c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
1002c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
1011e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
102e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
1031fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
1047d76853dSGreg Roach        Auth::login($user);
1057d76853dSGreg Roach
1067d76853dSGreg Roach        $tree->createIndividual("0 @@ FOO\n1 SEX U");
1077d76853dSGreg Roach    }
1087d76853dSGreg Roach
109963fbaeeSGreg Roach    public function testCreateIndividual(): void
110963fbaeeSGreg Roach    {
111e5a6b4d4SGreg Roach        $user_service          = new UserService();
1122c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
1132c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
1141e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
115e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
1161fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
117963fbaeeSGreg Roach        Auth::login($user);
118963fbaeeSGreg Roach
1198f9b0fb2SGreg Roach        $record = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
1205e933c21SGreg Roach        self::assertTrue($record->isPendingAddition());
121963fbaeeSGreg Roach
1221fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
1238f9b0fb2SGreg Roach        $record = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
1245e933c21SGreg Roach        self::assertFalse($record->isPendingAddition());
1258f9b0fb2SGreg Roach    }
126963fbaeeSGreg Roach
1277d76853dSGreg Roach    public function testCreateInvalidFamily(): void
1288f9b0fb2SGreg Roach    {
1293cfcc809SGreg Roach        $this->expectException(InvalidArgumentException::class);
1303cfcc809SGreg Roach
131e5a6b4d4SGreg Roach        $user_service          = new UserService();
1322c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
1332c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
1341e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
135e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
1361fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
1377d76853dSGreg Roach        Auth::login($user);
138963fbaeeSGreg Roach
1397d76853dSGreg Roach        $tree->createFamily("0 @@ FOO\n1 MARR Y");
1408f9b0fb2SGreg Roach    }
141963fbaeeSGreg Roach
1428f9b0fb2SGreg Roach    public function testCreateFamily(): void
1438f9b0fb2SGreg Roach    {
144e5a6b4d4SGreg Roach        $user_service          = new UserService();
1452c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
1462c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
1471e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
148e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
1491fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
1508f9b0fb2SGreg Roach        Auth::login($user);
1518f9b0fb2SGreg Roach
1528f9b0fb2SGreg Roach        $record = $tree->createFamily("0 @@ FAM\n1 MARR Y");
1535e933c21SGreg Roach        self::assertTrue($record->isPendingAddition());
1548f9b0fb2SGreg Roach
1551fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
1568f9b0fb2SGreg Roach        $record = $tree->createFamily("0 @@ FAM\n1 MARR Y");
1575e933c21SGreg Roach        self::assertFalse($record->isPendingAddition());
1588f9b0fb2SGreg Roach    }
1598f9b0fb2SGreg Roach
1607d76853dSGreg Roach    public function testCreateInvalidMediaObject(): void
1617d76853dSGreg Roach    {
1623cfcc809SGreg Roach        $this->expectException(InvalidArgumentException::class);
1633cfcc809SGreg Roach
164e5a6b4d4SGreg Roach        $user_service          = new UserService();
1652c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
1662c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
1671e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
168e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
1691fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
1707d76853dSGreg Roach        Auth::login($user);
1717d76853dSGreg Roach
1727d76853dSGreg Roach        $tree->createMediaObject("0 @@ FOO\n1 MARR Y");
1737d76853dSGreg Roach    }
1747d76853dSGreg Roach
1757d76853dSGreg Roach    public function testCreateMediaObject(): void
1767d76853dSGreg Roach    {
177e5a6b4d4SGreg Roach        $user_service          = new UserService();
1782c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
1792c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
1801e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
181e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
1821fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
1837d76853dSGreg Roach        Auth::login($user);
1847d76853dSGreg Roach
1857d76853dSGreg Roach        $record = $tree->createMediaObject("0 @@ OBJE\n1 FILE foo.jpeg");
1865e933c21SGreg Roach        self::assertTrue($record->isPendingAddition());
1877d76853dSGreg Roach
1881fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
1897d76853dSGreg Roach        $record = $tree->createMediaObject("0 @@ OBJE\n1 FILE foo.jpeg");
1905e933c21SGreg Roach        self::assertFalse($record->isPendingAddition());
1917d76853dSGreg Roach    }
1927d76853dSGreg Roach
193bec87e94SGreg Roach    public function testCreateInvalidRecord(): void
194bec87e94SGreg Roach    {
1953cfcc809SGreg Roach        $this->expectException(InvalidArgumentException::class);
1963cfcc809SGreg Roach
197e5a6b4d4SGreg Roach        $user_service          = new UserService();
1982c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
1992c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
2001e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
201e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
2021fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
203bec87e94SGreg Roach        Auth::login($user);
204bec87e94SGreg Roach
205bec87e94SGreg Roach        $tree->createRecord("0 @@FOO\n1 NOTE noted");
206bec87e94SGreg Roach    }
207bec87e94SGreg Roach
208bec87e94SGreg Roach    public function testCreateRecord(): void
209bec87e94SGreg Roach    {
210e5a6b4d4SGreg Roach        $user_service          = new UserService();
2112c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
2122c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
2131e653452SGreg Roach        $tree                  = $tree_service->create('name', 'title');
214e5a6b4d4SGreg Roach        $user                  = $user_service->create('user', 'User', 'user@example.com', 'secret');
2151fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
216bec87e94SGreg Roach        Auth::login($user);
217bec87e94SGreg Roach
218bec87e94SGreg Roach        $record = $tree->createRecord("0 @@ FOO\n1 NOTE noted");
2195e933c21SGreg Roach        self::assertTrue($record->isPendingAddition());
220bec87e94SGreg Roach
2211fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
222bec87e94SGreg Roach        $record = $tree->createRecord("0 @@ FOO\n1 NOTE noted");
2235e933c21SGreg Roach        self::assertFalse($record->isPendingAddition());
224bec87e94SGreg Roach    }
225bec87e94SGreg Roach
2268f9b0fb2SGreg Roach    public function testSignificantIndividual(): void
2278f9b0fb2SGreg Roach    {
2282c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
229e5a6b4d4SGreg Roach        $user_service = new UserService();
2302c685d76SGreg Roach        $tree_service = new TreeService($gedcom_import_service);
2311e653452SGreg Roach        $tree         = $tree_service->create('name', 'title');
232e5a6b4d4SGreg Roach        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
2331fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
2348f9b0fb2SGreg Roach        Auth::login($user);
2358f9b0fb2SGreg Roach
2365afbc57aSGreg Roach        // Delete the tree's default individual.
2372c685d76SGreg Roach        $gedcom_import_service->updateRecord('0 @X1@ INDI', $tree, true);
2385afbc57aSGreg Roach
2391f244d77SGreg Roach        // No individuals in tree?  Fake individual
2405e933c21SGreg Roach        self::assertSame('I', $tree->significantIndividual($user)->xref());
2418f9b0fb2SGreg Roach
2428f9b0fb2SGreg Roach        $record1 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
2438f9b0fb2SGreg Roach        $record2 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
2448f9b0fb2SGreg Roach        $record3 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
2458f9b0fb2SGreg Roach        $record4 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
2468f9b0fb2SGreg Roach
2478f9b0fb2SGreg Roach        // Individuals exist?  First one (lowest XREF).
2485e933c21SGreg Roach        self::assertSame($record1->xref(), $tree->significantIndividual($user)->xref());
2498f9b0fb2SGreg Roach
2508f9b0fb2SGreg Roach        // Preference for tree?
2518f9b0fb2SGreg Roach        $tree->setPreference('PEDIGREE_ROOT_ID', $record2->xref());
2525e933c21SGreg Roach        self::assertSame($record2->xref(), $tree->significantIndividual($user)->xref());
2538f9b0fb2SGreg Roach
2548f9b0fb2SGreg Roach        // User preference
2551fe542e9SGreg Roach        $tree->setUserPreference($user, UserInterface::PREF_TREE_ACCOUNT_XREF, $record3->xref());
2565e933c21SGreg Roach        self::assertSame($record3->xref(), $tree->significantIndividual($user)->xref());
2578f9b0fb2SGreg Roach
2588f9b0fb2SGreg Roach        // User record
2591fe542e9SGreg Roach        $tree->setUserPreference($user, UserInterface::PREF_TREE_DEFAULT_XREF, $record4->xref());
2605e933c21SGreg Roach        self::assertSame($record4->xref(), $tree->significantIndividual($user)->xref());
261963fbaeeSGreg Roach    }
262a7890280SGreg Roach
263a7890280SGreg Roach    public function testImportAndDeleteGedcomFile(): void
264a7890280SGreg Roach    {
2652c685d76SGreg Roach        $gedcom_import_service = new GedcomImportService();
2662c685d76SGreg Roach        $tree_service          = new TreeService($gedcom_import_service);
267a7890280SGreg Roach        $tree                  = $this->importTree('demo.ged');
2685e933c21SGreg Roach        self::assertNotNull($tree_service->all()->get('demo.ged'));
269432ff88aSGreg Roach        Site::setPreference('DEFAULT_GEDCOM', $tree->name());
270a7890280SGreg Roach
2715afbc57aSGreg Roach        $tree_service->delete($tree);
272432ff88aSGreg Roach
2735e933c21SGreg Roach        self::assertNull($tree_service->all()->get('demo.ged'));
2745e933c21SGreg Roach        self::assertSame('', Site::getPreference('DEFAULT_GEDCOM'));
275a7890280SGreg Roach    }
27615a3f100SGreg Roach
27715a3f100SGreg Roach    public function testHasPendingEdits(): void
27815a3f100SGreg Roach    {
279e5a6b4d4SGreg Roach        $user_service = new UserService();
28015a3f100SGreg Roach        $tree         = $this->importTree('demo.ged');
281e5a6b4d4SGreg Roach        $user         = $user_service->create('admin', 'Administrator', 'admin@example.com', 'secret');
2821fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
28315a3f100SGreg Roach        Auth::login($user);
28415a3f100SGreg Roach
2851fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
28615a3f100SGreg Roach        $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
2875e933c21SGreg Roach        self::assertFalse($tree->hasPendingEdit());
28815a3f100SGreg Roach
2891fe542e9SGreg Roach        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '');
29015a3f100SGreg Roach        $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
2915e933c21SGreg Roach        self::assertTrue($tree->hasPendingEdit());
29215a3f100SGreg Roach    }
29394026f20SGreg Roach
29494026f20SGreg Roach    public function testExportGedcom(): void
29594026f20SGreg Roach    {
29694026f20SGreg Roach        $tree = $this->importTree('demo.ged');
29794026f20SGreg Roach
29816ecfcafSGreg Roach        $gedcom_export_service = new GedcomExportService(new Psr17Factory(), new Psr17Factory());
29994026f20SGreg Roach
300ea517a3bSGreg Roach        $resource = $gedcom_export_service->export($tree, true);
30194026f20SGreg Roach        $original = file_get_contents(__DIR__ . '/../data/demo.ged');
302ea517a3bSGreg Roach        $export   = stream_get_contents($resource);
303ea517a3bSGreg Roach        fclose($resource);
30494026f20SGreg Roach
30569c05a6eSGreg Roach        // The version, date and time in the HEAD record will be different.
30669c05a6eSGreg Roach        $original = preg_replace('/\n2 VERS .*/', '', $original, 1);
30769c05a6eSGreg Roach        $export   = preg_replace('/\n2 VERS .*/', '', $export, 1);
30894026f20SGreg Roach        $original = preg_replace('/\n1 DATE .. ... ..../', '', $original, 1);
30994026f20SGreg Roach        $export   = preg_replace('/\n1 DATE .. ... ..../', '', $export, 1);
31094026f20SGreg Roach        $original = preg_replace('/\n2 TIME ..:..:../', '', $original, 1);
31194026f20SGreg Roach        $export   = preg_replace('/\n2 TIME ..:..:../', '', $export, 1);
31294026f20SGreg Roach
3135e933c21SGreg Roach        self::assertSame($original, $export);
31494026f20SGreg Roach    }
315a25f0a04SGreg Roach}
316