From a82dea0685363418885bf3141429e32a318dadd2 Mon Sep 17 00:00:00 2001 From: Alex Kirk Date: Fri, 2 Dec 2022 12:46:42 +0100 Subject: [PATCH] Add unit test --- .gitignore | 2 + includes/functions.php | 4 + .../class-friends-feed-parser-activitypub.php | 58 ++--- tests/bootstrap.php | 4 + ...-class-friends-feed-parser-activitypub.php | 200 ++++++++++++++++++ 5 files changed, 239 insertions(+), 29 deletions(-) create mode 100644 tests/test-class-friends-feed-parser-activitypub.php diff --git a/.gitignore b/.gitignore index 6012ccb..aa42b00 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ composer.lock .DS_Store .idea/ .php_cs.cache +.phpunit.result.cache + diff --git a/includes/functions.php b/includes/functions.php index 1b1269c..290bb57 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -113,6 +113,10 @@ function get_webfinger_resource( $user_id ) { * @return array */ function get_remote_metadata_by_actor( $actor ) { + $pre = apply_filters( 'pre_get_remote_metadata_by_actor', false, $actor ); + if ( $pre ) { + return $pre; + } if ( preg_match( '/^@?[^@]+@((?:[a-z0-9-]+\.)+[a-z]+)$/i', $actor ) ) { $actor = Rest\Webfinger::resolve( $actor ); } diff --git a/integration/class-friends-feed-parser-activitypub.php b/integration/class-friends-feed-parser-activitypub.php index cc4b236..80eabc7 100644 --- a/integration/class-friends-feed-parser-activitypub.php +++ b/integration/class-friends-feed-parser-activitypub.php @@ -153,7 +153,8 @@ class Friends_Feed_Parser_ActivityPub extends \Friends\Feed_Parser { // We don't need to handle 'Accept' types since it's handled by the ActivityPub plugin itself. 'create', 'announce', - ) + ), + true ) ) { return false; } @@ -215,16 +216,35 @@ class Friends_Feed_Parser_ActivityPub extends \Friends\Feed_Parser { * @param \Friends\User_Feed $user_feed The user feed. */ private function handle_incoming_post( $object, \Friends\User_Feed $user_feed ) { - $item = new \Friends\Feed_Item( - array( - 'permalink' => $object['url'], - 'content' => $object['content'], - 'post_format' => $this->map_type_to_post_format( $object['type'] ), - 'date' => $object['published'], - ) + $data = array( + 'permalink' => $object['url'], + 'content' => $object['content'], + 'post_format' => $this->map_type_to_post_format( $object['type'] ), + 'date' => $object['published'], ); + if ( isset( $object['attributedTo'] ) ) { + $meta = \Activitypub\get_remote_metadata_by_actor( $object['attributedTo'] ); + $this->log( 'Attributed to ' . $object['attributedTo'], compact( 'meta' ) ); + if ( isset( $meta['name'] ) ) { + $override_author = $meta['name']; + } elseif ( isset( $meta['preferredUsername'] ) ) { + $override_author = $meta['preferredUsername']; + } + } + + $this->log( + 'Received feed item', + array( + 'url' => $object['url'], + 'data' => $data, + ) + ); + $item = new \Friends\Feed_Item( $data ); + $this->friends_feed->process_incoming_feed_items( array( $item ), $user_feed ); + + return true; } /** @@ -250,27 +270,7 @@ class Friends_Feed_Parser_ActivityPub extends \Friends\Feed_Parser { } $this->log( 'Received response', compact( 'url', 'object' ) ); - $data = array( - 'permalink' => $url, - 'content' => $object['content'], - 'post_format' => $this->map_type_to_post_format( $object['type'] ), - 'date' => $object['published'], - ); - - if ( isset( $object['attributedTo'] ) ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $object['attributedTo'] ); - $this->log( 'Attributed to ' . $object['attributedTo'], compact( 'meta' ) ); - if ( isset( $meta['name'] ) ) { - $data['author'] = $meta['name']; - } elseif ( isset( $meta['preferredUsername'] ) ) { - $data['author'] = $meta['preferredUsername']; - } - } - $this->log( 'Received feed item', compact( 'url', 'data' ) ); - - $item = new \Friends\Feed_Item( $data ); - - $this->friends_feed->process_incoming_feed_items( array( $item ), $user_feed ); + return $this->handle_incoming_post( $object, $user_feed ); } /** diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9acf920..67da18a 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -19,6 +19,10 @@ require_once $_tests_dir . '/includes/functions.php'; */ function _manually_load_plugin() { require \dirname( \dirname( __FILE__ ) ) . '/activitypub.php'; + $friends_plugin = \dirname( \dirname( \dirname( __FILE__ ) ) ) . '/friends/friends.php'; + if ( file_exists( $friends_plugin ) ) { + require $friends_plugin; + } } \tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); diff --git a/tests/test-class-friends-feed-parser-activitypub.php b/tests/test-class-friends-feed-parser-activitypub.php new file mode 100644 index 0000000..a7ac7a8 --- /dev/null +++ b/tests/test-class-friends-feed-parser-activitypub.php @@ -0,0 +1,200 @@ +markTestSkipped( 'The Friends plugin is not loaded.' ); + } + parent::set_up(); + + // Manually activate the REST server. + global $wp_rest_server; + $wp_rest_server = new \Spy_REST_Server(); + $this->server = $wp_rest_server; + do_action( 'rest_api_init' ); + + add_filter( + 'rest_url', + function() { + return get_option( 'home' ) . '/wp-json/'; + } + ); + + add_filter( 'pre_http_request', array( get_called_class(), 'pre_http_request' ), 10, 3 ); + add_filter( 'http_request_host_is_external', array( get_called_class(), 'http_request_host_is_external' ), 10, 2 ); + add_filter( 'http_request_args', array( get_called_class(), 'http_request_args' ), 10, 2 ); + add_filter( 'pre_get_remote_metadata_by_actor', array( get_called_class(), 'pre_get_remote_metadata_by_actor' ), 10, 2 ); + + } + + public function tear_down() { + remove_filter( 'pre_http_request', array( get_called_class(), 'pre_http_request' ) ); + remove_filter( 'http_request_host_is_external', array( get_called_class(), 'http_request_host_is_external' ) ); + remove_filter( 'http_request_args', array( get_called_class(), 'http_request_args' ) ); + remove_filter( 'pre_get_remote_metadata_by_actor', array( get_called_class(), 'pre_get_remote_metadata_by_actor' ) ); + } + + public static function pre_get_remote_metadata_by_actor( $pre, $actor ) { + if ( isset( self::$users[ $actor ] ) ) { + return self::$users[ $actor ]; + } + return $pre; + } + public static function http_request_host_is_external( $in, $host ) { + if ( in_array( $host, array( 'mastodon.local' ), true ) ) { + return true; + } + return $in; + } + public static function http_request_args( $args, $url ) { + if ( in_array( parse_url( $url, PHP_URL_HOST ), array( 'mastodon.local' ), true ) ) { + $args['reject_unsafe_urls'] = false; + } + return $args; + } + public static function pre_http_request( $preempt, $request, $url ) { + $home_url = home_url(); + + // Pretend the url now is the requested one. + update_option( 'home', $p['scheme'] . '://' . $p['host'] ); + $rest_prefix = home_url() . '/wp-json'; + + if ( false === strpos( $url, $rest_prefix ) ) { + // Restore the old home_url. + update_option( 'home', $home_url ); + return $preempt; + } + + $url = substr( $url, strlen( $rest_prefix ) ); + $r = new \WP_REST_Request( $request['method'], $url ); + if ( ! empty( $request['body'] ) ) { + foreach ( $request['body'] as $key => $value ) { + $r->set_param( $key, $value ); + } + } + global $wp_rest_server; + $response = $wp_rest_server->dispatch( $r ); + // Restore the old url. + update_option( 'home', $home_url ); + + return apply_filters( + 'fake_http_response', + array( + 'headers' => array( + 'content-type' => 'text/json', + ), + 'body' => wp_json_encode( $response->data ), + 'response' => array( + 'code' => $response->status, + ), + ), + $p['scheme'] . '://' . $p['host'], + $url, + $request + ); + } + + public function test_incoming_post() { + $now = time() - 10; + $status_id = 123; + + $friend_name = 'Alex'; + $actor = 'https://mastodon.local/users/alex'; + + $friend_id = $this->factory->user->create( + array( + 'user_login' => 'alex-mastodon.local', + 'display_name' => $friend_name, + 'role' => 'friend', + ) + ); + \Friends\User_Feed::save( + new \Friends\User( $friend_id ), + $actor, + array( + 'parser' => 'activitypub', + ) + ); + + self::$users[ $actor ] = array( + 'url' => $actor, + 'name' => $friend_name, + ); + self::$users['https://mastodon.local/@alex'] = self::$users[ $actor ]; + + $posts = get_posts( + array( + 'post_type' => \Friends\Friends::CPT, + 'author' => $friend_id, + ) + ); + + $this->assertEquals( 0, count( $posts ) ); + + $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/' . get_current_user_id() . '/inbox' ); + $request->set_param( 'type', 'Create' ); + $request->set_param( 'id', 'test1' ); + $request->set_param( 'actor', $actor ); + $date = date( \DATE_W3C, $now++ ); + $content = 'Test ' . $date . ' ' . rand(); + $request->set_param( + 'object', + array( + 'type' => 'Note', + 'id' => 'test1', + 'attributedTo' => $actor, + 'content' => $content, + 'url' => 'https://mastodon.local/users/alex/statuses/' . ( $status_id++ ), + 'published' => $date, + ) + ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 202, $response->get_status() ); + + $posts = get_posts( + array( + 'post_type' => \Friends\Friends::CPT, + 'author' => $friend_id, + ) + ); + + $this->assertEquals( 1, count( $posts ) ); + $this->assertEquals( $content, $posts[0]->post_content ); + $this->assertEquals( $friend_id, $posts[0]->post_author ); + + $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/' . get_current_user_id() . '/inbox' ); + $request->set_param( 'type', 'Create' ); + $request->set_param( 'id', 'test1' ); + $request->set_param( 'actor', 'https://mastodon.local/@alex' ); + $date = date( \DATE_W3C, $now++ ); + $content = 'Test ' . $date . ' ' . rand(); + $request->set_param( + 'object', + array( + 'type' => 'Note', + 'id' => 'test2', + 'attributedTo' => 'https://mastodon.local/@alex', + 'content' => $content, + 'url' => 'https://mastodon.local/users/alex/statuses/' . ( $status_id++ ), + 'published' => $date, + ) + ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 202, $response->get_status() ); + + $posts = get_posts( + array( + 'post_type' => \Friends\Friends::CPT, + 'author' => $friend_id, + ) + ); + + $this->assertEquals( 2, count( $posts ) ); + $this->assertEquals( $content, $posts[0]->post_content ); + $this->assertEquals( $friend_id, $posts[0]->post_author ); + } +}