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