xref: /webtrees/tests/app/TreeTest.php (revision e57829396e448d173bb4be808a8b9fd89548228b)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees;
21
22use Fisharebest\Webtrees\Contracts\CacheFactoryInterface;
23use Fisharebest\Webtrees\Contracts\UserInterface;
24use Fisharebest\Webtrees\Functions\FunctionsImport;
25use Fisharebest\Webtrees\Services\GedcomExportService;
26use Fisharebest\Webtrees\Services\TreeService;
27use Fisharebest\Webtrees\Services\UserService;
28use InvalidArgumentException;
29use Symfony\Component\Cache\Adapter\NullAdapter;
30
31use function fclose;
32use function file_get_contents;
33use function preg_replace;
34use function stream_get_contents;
35
36/**
37 * Test harness for the class Tree
38 */
39class TreeTest extends TestCase
40{
41    protected static bool $uses_database = true;
42
43    public function setUp(): void
44    {
45        parent::setUp();
46
47        $cache_factory = $this->createMock(CacheFactoryInterface::class);
48        $cache_factory->method('array')->willReturn(new Cache(new NullAdapter()));
49        Registry::cache($cache_factory);
50    }
51
52    /**
53     * @covers \Fisharebest\Webtrees\Tree::__construct
54     * @covers \Fisharebest\Webtrees\Tree::id
55     * @covers \Fisharebest\Webtrees\Tree::name
56     * @covers \Fisharebest\Webtrees\Tree::title
57     * @return void
58     */
59    public function testConstructor(): void
60    {
61        $tree_service = new TreeService();
62        $tree         = $tree_service->create('name', 'title');
63
64        self::assertSame('name', $tree->name());
65        self::assertSame('title', $tree->title());
66    }
67
68    /**
69     * @covers \Fisharebest\Webtrees\Tree::getPreference
70     * @covers \Fisharebest\Webtrees\Tree::setPreference
71     * @return void
72     */
73    public function testTreePreferences(): void
74    {
75        $tree_service = new TreeService();
76        $tree         = $tree_service->create('name', 'title');
77
78        $tree->setPreference('foo', 'bar');
79        $pref = $tree->getPreference('foo');
80        self::assertSame('bar', $pref);
81    }
82
83    /**
84     * @covers \Fisharebest\Webtrees\Tree::getUserPreference
85     * @covers \Fisharebest\Webtrees\Tree::setUserPreference
86     * @return void
87     */
88    public function testUserTreePreferences(): void
89    {
90        $user_service = new UserService();
91        $tree_service = new TreeService();
92        $tree         = $tree_service->create('name', 'title');
93        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
94
95        $pref = $tree->getUserPreference($user, 'foo', 'default');
96        self::assertSame('default', $pref);
97
98        $tree->setUserPreference($user, 'foo', 'bar');
99        $pref = $tree->getUserPreference($user, 'foo', 'default');
100        self::assertSame('bar', $pref);
101    }
102
103    /**
104     * @covers \Fisharebest\Webtrees\Tree::createIndividual
105     * @return void
106     */
107    public function testCreateInvalidIndividual(): void
108    {
109        $this->expectException(InvalidArgumentException::class);
110
111        $user_service = new UserService();
112        $tree_service = new TreeService();
113        $tree         = $tree_service->create('name', 'title');
114        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
115        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
116        Auth::login($user);
117
118        $tree->createIndividual("0 @@ FOO\n1 SEX U");
119    }
120
121    /**
122     * @covers \Fisharebest\Webtrees\Tree::createIndividual
123     * @return void
124     */
125    public function testCreateIndividual(): void
126    {
127        $user_service = new UserService();
128        $tree_service = new TreeService();
129        $tree         = $tree_service->create('name', 'title');
130        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
131        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
132        Auth::login($user);
133
134        $record = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
135        self::assertTrue($record->isPendingAddition());
136
137        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
138        $record = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
139        self::assertFalse($record->isPendingAddition());
140    }
141
142    /**
143     * @covers \Fisharebest\Webtrees\Tree::createFamily
144     * @return void
145     */
146    public function testCreateInvalidFamily(): void
147    {
148        $this->expectException(InvalidArgumentException::class);
149
150        $user_service = new UserService();
151        $tree_service = new TreeService();
152        $tree         = $tree_service->create('name', 'title');
153        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
154        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
155        Auth::login($user);
156
157        $tree->createFamily("0 @@ FOO\n1 MARR Y");
158    }
159
160    /**
161     * @covers \Fisharebest\Webtrees\Tree::createFamily
162     * @return void
163     */
164    public function testCreateFamily(): void
165    {
166        $user_service = new UserService();
167        $tree_service = new TreeService();
168        $tree         = $tree_service->create('name', 'title');
169        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
170        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
171        Auth::login($user);
172
173        $record = $tree->createFamily("0 @@ FAM\n1 MARR Y");
174        self::assertTrue($record->isPendingAddition());
175
176        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
177        $record = $tree->createFamily("0 @@ FAM\n1 MARR Y");
178        self::assertFalse($record->isPendingAddition());
179    }
180
181    /**
182     * @covers \Fisharebest\Webtrees\Tree::createMediaObject
183     * @return void
184     */
185    public function testCreateInvalidMediaObject(): void
186    {
187        $this->expectException(InvalidArgumentException::class);
188
189        $user_service = new UserService();
190        $tree_service = new TreeService();
191        $tree         = $tree_service->create('name', 'title');
192        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
193        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
194        Auth::login($user);
195
196        $tree->createMediaObject("0 @@ FOO\n1 MARR Y");
197    }
198
199    /**
200     * @covers \Fisharebest\Webtrees\Tree::createMediaObject
201     * @return void
202     */
203    public function testCreateMediaObject(): void
204    {
205        $user_service = new UserService();
206        $tree_service = new TreeService();
207        $tree         = $tree_service->create('name', 'title');
208        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
209        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
210        Auth::login($user);
211
212        $record = $tree->createMediaObject("0 @@ OBJE\n1 FILE foo.jpeg");
213        self::assertTrue($record->isPendingAddition());
214
215        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
216        $record = $tree->createMediaObject("0 @@ OBJE\n1 FILE foo.jpeg");
217        self::assertFalse($record->isPendingAddition());
218    }
219
220    /**
221     * @covers \Fisharebest\Webtrees\Tree::createRecord
222     * @return void
223     */
224    public function testCreateInvalidRecord(): void
225    {
226        $this->expectException(InvalidArgumentException::class);
227
228        $user_service = new UserService();
229        $tree_service = new TreeService();
230        $tree         = $tree_service->create('name', 'title');
231        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
232        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
233        Auth::login($user);
234
235        $tree->createRecord("0 @@FOO\n1 NOTE noted");
236    }
237
238    /**
239     * @covers \Fisharebest\Webtrees\Tree::createRecord
240     * @return void
241     */
242    public function testCreateRecord(): void
243    {
244        $user_service = new UserService();
245        $tree_service = new TreeService();
246        $tree         = $tree_service->create('name', 'title');
247        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
248        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
249        Auth::login($user);
250
251        $record = $tree->createRecord("0 @@ FOO\n1 NOTE noted");
252        self::assertTrue($record->isPendingAddition());
253
254        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
255        $record = $tree->createRecord("0 @@ FOO\n1 NOTE noted");
256        self::assertFalse($record->isPendingAddition());
257    }
258
259    /**
260     * @covers \Fisharebest\Webtrees\Tree::significantIndividual
261     * @return void
262     */
263    public function testSignificantIndividual(): void
264    {
265        $user_service = new UserService();
266        $tree_service = new TreeService();
267        $tree         = $tree_service->create('name', 'title');
268        $user         = $user_service->create('user', 'User', 'user@example.com', 'secret');
269        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
270        Auth::login($user);
271
272        // Delete the tree's default individual.
273        FunctionsImport::updateRecord('0 @X1@ INDI', $tree, true);
274
275        // No individuals in tree?  Fake individual
276        self::assertSame('I', $tree->significantIndividual($user)->xref());
277
278        $record1 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
279        $record2 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
280        $record3 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
281        $record4 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
282
283        // Individuals exist?  First one (lowest XREF).
284        self::assertSame($record1->xref(), $tree->significantIndividual($user)->xref());
285
286        // Preference for tree?
287        $tree->setPreference('PEDIGREE_ROOT_ID', $record2->xref());
288        self::assertSame($record2->xref(), $tree->significantIndividual($user)->xref());
289
290        // User preference
291        $tree->setUserPreference($user, UserInterface::PREF_TREE_ACCOUNT_XREF, $record3->xref());
292        self::assertSame($record3->xref(), $tree->significantIndividual($user)->xref());
293
294        // User record
295        $tree->setUserPreference($user, UserInterface::PREF_TREE_DEFAULT_XREF, $record4->xref());
296        self::assertSame($record4->xref(), $tree->significantIndividual($user)->xref());
297    }
298
299    /**
300     * @covers \Fisharebest\Webtrees\Services\TreeService::importGedcomFile
301     * @covers \Fisharebest\Webtrees\Services\TreeService::deleteGenealogyData
302     * @return void
303     */
304    public function testImportAndDeleteGedcomFile(): void
305    {
306        $tree_service = new TreeService();
307        $tree = $this->importTree('demo.ged');
308        self::assertNotNull($tree_service->all()->get('demo.ged'));
309        Site::setPreference('DEFAULT_GEDCOM', $tree->name());
310
311        $tree_service->delete($tree);
312
313        self::assertNull($tree_service->all()->get('demo.ged'));
314        self::assertSame('', Site::getPreference('DEFAULT_GEDCOM'));
315    }
316
317    /**
318     * @covers \Fisharebest\Webtrees\Tree::hasPendingEdit
319     * @return void
320     */
321    public function testHasPendingEdits(): void
322    {
323        $user_service = new UserService();
324        $tree         = $this->importTree('demo.ged');
325        $user         = $user_service->create('admin', 'Administrator', 'admin@example.com', 'secret');
326        $user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
327        Auth::login($user);
328
329        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1');
330        $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
331        self::assertFalse($tree->hasPendingEdit());
332
333        $user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '');
334        $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/");
335        self::assertTrue($tree->hasPendingEdit());
336    }
337
338    /**
339     * @covers \Fisharebest\Webtrees\Services\GedcomExportService::export
340     * @return void
341     */
342    public function testExportGedcom(): void
343    {
344        $tree = $this->importTree('demo.ged');
345
346        $gedcom_export_service = new GedcomExportService();
347
348        $resource = $gedcom_export_service->export($tree, true);
349        $original = file_get_contents(__DIR__ . '/../data/demo.ged');
350        $export   = stream_get_contents($resource);
351        fclose($resource);
352
353        // The version, date and time in the HEAD record will be different.
354        $original = preg_replace('/\n2 VERS .*/', '', $original, 1);
355        $export   = preg_replace('/\n2 VERS .*/', '', $export, 1);
356        $original = preg_replace('/\n1 DATE .. ... ..../', '', $original, 1);
357        $export   = preg_replace('/\n1 DATE .. ... ..../', '', $export, 1);
358        $original = preg_replace('/\n2 TIME ..:..:../', '', $original, 1);
359        $export   = preg_replace('/\n2 TIME ..:..:../', '', $export, 1);
360
361        self::assertSame($original, $export);
362    }
363}
364