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