activitypub-1.0.x-dev/tests/src/Functional/ActorTest.php
tests/src/Functional/ActorTest.php
<?php
namespace Drupal\Tests\activitypub\Functional;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\Tests\WebAssert;
/**
* Tests Actor functionality.
*
* @group activitypub
*/
class ActorTest extends ActivityPubTestBase {
/**
* Tests opt in/out.
*
* @throws \Behat\Mink\Exception\ExpectationException
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function testOptInAndOptOut() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$this->enableActivityPub($assert_session);
$assert_session->responseContains('ActivityPub is enabled.');
$edit = [
'activitypub_enable' => TRUE,
'activitypub_name' => 'fediverse accountname',
];
$this->drupalLogin($this->authenticatedUserTwo);
$this->drupalGet('user/' . $this->authenticatedUserTwo->id() . '/activitypub/settings');
$this->submitForm($edit, 'Save');
$assert_session->responseContains('The username can only contain letters and numbers.');
$edit['activitypub_name'] = 'fediverseaccountname_';
$this->drupalGet('user/' . $this->authenticatedUserTwo->id() . '/activitypub/settings');
$this->submitForm($edit, 'Save');
$assert_session->responseContains('The username can only contain letters and numbers.');
$edit['activitypub_name'] = $this->accountNameOne;
$this->drupalGet('user/' . $this->authenticatedUserTwo->id() . '/activitypub/settings');
$this->submitForm($edit, 'Save');
$assert_session->responseContains('This username is already taken.');
$edit['activitypub_name'] = $this->accountNameTwo;
$this->drupalGet('user/' . $this->authenticatedUserTwo->id() . '/activitypub/settings');
$this->submitForm($edit, 'Save');
$this->drupalLogout();
$this->drupalGet(Url::fromRoute('activitypub.inbox', [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
])->toString());
// By default we'll get back a 400, that's ok as an assertion :)
$assert_session->statusCodeEquals(400);
$this->drupalGet(Url::fromRoute('activitypub.user.self', [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
]
)->toString());
$assert_session->statusCodeEquals(200);
$content = json_decode($page->getContent());
self::assertTrue(strpos($content->icon->url, 'assets/avatar.png') !== FALSE);
self::assertTrue(!isset($content->summary));
$this->drupalLogin($this->authenticatedUserOne);
$this->drupalGet('user/' . $this->authenticatedUserOne->id() . '/activitypub/delete/unknown');
$assert_session->statusCodeEquals(404);
$this->drupalGet('user/' . $this->authenticatedUserOne->id() . '/activitypub/delete/' . $this->accountNameTwo);
$assert_session->statusCodeEquals(403);
$this->drupalGet('user/' . $this->authenticatedUserOne->id() . '/activitypub/delete/' . $this->accountNameOne);
$this->submitForm(array(), 'Confirm');
self::assertTrue(!file_exists('private://activitypub/keys/' . $this->accountNameOne . '/public.pem'));
$this->drupalGet('user/' . $this->authenticatedUserOne->id() . '/activitypub');
$assert_session->responseNotContains('ActivityPub is enabled.');
$this->drupalLogout();
$this->drupalGet(Url::fromRoute('activitypub.inbox', [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
])->toString());
$assert_session->statusCodeEquals(404);
$this->drupalGet(Url::fromRoute('activitypub.user.self', [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
])->toString());
$assert_session->statusCodeEquals(404);
$this->enableActivityPub($assert_session);
$this->drupalLogout();
$this->flushBins();
/** @var \Drupal\activitypub\Entity\ActivityPubActorInterface $actor */
$actor = \Drupal::entityTypeManager()->getStorage('activitypub_actor')->loadActorByEntityIdAndType($this->authenticatedUserOne->id(), 'person');
self::assertEquals($actor->getOwnerId(), $this->authenticatedUserOne->id());
$this->drupalGet(Url::fromRoute('activitypub.user.self', [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
])->toString());
$assert_session->statusCodeEquals(200);
$this->createActivity($this->authenticatedUserOne->id());
$this->createActivity($this->authenticatedUserTwo->id());
$activities = \Drupal::entityTypeManager()->getStorage('activitypub_activity')->loadByProperties([
'uid' => $this->authenticatedUserOne->id(),
]);
self::assertEquals(1, count($activities));
$this->drupalLogin($this->authenticatedUserOne);
$edit = [
'activitypub_summary' => 'Hi, I am just testing the Drupal integration',
];
$this->drupalGet('user/' . $this->authenticatedUserOne->id() . '/activitypub/settings');
$this->submitForm($edit, 'Save');
$this->drupalLogout();
$this->drupalGet(Url::fromRoute('activitypub.user.self', [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
])->toString());
$assert_session->statusCodeEquals(200);
$content = json_decode($page->getContent());
self::assertEquals('<p>' . $edit['activitypub_summary'] . '</p>', $content->summary);
// Block user.
$this->checkRouteAccess($assert_session);
$this->authenticatedUserOne->block()->save();
$this->checkRouteAccess($assert_session, TRUE);
// Delete user.
\Drupal::entityTypeManager()->getStorage('user')->load($this->authenticatedUserOne->id())->delete();
$actor = \Drupal::entityTypeManager()->getStorage('activitypub_actor')->loadActorByEntityIdAndType($this->authenticatedUserOne->id(), 'person');
self::assertNull($actor);
self::assertTrue(!file_exists('private://activitypub/keys/' . $this->accountNameOne . '/public.pem'));
$activities = \Drupal::entityTypeManager()->getStorage('activitypub_activity')->loadByProperties(['uid' => $this->authenticatedUserOne->id()]);
self::assertEquals(0, count($activities));
$activities = \Drupal::entityTypeManager()->getStorage('activitypub_activity')->loadByProperties(['uid' => $this->authenticatedUserTwo->id()]);
self::assertEquals(1, count($activities));
}
/**
* Check route access on several AP paths.
*
* @param \Drupal\Tests\WebAssert $assert_session
* WebAssert object for asserting the presence of elements with.
* @param bool $blocked
* If route is whether blocked or not.
*
* @throws \Behat\Mink\Exception\ExpectationException
*/
protected function checkRouteAccess(WebAssert $assert_session, bool $blocked = FALSE) {
$actor_stream_href = Url::fromRoute('activitypub.user.self', [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
])->toString();
$this->drupalGet($actor_stream_href);
$assert_session->statusCodeEquals($blocked ? 403 : 200);
foreach ([
'activitypub.outbox',
'activitypub.inbox',
'activitypub.followers',
'activitypub.following',
] as $route_name) {
$href = Url::fromRoute($route_name, [
'user' => $this->authenticatedUserOne->id(),
'activitypub_actor' => $this->accountNameOne,
])->toString();
$this->drupalGet($href);
$status = $blocked ? 403 : 200;
// 400 is fine as an assert for 'ok' for inbox.
if ($route_name == 'activitypub.inbox' && !$blocked) {
$status = 400;
}
$assert_session->statusCodeEquals($status);
}
}
/**
* Tests the follow me block with mastodon.social.
*/
public function testFollowBlock() {
$assert_session = $this->assertSession();
$block_settings = [
'label' => 'activitypub follow block',
//'settings[label_display]' => TRUE,
'region' => 'header',
//'visibility' => ['[request_path][pages]' => '/user/*'],
'visibility' => ['request_path' => ['pages' => '/user/*']],
];
$this->drupalPlaceBlock('activitypub_follow', $block_settings);
$this->enableActivityPub($assert_session);
$assert_session->responseContains('ActivityPub is enabled.');
// Confirm that the user has the activity pub follow block.
$this->drupalGet('user/' . $this->authenticatedUserOne->id());
$assert_session->responseContains('<h2>activitypub follow block</h2>');
// The rest of this test needs internet connection and goes out to call
// mastodon.social, which at times has connectivity problems, so ignore the
// rest by default on the bot.
if (Settings::get('activitypub_stop_follow_block_test', TRUE)) {
return;
}
// Do not let the third party service redirect to login as we want the
// templated url by RFC6570.
$this
->getSession()
->getDriver()
->getClient()
->followRedirects(FALSE);
$this->submitForm(['handle' => 'test@mastodon.social'], t('Proceed to follow'));
// The first url which redirects the follow form.
$current_url = urldecode($this->getUrl());
// User one activitypub address excluding
// subdirectories or trailing slashes.
$account_one_address = explode("/", $this->getResourceUrl($this->accountNameOne, FALSE))[0];
$url_pattern = "/" . preg_quote('https://mastodon.social', '/') . ".*" . preg_quote($account_one_address) . "/";
$message = sprintf('Current page "%s" does not match the regex "%s".', $current_url, $url_pattern);
$this->assertTrue((bool) preg_match($url_pattern, $current_url), $message);
}
}
