]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - tests/Core/ActivityGenerationTests.php
[CORE] Bump PHP requirement to PHP7.3+
[quix0rs-gnu-social.git] / tests / Core / ActivityGenerationTests.php
1 <?php
2 // This file is part of GNU social - https://www.gnu.org/software/social
3 //
4 // GNU social is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // GNU social is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU Affero General Public License for more details.
13 //
14 // You should have received a copy of the GNU Affero General Public License
15 // along with GNU social.  If not, see <http://www.gnu.org/licenses/>.
16
17 namespace Tests\Unit;
18
19 if (!defined('INSTALLDIR')) {
20     define('INSTALLDIR', dirname(dirname(__DIR__)));
21 }
22 if (!defined('PUBLICDIR')) {
23     define('PUBLICDIR', INSTALLDIR . DIRECTORY_SEPARATOR . 'public');
24 }
25 if (!defined('GNUSOCIAL')) {
26     define('GNUSOCIAL', true);
27 }
28 if (!defined('STATUSNET')) { // Compatibility
29     define('STATUSNET', true);
30 }
31
32 use Activity;
33 use ActivityObject;
34 use ActivityUtils;
35 use ActivityVerb;
36 use Conversation;
37 use DOMDocument;
38 use Exception;
39 use Notice;
40 use PHPUnit\Framework\TestCase;
41 use User;
42 use User_group;
43
44 require_once INSTALLDIR . '/lib/common.php';
45
46 final class ActivityGenerationTests extends TestCase
47 {
48     static $author1 = null;
49     static $author2 = null;
50
51     static $targetUser1 = null;
52     static $targetUser2 = null;
53
54     static $targetGroup1 = null;
55     static $targetGroup2 = null;
56
57     public static function setUpBeforeClass(): void
58     {
59         $authorNick1 = 'activitygenerationtestsuser' . common_random_hexstr(4);
60         $authorNick2 = 'activitygenerationtestsuser' . common_random_hexstr(4);
61
62         $targetNick1 = 'activitygenerationteststarget' . common_random_hexstr(4);
63         $targetNick2 = 'activitygenerationteststarget' . common_random_hexstr(4);
64
65         $groupNick1 = 'activitygenerationtestsgroup' . common_random_hexstr(4);
66         $groupNick2 = 'activitygenerationtestsgroup' . common_random_hexstr(4);
67
68         try {
69             self::$author1 = User::register(array('nickname' => $authorNick1,
70                 'email' => $authorNick1 . '@example.net',
71                 'email_confirmed' => true));
72
73             self::$author2 = User::register(array('nickname' => $authorNick2,
74                 'email' => $authorNick2 . '@example.net',
75                 'email_confirmed' => true));
76
77             self::$targetUser1 = User::register(array('nickname' => $targetNick1,
78                 'email' => $targetNick1 . '@example.net',
79                 'email_confirmed' => true));
80
81             self::$targetUser2 = User::register(array('nickname' => $targetNick2,
82                 'email' => $targetNick2 . '@example.net',
83                 'email_confirmed' => true));
84
85             self::$targetGroup1 = User_group::register(array('nickname' => $groupNick1,
86                 'userid' => self::$author1->id,
87                 'aliases' => array(),
88                 'local' => true,
89                 'location' => null,
90                 'description' => null,
91                 'fullname' => null,
92                 'homepage' => null,
93                 'mainpage' => null));
94             self::$targetGroup2 = User_group::register(array('nickname' => $groupNick2,
95                 'userid' => self::$author1->id,
96                 'aliases' => array(),
97                 'local' => true,
98                 'location' => null,
99                 'description' => null,
100                 'fullname' => null,
101                 'homepage' => null,
102                 'mainpage' => null));
103         } catch (Exception $e) {
104             self::tearDownAfterClass();
105             throw $e;
106         }
107     }
108
109     public function testBasicNoticeActivity()
110     {
111         $notice = $this->_fakeNotice();
112
113         $entry = $notice->asAtomEntry(true);
114
115         $element = $this->_entryToElement($entry, false);
116
117         $this->assertEquals($notice->getUri(), ActivityUtils::childContent($element, 'id'));
118         $this->assertEquals('New note by ' . self::$author1->nickname, ActivityUtils::childContent($element, 'title'));
119         $this->assertEquals($notice->rendered, ActivityUtils::childContent($element, 'content'));
120         $this->assertEquals(strtotime($notice->created), strtotime(ActivityUtils::childContent($element, 'published')));
121         $this->assertEquals(strtotime($notice->created), strtotime(ActivityUtils::childContent($element, 'updated')));
122         $this->assertEquals(ActivityVerb::POST, ActivityUtils::childContent($element, 'verb', Activity::SPEC));
123         $this->assertEquals(ActivityObject::NOTE, ActivityUtils::childContent($element, 'object-type', Activity::SPEC));
124     }
125
126     public function testNamespaceFlag()
127     {
128         $notice = $this->_fakeNotice();
129
130         $entry = $notice->asAtomEntry(true);
131
132         $element = $this->_entryToElement($entry, false);
133
134         $this->assertTrue($element->hasAttribute('xmlns'));
135         $this->assertTrue($element->hasAttribute('xmlns:thr'));
136         $this->assertTrue($element->hasAttribute('xmlns:georss'));
137         $this->assertTrue($element->hasAttribute('xmlns:activity'));
138         $this->assertTrue($element->hasAttribute('xmlns:media'));
139         $this->assertTrue($element->hasAttribute('xmlns:poco'));
140         $this->assertTrue($element->hasAttribute('xmlns:ostatus'));
141         $this->assertTrue($element->hasAttribute('xmlns:statusnet'));
142
143         $entry = $notice->asAtomEntry(false);
144
145         $element = $this->_entryToElement($entry, true);
146
147         $this->assertFalse($element->hasAttribute('xmlns'));
148         $this->assertFalse($element->hasAttribute('xmlns:thr'));
149         $this->assertFalse($element->hasAttribute('xmlns:georss'));
150         $this->assertFalse($element->hasAttribute('xmlns:activity'));
151         $this->assertFalse($element->hasAttribute('xmlns:media'));
152         $this->assertFalse($element->hasAttribute('xmlns:poco'));
153         $this->assertFalse($element->hasAttribute('xmlns:ostatus'));
154         $this->assertFalse($element->hasAttribute('xmlns:statusnet'));
155     }
156
157     public function testSourceFlag()
158     {
159         $notice = $this->_fakeNotice();
160
161         // Test with no source
162
163         $entry = $notice->asAtomEntry(false, false);
164
165         $element = $this->_entryToElement($entry, true);
166
167         $source = ActivityUtils::child($element, 'source');
168
169         $this->assertNull($source);
170
171         // Test with source
172
173         $entry = $notice->asAtomEntry(false, true);
174
175         $element = $this->_entryToElement($entry, true);
176
177         $source = ActivityUtils::child($element, 'source');
178
179         $this->assertNotNull($source);
180     }
181
182     public function testSourceContent()
183     {
184         $notice = $this->_fakeNotice();
185         // make a time difference!
186         sleep(2);
187         $notice2 = $this->_fakeNotice();
188
189         $entry = $notice->asAtomEntry(false, true);
190
191         $element = $this->_entryToElement($entry, true);
192
193         $source = ActivityUtils::child($element, 'source');
194
195         $atomUrl = common_local_url('ApiTimelineUser', array('id' => self::$author1->id, 'format' => 'atom'));
196
197         $profile = self::$author1->getProfile();
198
199         $this->assertEquals($atomUrl, ActivityUtils::childContent($source, 'id'));
200         $this->assertEquals($atomUrl, ActivityUtils::getLink($source, 'self', 'application/atom+xml'));
201         $this->assertEquals($profile->profileurl, ActivityUtils::getPermalink($source));
202         $this->assertEquals(strtotime($notice2->created), strtotime(ActivityUtils::childContent($source, 'updated')));
203         // XXX: do we care here?
204         $this->assertFalse(is_null(ActivityUtils::childContent($source, 'title')));
205         $this->assertEquals(common_config('license', 'url'), ActivityUtils::getLink($source, 'license'));
206     }
207
208     public function testAuthorFlag()
209     {
210         $notice = $this->_fakeNotice();
211
212         // Test with no author
213
214         $entry = $notice->asAtomEntry(false, false, false);
215
216         $element = $this->_entryToElement($entry, true);
217
218         $this->assertNull(ActivityUtils::child($element, 'author'));
219         $this->assertNull(ActivityUtils::child($element, 'actor', Activity::SPEC));
220
221         // Test with source
222
223         $entry = $notice->asAtomEntry(false, false, true);
224
225         $element = $this->_entryToElement($entry, true);
226
227         $author = ActivityUtils::child($element, 'author');
228         $actor = ActivityUtils::child($element, 'actor', Activity::SPEC);
229
230         $this->assertFalse(is_null($author));
231         $this->assertTrue(is_null($actor)); // <activity:actor> is obsolete, no longer added
232     }
233
234     public function testAuthorContent()
235     {
236         $notice = $this->_fakeNotice();
237
238         // Test with author
239
240         $entry = $notice->asAtomEntry(false, false, true);
241
242         $element = $this->_entryToElement($entry, true);
243
244         $author = ActivityUtils::child($element, 'author');
245
246         $this->assertEquals(self::$author1->getNickname(), ActivityUtils::childContent($author, 'name'));
247         $this->assertEquals(self::$author1->getUri(), ActivityUtils::childContent($author, 'uri'));
248     }
249
250     /**
251      * We no longer create <activity:actor> entries, they have merged to <atom:author>
252      */
253     public function testActorContent()
254     {
255         $notice = $this->_fakeNotice();
256
257         // Test with author
258
259         $entry = $notice->asAtomEntry(false, false, true);
260
261         $element = $this->_entryToElement($entry, true);
262
263         $actor = ActivityUtils::child($element, 'actor', Activity::SPEC);
264
265         $this->assertEquals($actor, null);
266     }
267
268     public function testReplyLink()
269     {
270         $orig = $this->_fakeNotice(self::$targetUser1);
271
272         $text = "@" . self::$targetUser1->nickname . " reply text " . common_random_hexstr(4);
273
274         $reply = Notice::saveNew(self::$author1->id, $text, 'test', array('uri' => null, 'reply_to' => $orig->id));
275
276         $entry = $reply->asAtomEntry();
277
278         $element = $this->_entryToElement($entry, true);
279
280         $irt = ActivityUtils::child($element, 'in-reply-to', 'http://purl.org/syndication/thread/1.0');
281
282         $this->assertNotNull($irt);
283         $this->assertEquals($orig->getUri(), $irt->getAttribute('ref'));
284         $this->assertEquals($orig->getUrl(), $irt->getAttribute('href'));
285     }
286
287     public function testReplyAttention()
288     {
289         $orig = $this->_fakeNotice(self::$targetUser1);
290
291         $text = "@" . self::$targetUser1->nickname . " reply text " . common_random_hexstr(4);
292
293         $reply = Notice::saveNew(self::$author1->id, $text, 'test', array('uri' => null, 'reply_to' => $orig->id));
294
295         $entry = $reply->asAtomEntry();
296
297         $element = $this->_entryToElement($entry, true);
298
299         $this->assertEquals(self::$targetUser1->getUri(), ActivityUtils::getLink($element, 'mentioned'));
300     }
301
302     public function testMultipleReplyAttention()
303     {
304         $orig = $this->_fakeNotice(self::$targetUser1);
305
306         $text = "@" . self::$targetUser1->nickname . " reply text " . common_random_hexstr(4);
307
308         $reply = Notice::saveNew(self::$targetUser2->id, $text, 'test', array('uri' => null, 'reply_to' => $orig->id));
309
310         $text = "@" . self::$targetUser1->nickname . " @" . self::$targetUser2->nickname . " reply text " . common_random_hexstr(4);
311
312         $reply2 = Notice::saveNew(self::$author1->id, $text, 'test', array('uri' => null, 'reply_to' => $reply->id));
313
314         $entry = $reply2->asAtomEntry();
315
316         $element = $this->_entryToElement($entry, true);
317
318         $links = ActivityUtils::getLinks($element, 'mentioned');
319
320         $hrefs = array();
321
322         foreach ($links as $link) {
323             $hrefs[] = $link->getAttribute('href');
324         }
325
326         $this->assertTrue(in_array(self::$targetUser1->getUri(), $hrefs));
327         $this->assertTrue(in_array(self::$targetUser2->getUri(), $hrefs));
328     }
329
330     public function testGroupPostAttention()
331     {
332         $text = "!" . self::$targetGroup1->nickname . " reply text " . common_random_hexstr(4);
333
334         $notice = Notice::saveNew(self::$author1->id, $text, 'test', array('uri' => null));
335
336         $entry = $notice->asAtomEntry();
337
338         $element = $this->_entryToElement($entry, true);
339
340         $this->assertEquals(self::$targetGroup1->getUri(), ActivityUtils::getLink($element, 'mentioned'));
341     }
342
343     public function testMultipleGroupPostAttention()
344     {
345         $text = "!" . self::$targetGroup1->nickname . " !" . self::$targetGroup2->nickname . " reply text " . common_random_hexstr(4);
346
347         $notice = Notice::saveNew(self::$author1->id, $text, 'test', array('uri' => null));
348
349         $entry = $notice->asAtomEntry();
350
351         $element = $this->_entryToElement($entry, true);
352
353         $links = ActivityUtils::getLinks($element, 'mentioned');
354
355         $hrefs = array();
356
357         foreach ($links as $link) {
358             $hrefs[] = $link->getAttribute('href');
359         }
360
361         $this->assertTrue(in_array(self::$targetGroup1->getUri(), $hrefs));
362         $this->assertTrue(in_array(self::$targetGroup2->getUri(), $hrefs));
363
364     }
365
366     public function testRepeatLink()
367     {
368         $notice = $this->_fakeNotice(self::$author1);
369         $repeat = $notice->repeat(self::$author2->getProfile(), 'test');
370
371         $entry = $repeat->asAtomEntry();
372
373         $element = $this->_entryToElement($entry, true);
374
375         $noticeInfo = ActivityUtils::child($element, 'notice_info', 'http://status.net/schema/api/1/');
376
377         $this->assertNotNull($noticeInfo);
378         $this->assertEquals($notice->id, $noticeInfo->getAttribute('repeat_of'));
379         $this->assertEquals($repeat->id, $noticeInfo->getAttribute('local_id'));
380     }
381
382     public function testTag()
383     {
384         $tag1 = common_random_hexstr(4);
385
386         $notice = $this->_fakeNotice(self::$author1, '#' . $tag1);
387
388         $entry = $notice->asAtomEntry();
389
390         $element = $this->_entryToElement($entry, true);
391
392         $category = ActivityUtils::child($element, 'category');
393
394         $this->assertNotNull($category);
395         $this->assertEquals($tag1, $category->getAttribute('term'));
396     }
397
398     public function testMultiTag()
399     {
400         $tag1 = common_random_hexstr(4);
401         $tag2 = common_random_hexstr(4);
402
403         $notice = $this->_fakeNotice(self::$author1, '#' . $tag1 . ' #' . $tag2);
404
405         $entry = $notice->asAtomEntry();
406
407         $element = $this->_entryToElement($entry, true);
408
409         $categories = $element->getElementsByTagName('category');
410
411         $this->assertNotNull($categories);
412         $this->assertEquals(2, $categories->length);
413
414         $terms = array();
415
416         for ($i = 0; $i < $categories->length; $i++) {
417             $cat = $categories->item($i);
418             $terms[] = $cat->getAttribute('term');
419         }
420
421         $this->assertTrue(in_array($tag1, $terms));
422         $this->assertTrue(in_array($tag2, $terms));
423     }
424
425     public function testGeotaggedActivity()
426     {
427         $notice = Notice::saveNew(self::$author1->id, common_random_hexstr(4), 'test', array('uri' => null, 'lat' => 45.5, 'lon' => -73.6));
428
429         $entry = $notice->asAtomEntry();
430
431         $element = $this->_entryToElement($entry, true);
432
433         $this->assertEquals('45.5000000 -73.6000000', ActivityUtils::childContent($element, 'point', "http://www.georss.org/georss"));
434     }
435
436     public function testNoticeInfo()
437     {
438         $notice = $this->_fakeNotice();
439
440         $entry = $notice->asAtomEntry();
441
442         $element = $this->_entryToElement($entry, true);
443
444         $noticeInfo = ActivityUtils::child($element, 'notice_info', "http://status.net/schema/api/1/");
445
446         $this->assertEquals($notice->id, $noticeInfo->getAttribute('local_id'));
447         $this->assertEquals($notice->source, $noticeInfo->getAttribute('source'));
448         $this->assertEquals('', $noticeInfo->getAttribute('repeat_of'));
449         $this->assertEquals('', $noticeInfo->getAttribute('repeated'));
450 //        $this->assertEquals('', $noticeInfo->getAttribute('favorite'));
451         $this->assertEquals('', $noticeInfo->getAttribute('source_link'));
452     }
453
454     public function testNoticeInfoRepeatOf()
455     {
456         $notice = $this->_fakeNotice();
457
458         $repeat = $notice->repeat(self::$author2->getProfile(), 'test');
459
460         $entry = $repeat->asAtomEntry();
461
462         $element = $this->_entryToElement($entry, true);
463
464         $noticeInfo = ActivityUtils::child($element, 'notice_info', "http://status.net/schema/api/1/");
465
466         $this->assertEquals($notice->id, $noticeInfo->getAttribute('repeat_of'));
467     }
468
469     public function testNoticeInfoRepeated()
470     {
471         $notice = $this->_fakeNotice();
472
473         $repeat = $notice->repeat(self::$author2->getProfile(), 'test');
474
475         $entry = $notice->asAtomEntry(false, false, false, self::$author2->getProfile());
476
477         $element = $this->_entryToElement($entry, true);
478
479         $noticeInfo = ActivityUtils::child($element, 'notice_info', "http://status.net/schema/api/1/");
480
481         $this->assertEquals('true', $noticeInfo->getAttribute('repeated'));
482
483         $entry = $notice->asAtomEntry(false, false, false, self::$targetUser1->getProfile());
484
485         $element = $this->_entryToElement($entry, true);
486
487         $noticeInfo = ActivityUtils::child($element, 'notice_info', "http://status.net/schema/api/1/");
488
489         $this->assertEquals('false', $noticeInfo->getAttribute('repeated'));
490     }
491
492     /*    public function testNoticeInfoFave()
493         {
494             $notice = $this->_fakeNotice();
495
496             $fave = Fave::addNew(self::$author2->getProfile(), $notice);
497
498             // Should be set if user has faved
499
500             $entry = $notice->asAtomEntry(false, false, false, self::$author2);
501
502             $element = $this->_entryToElement($entry, true);
503
504             $noticeInfo = ActivityUtils::child($element, 'notice_info', "http://status.net/schema/api/1/");
505
506             $this->assertEquals('true', $noticeInfo->getAttribute('favorite'));
507
508             // Shouldn't be set if user has not faved
509
510             $entry = $notice->asAtomEntry(false, false, false, self::$targetUser1);
511
512             $element = $this->_entryToElement($entry, true);
513
514             $noticeInfo = ActivityUtils::child($element, 'notice_info', "http://status.net/schema/api/1/");
515
516             $this->assertEquals('false', $noticeInfo->getAttribute('favorite'));
517         }*/
518
519     public function testConversationLink()
520     {
521         $orig = $this->_fakeNotice(self::$targetUser1);
522
523         $text = "@" . self::$targetUser1->nickname . " reply text " . common_random_hexstr(4);
524
525         $reply = Notice::saveNew(self::$author1->id, $text, 'test', array('uri' => null, 'reply_to' => $orig->id));
526
527         $conv = Conversation::getKV('id', $reply->conversation);
528
529         $entry = $reply->asAtomEntry();
530
531         $element = $this->_entryToElement($entry, true);
532
533         $this->assertEquals($conv->getUrl(), ActivityUtils::getLink($element, 'ostatus:conversation'));
534     }
535
536     public static function tearDownAfterClass(): void
537     {
538         if (!is_null(self::$author1)) {
539             self::$author1->getProfile()->delete();
540         }
541
542         if (!is_null(self::$author2)) {
543             self::$author2->getProfile()->delete();
544         }
545
546         if (!is_null(self::$targetUser1)) {
547             self::$targetUser1->getProfile()->delete();
548         }
549
550         if (!is_null(self::$targetUser2)) {
551             self::$targetUser2->getProfile()->delete();
552         }
553
554         if (!is_null(self::$targetGroup1)) {
555             self::$targetGroup1->delete();
556         }
557
558         if (!is_null(self::$targetGroup2)) {
559             self::$targetGroup2->delete();
560         }
561     }
562
563     private function _fakeNotice($user = null, $text = null)
564     {
565         if (empty($user)) {
566             $user = self::$author1;
567         }
568
569         if (empty($text)) {
570             $text = "fake-o text-o " . common_random_hexstr(32);
571         }
572
573         return Notice::saveNew($user->id, $text, 'test', array('uri' => null));
574     }
575
576     private function _entryToElement($entry, $namespace = false)
577     {
578         $xml = '<?xml version="1.0" encoding="utf-8"?>' . "\n\n";
579         $xml .= '<feed';
580         if ($namespace) {
581             $xml .= ' xmlns="http://www.w3.org/2005/Atom"';
582             $xml .= ' xmlns:thr="http://purl.org/syndication/thread/1.0"';
583             $xml .= ' xmlns:georss="http://www.georss.org/georss"';
584             $xml .= ' xmlns:activity="http://activitystrea.ms/spec/1.0/"';
585             $xml .= ' xmlns:media="http://purl.org/syndication/atommedia"';
586             $xml .= ' xmlns:poco="http://portablecontacts.net/spec/1.0"';
587             $xml .= ' xmlns:ostatus="http://ostatus.org/schema/1.0"';
588             $xml .= ' xmlns:statusnet="http://status.net/schema/api/1/"';
589         }
590         $xml .= '>' . "\n" . $entry . "\n" . '</feed>' . "\n";
591         $doc = new DOMDocument();
592         $doc->loadXML($xml);
593         $feed = $doc->documentElement;
594         $entries = $feed->getElementsByTagName('entry');
595
596         return $entries->item(0);
597     }
598 }