From 51eb8e2b518c1127b13092b269251f18f0380ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Fri, 20 Dec 2024 18:39:20 +0100 Subject: [PATCH] Update tests directory structure to follow plugin structure --- composer.json | 26 +- includes/activitypub/class-handler.php | 89 +---- .../collection/class-event-sources.php | 2 +- includes/activitypub/handler/class-create.php | 14 +- includes/activitypub/handler/class-delete.php | 8 +- includes/activitypub/handler/class-update.php | 6 +- .../activitypub/transmogrifier/class-base.php | 24 -- includes/class-event-sources.php | 122 +++++- phpunit.xml | 2 +- tests/bootstrap.php | 13 +- .../class-test-event-organiser.php} | 6 +- .../transformer/class-test-event.php} | 8 +- .../transformer/class-test-eventin.php} | 4 +- .../transformer/class-test-eventprime.php} | 4 +- .../transformer/class-test-events-manger.php} | 4 +- .../transformer/class-test-gatherpress.php} | 4 +- ...lass-test-modern-events-calendar-lite.php} | 4 +- .../class-test-plugin-vs-event-list.php} | 4 +- .../class-test-the-events-calendar.php} | 4 +- .../class-test-wp-event-manager.php} | 4 +- tests/includes/class-test-event-sources.php | 226 +++++++++++ ...t-bridge-for-activitypub-event-sources.php | 356 ------------------ 22 files changed, 419 insertions(+), 515 deletions(-) rename tests/{test-class-plugin-event-organiser.php => includes/activitypub/transformer/class-test-event-organiser.php} (97%) rename tests/{test-class-activitypub-event-bridge-shortcodes.php => includes/activitypub/transformer/class-test-event.php} (96%) rename tests/{test-class-plugin-eventin.php => includes/activitypub/transformer/class-test-eventin.php} (98%) rename tests/{test-class-plugin-eventprime.php => includes/activitypub/transformer/class-test-eventprime.php} (98%) rename tests/{test-class-plugin-events-manger.php => includes/activitypub/transformer/class-test-events-manger.php} (98%) rename tests/{test-class-plugin-gatherpress.php => includes/activitypub/transformer/class-test-gatherpress.php} (96%) rename tests/{test-class-plugin-modern-events-calendar-lite.php => includes/activitypub/transformer/class-test-modern-events-calendar-lite.php} (98%) rename tests/{test-class-plugin-vs-event-list.php => includes/activitypub/transformer/class-test-plugin-vs-event-list.php} (98%) rename tests/{test-class-plugin-the-events-calendar.php => includes/activitypub/transformer/class-test-the-events-calendar.php} (98%) rename tests/{test-class-plugin-wp-event-manager.php => includes/activitypub/transformer/class-test-wp-event-manager.php} (98%) create mode 100644 tests/includes/class-test-event-sources.php delete mode 100644 tests/test-class-event-bridge-for-activitypub-event-sources.php diff --git a/composer.json b/composer.json index 1b77eba..f38bc96 100644 --- a/composer.json +++ b/composer.json @@ -48,20 +48,20 @@ ], "test": [ "@prepare-test", - "@test-vs-event-list", - "@test-the-events-calendar", - "@test-gatherpress", - "@test-events-manager", - "@test-wp-event-manager", - "@test-eventin", - "@test-modern-events-calendar-lite", - "@test-eventprime", - "@test-event-organiser", - "@test-event-bridge-for-activitypub-event-sources" + "@test-integration-vs-event-list", + "@test-integration-the-events-calendar", + "@test-integration-gatherpress", + "@test-integration- events-manager", + "@test-integration-wp-event-manager", + "@test-integration-eventin", + "@test-integration-modern-events-calendar-lite", + "@test-integration-eventprime", + "@test-integration-event-organiser", + "@test-event-sources" ], "test-debug": [ "@prepare-test", - "@test-the-events-calendar" + "@test-event-sources" ], "test-vs-event-list": "phpunit --filter=vs_event_list", "test-the-events-calendar": "phpunit --filter=the_events_calendar", @@ -72,8 +72,8 @@ "test-modern-events-calendar-lite": "phpunit --filter=modern_events_calendar_lite", "test-eventprime": "phpunit --filter=eventprime", "test-event-organiser": "phpunit --filter=event_organiser", - "test-event-bridge-for-activitypub-shortcodes": "phpunit --filter=event_bridge_for_activitypub_shortcodes", - "test-event-bridge-for-activitypub-event-sources": "phpunit --filter=event_bridge_for_activitypub_event_sources", + "test-event-bridge-for-activitypub-shortcodes": "phpunit --filter=shortcodes", + "test-event-sources": "phpunit --filter=event_sources", "test-all": "phpunit" } } diff --git a/includes/activitypub/class-handler.php b/includes/activitypub/class-handler.php index 47a6f7c..ce710fd 100644 --- a/includes/activitypub/class-handler.php +++ b/includes/activitypub/class-handler.php @@ -12,9 +12,7 @@ namespace Event_Bridge_For_ActivityPub\ActivityPub; // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore -use DateTime; -use DateTimeZone; -use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources; +use Event_Bridge_For_ActivityPub\Event_Sources; use Event_Bridge_For_ActivityPub\ActivityPub\Handler\Accept; use Event_Bridge_For_ActivityPub\ActivityPub\Handler\Update; use Event_Bridge_For_ActivityPub\ActivityPub\Handler\Create; @@ -34,92 +32,9 @@ class Handler { Delete::init(); \add_filter( 'activitypub_validate_object', - array( self::class, 'validate_object' ), + array( Event_Sources::class, 'validate_event_object' ), 12, 3 ); } - - - /** - * Validate the object. - * - * @param bool $valid The validation state. - * @param string $param The object parameter. - * @param \WP_REST_Request $request The request object. - * - * @return bool The validation state: true if valid, false if not. - */ - public static function validate_object( $valid, $param, $request ) { - $json_params = $request->get_json_params(); - - if ( isset( $json_params['object']['type'] ) && 'Event' === $json_params['object']['type'] ) { - $valid = true; - } else { - return $valid; - } - - if ( empty( $json_params['type'] ) ) { - return false; - } - - if ( empty( $json_params['actor'] ) ) { - return false; - } - - if ( ! in_array( $json_params['type'], array( 'Create', 'Update', 'Delete', 'Announce' ), true ) || is_wp_error( $request ) ) { - return $valid; - } - - $object = $json_params['object']; - - if ( ! is_array( $object ) ) { - return false; - } - - $required = array( - 'id', - 'startTime', - 'name', - ); - - if ( array_intersect( $required, array_keys( $object ) ) !== $required ) { - return false; - } - - return $valid; - } - - /** - * Check if a given DateTime is already passed. - * - * @param string $time_string The ActivityPub like time string. - * @return bool - */ - public static function is_time_passed( $time_string ) { - // Create a DateTime object from the ActivityPub time string. - $time = new DateTime( $time_string, new DateTimeZone( 'UTC' ) ); - - // Get the current time in UTC. - $current_time = new DateTime( 'now', new DateTimeZone( 'UTC' ) ); - - // Compare the event time with the current time. - return $time < $current_time; - } - - /** - * Check that an ActivityPub actor is an event source (i.e. it is followed by the ActivityPub blog actor). - * - * @param string $actor_id The actor ID. - * @return bool True if the ActivityPub actor ID is followed, false otherwise. - */ - public static function actor_is_event_source( $actor_id ) { - $event_sources = Event_Sources::get_event_sources(); - foreach ( $event_sources as $event_source ) { - if ( $actor_id === $event_source->get_id() ) { - return true; - } - } - return false; - } } diff --git a/includes/activitypub/collection/class-event-sources.php b/includes/activitypub/collection/class-event-sources.php index d76ab89..3dc412e 100644 --- a/includes/activitypub/collection/class-event-sources.php +++ b/includes/activitypub/collection/class-event-sources.php @@ -236,7 +236,7 @@ class Event_Sources { /** * Get all Event-Sources. * - * @return array The Term list of Event Sources. + * @return Event_Source[] A List of all Event Sources (follows). */ public static function get_event_sources() { return self::get_event_sources_with_count()['actors']; diff --git a/includes/activitypub/handler/class-create.php b/includes/activitypub/handler/class-create.php index 04b77e7..72e4188 100644 --- a/includes/activitypub/handler/class-create.php +++ b/includes/activitypub/handler/class-create.php @@ -8,8 +8,8 @@ namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler; use Activitypub\Collection\Actors; +use Event_Bridge_For_ActivityPub\Event_Sources; use Event_Bridge_For_ActivityPub\Setup; -use Event_Bridge_For_ActivityPub\ActivityPub\Handler; use function Activitypub\is_activity_public; @@ -36,12 +36,12 @@ class Create { * @param int $user_id The id of the local blog-user. */ public static function handle_create( $activity, $user_id ) { - // We only process activities that are target to the application user. + // We only process activities that are target to the blog actor. if ( Actors::BLOG_USER_ID !== $user_id ) { return; } - if ( ! Handler::actor_is_event_source( $activity['actor'] ) ) { + if ( ! Event_Sources::actor_is_event_source( $activity['actor'] ) ) { return; } @@ -55,8 +55,12 @@ class Create { return; } - if ( Handler::is_time_passed( $activity['object']['startTime'] ) ) { - return; + if ( Event_Sources::is_time_passed( $activity['object']['startTime'] ) ) { + return new \WP_Error( + 'event_bridge_for_activitypub_not_accepting_events_from_the_past', + __( 'We do not accept this event because it took place in the past.', 'event-bridge-for-activitypub' ), + array( 'status' => 403 ) + ); } $transmogrifier = Setup::get_transmogrifier(); diff --git a/includes/activitypub/handler/class-delete.php b/includes/activitypub/handler/class-delete.php index ae8eb8f..a435860 100644 --- a/includes/activitypub/handler/class-delete.php +++ b/includes/activitypub/handler/class-delete.php @@ -8,8 +8,8 @@ namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler; use Activitypub\Collection\Actors; +use Event_Bridge_For_ActivityPub\Event_Sources; use Event_Bridge_For_ActivityPub\Setup; -use Event_Bridge_For_ActivityPub\ActivityPub\Handler; /** * Handle Delete requests. @@ -39,7 +39,7 @@ class Delete { return; } - if ( ! Handler::actor_is_event_source( $activity['actor'] ) ) { + if ( ! Event_Sources::actor_is_event_source( $activity['actor'] ) ) { return; } @@ -48,10 +48,6 @@ class Delete { return; } - if ( Handler::is_time_passed( $activity['object']['startTime'] ) ) { - return; - } - $transmogrifier = Setup::get_transmogrifier(); if ( ! $transmogrifier ) { diff --git a/includes/activitypub/handler/class-update.php b/includes/activitypub/handler/class-update.php index bbc918f..ac71f7e 100644 --- a/includes/activitypub/handler/class-update.php +++ b/includes/activitypub/handler/class-update.php @@ -8,8 +8,8 @@ namespace Event_Bridge_For_ActivityPub\ActivityPub\Handler; use Activitypub\Collection\Actors; +use Event_Bridge_For_ActivityPub\Event_Sources; use Event_Bridge_For_ActivityPub\Setup; -use Event_Bridge_For_ActivityPub\ActivityPub\Handler; use function Activitypub\is_activity_public; @@ -41,7 +41,7 @@ class Update { return; } - if ( ! Handler::actor_is_event_source( $activity['actor'] ) ) { + if ( ! Event_Sources::actor_is_event_source( $activity['actor'] ) ) { return; } @@ -55,7 +55,7 @@ class Update { return; } - if ( Handler::is_time_passed( $activity['object']['startTime'] ) ) { + if ( Event_Sources::is_time_passed( $activity['object']['startTime'] ) ) { return; } diff --git a/includes/activitypub/transmogrifier/class-base.php b/includes/activitypub/transmogrifier/class-base.php index 6f399ae..4b3f52a 100644 --- a/includes/activitypub/transmogrifier/class-base.php +++ b/includes/activitypub/transmogrifier/class-base.php @@ -74,30 +74,6 @@ abstract class Base { ); } - /** - * Validate a time string if it is according to the ActivityPub specification. - * - * @param string $time_string The time string. - * @return bool - */ - public static function is_valid_activitypub_time_string( $time_string ) { - // Try to create a DateTime object from the input string. - try { - $date = new DateTime( $time_string ); - } catch ( Exception $e ) { - // If parsing fails, it's not valid. - return false; - } - - // Ensure the timezone is correctly formatted (e.g., 'Z' or a valid offset). - $timezone = $date->getTimezone(); - $formatted_timezone = $timezone->getName(); - - // Return true only if the time string includes 'Z' or a valid timezone offset. - $valid = 'Z' === $formatted_timezone || preg_match( '/^[+-]\d{2}:\d{2}$/ ', $formatted_timezone ); - return $valid; - } - /** * Get the image URL and alt-text of an ActivityPub object. * diff --git a/includes/class-event-sources.php b/includes/class-event-sources.php index e0619b2..e4b12aa 100644 --- a/includes/class-event-sources.php +++ b/includes/class-event-sources.php @@ -10,11 +10,14 @@ namespace Event_Bridge_For_ActivityPub; use Activitypub\Model\Blog; +use DateTime; +use DateTimeZone; use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection; use Event_Bridge_For_ActivityPub\ActivityPub\Handler; use Event_Bridge_For_ActivityPub\Admin\User_Interface; use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin_Integration; use Event_Bridge_For_ActivityPub\Integrations\Feature_Event_Sources; +use Exception; use function Activitypub\get_remote_metadata_by_actor; use function Activitypub\is_activitypub_request; @@ -244,7 +247,7 @@ class Event_Sources { /** * Get an array will all unique hosts of all Event-Sources. * - * @return array The Term list of Event Sources. + * @return array A list with all unique hosts of all Event Sources' ActivityPub IDs. */ public static function get_event_sources_hosts() { $hosts = get_transient( 'event_bridge_for_activitypub_event_sources_hosts' ); @@ -253,7 +256,7 @@ class Event_Sources { return $hosts; } - $actors = Event_Sources_Collection::get_event_sources_with_count()['actors']; + $actors = Event_Sources_Collection::get_event_sources(); $hosts = array(); foreach ( $actors as $actor ) { @@ -273,7 +276,7 @@ class Event_Sources { /** * Get add Event Sources ActivityPub IDs. * - * @return array The Term list of Event Sources. + * @return array A list with the ActivityPub IDs of all Event Sources (follows). */ public static function get_event_sources_ids() { $ids = get_transient( 'event_bridge_for_activitypub_event_sources_ids' ); @@ -282,7 +285,7 @@ class Event_Sources { return $ids; } - $actors = Event_Sources_Collection::get_event_sources_with_count()['actors']; + $actors = Event_Sources_Collection::get_event_sources(); $ids = array(); foreach ( $actors as $actor ) { @@ -304,4 +307,115 @@ class Event_Sources { $event_sources_hosts = self::get_event_sources_hosts(); return array_merge( $hosts, $event_sources_hosts ); } + + + /** + * Validate the event object. + * + * @param bool $valid The validation state. + * @param string $param The object parameter. + * @param \WP_REST_Request $request The request object. + * + * @return bool|WP_Error The validation state: true if valid, false if not. + */ + public static function validate_event_object( $valid, $param, $request ) { + $json_params = $request->get_json_params(); + + if ( isset( $json_params['object']['type'] ) && 'Event' === $json_params['object']['type'] ) { + $valid = true; + } else { + return $valid; + } + + if ( empty( $json_params['type'] ) ) { + return false; + } + + if ( empty( $json_params['actor'] ) ) { + return false; + } + + if ( ! in_array( $json_params['type'], array( 'Create', 'Update', 'Delete', 'Announce' ), true ) || is_wp_error( $request ) ) { + return $valid; + } + + $object = $json_params['object']; + + if ( ! is_array( $object ) ) { + return false; + } + + $required = array( + 'id', + 'startTime', + 'name', + ); + + if ( array_intersect( $required, array_keys( $object ) ) !== $required ) { + return false; + } + + if ( ! self::is_valid_activitypub_time_string( $object['startTime'] ) ) { + return false; + } + + return $valid; + } + + /** + * Validate a time string if it is according to the ActivityPub specification. + * + * @param string $time_string The time string. + * @return bool + */ + public static function is_valid_activitypub_time_string( $time_string ) { + // Try to create a DateTime object from the input string. + try { + $date = new DateTime( $time_string ); + } catch ( Exception $e ) { + // If parsing fails, it's not valid. + return false; + } + + // Ensure the timezone is correctly formatted (e.g., 'Z' or a valid offset). + $timezone = $date->getTimezone(); + $formatted_timezone = $timezone->getName(); + + // Return true only if the time string includes 'Z' or a valid timezone offset. + $valid = 'Z' === $formatted_timezone || preg_match( '/^[+-]\d{2}:\d{2}$/ ', $formatted_timezone ); + return $valid; + } + + /** + * Check if a given DateTime is already passed. + * + * @param string $time_string The ActivityPub like time string. + * @return bool + */ + public static function is_time_passed( $time_string ) { + // Create a DateTime object from the ActivityPub time string. + $time = new DateTime( $time_string, new DateTimeZone( 'UTC' ) ); + + // Get the current time in UTC. + $current_time = new DateTime( 'now', new DateTimeZone( 'UTC' ) ); + + // Compare the event time with the current time. + return $time < $current_time; + } + + /** + * Check that an ActivityPub actor is an event source (i.e. it is followed by the ActivityPub blog actor). + * + * @param string $actor_id The actor ID. + * @return bool True if the ActivityPub actor ID is followed, false otherwise. + */ + public static function actor_is_event_source( $actor_id ) { + $event_sources = Event_Sources_Collection::get_event_sources(); + foreach ( $event_sources as $event_source ) { + if ( $actor_id === $event_source->get_id() ) { + return true; + } + } + return false; + } } diff --git a/phpunit.xml b/phpunit.xml index e8e8560..09ea421 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -9,7 +9,7 @@ > - ./tests/ + ./tests/ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index e409173..35433bf 100755 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -5,6 +5,11 @@ * @package Event_Bridge_For_ActivityPub */ +// Defined here because setting them in .wp-env.json doesn't work for some reason. +\define( 'WP_TESTS_DOMAIN', 'example.org' ); +\define( 'WP_SITEURL', 'http://example.org' ); +\define( 'WP_HOME', 'http://example.org' ); + $_tests_dir = getenv( 'WP_TESTS_DIR' ); if ( ! $_tests_dir ) { @@ -96,9 +101,12 @@ function _manually_load_plugin() { if ( $plugin_file ) { _manually_load_event_plugin( $plugin_file ); - } elseif ( 'event_bridge_for_activitypub_event_sources' === $event_bridge_for_activitypub_integration_filter ) { + } elseif ( 'event_sources' === $event_bridge_for_activitypub_integration_filter ) { // For the Event Sources feature we currently only test with GatherPress. _manually_load_event_plugin( 'gatherpress/gatherpress.php' ); + \update_option( 'event_bridge_for_activitypub_event_sources_active', true ); + \update_option( 'event_bridge_for_activitypub_plugin_used_for_event_source_feature', 'GatherPress' ); + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); } else { // For all other tests we mainly use the Events Calendar as a reference. _manually_load_event_plugin( 'the-events-calendar/the-events-calendar.php' ); @@ -121,6 +129,9 @@ function _manually_load_plugin() { // At last manually load our WordPress plugin. require dirname( __DIR__ ) . '/event-bridge-for-activitypub.php'; + + // Always manually load the ActivityPub plugin. + require_once $plugin_dir . 'activitypub/activitypub.php'; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); diff --git a/tests/test-class-plugin-event-organiser.php b/tests/includes/activitypub/transformer/class-test-event-organiser.php similarity index 97% rename from tests/test-class-plugin-event-organiser.php rename to tests/includes/activitypub/transformer/class-test-event-organiser.php index 6a3c521..ecced27 100644 --- a/tests/test-class-plugin-event-organiser.php +++ b/tests/includes/activitypub/transformer/class-test-event-organiser.php @@ -5,10 +5,14 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + +use DateTime; + /** * Sample test case. */ -class Test_Event_Organiser extends WP_UnitTestCase { +class Test_Event_Organiser extends \WP_UnitTestCase { /** * Override the setup function, so that tests don't run if the Events Calendar is not active. */ diff --git a/tests/test-class-activitypub-event-bridge-shortcodes.php b/tests/includes/activitypub/transformer/class-test-event.php similarity index 96% rename from tests/test-class-activitypub-event-bridge-shortcodes.php rename to tests/includes/activitypub/transformer/class-test-event.php index 73f4768..ddabfe1 100644 --- a/tests/test-class-activitypub-event-bridge-shortcodes.php +++ b/tests/includes/activitypub/transformer/class-test-event.php @@ -6,14 +6,12 @@ * @license AGPL-3.0-or-later */ -use Activitypub\Shortcodes; +namespace Event_Bridge_For_ActivityPup\Tests\ActivityPub\Transformer; /** - * Test class for Activitypub Shortcodes. - * - * @coversDefaultClass \Activitypub\Shortcodes + * Test class for Shortcodes. */ -class Test_Activitypub_Event_Bridge_Shortcodes extends WP_UnitTestCase { +class Test_Event extends \WP_UnitTestCase { /** * Override the setup function, so that tests don't run if the Events Calendar is not active. */ diff --git a/tests/test-class-plugin-eventin.php b/tests/includes/activitypub/transformer/class-test-eventin.php similarity index 98% rename from tests/test-class-plugin-eventin.php rename to tests/includes/activitypub/transformer/class-test-eventin.php index d316f2b..fb1960f 100644 --- a/tests/test-class-plugin-eventin.php +++ b/tests/includes/activitypub/transformer/class-test-eventin.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Test cases for WP Event Solution. */ -class Test_Eventin extends WP_UnitTestCase { +class Test_Eventin extends \WP_UnitTestCase { /** * Basic Mock-up event. */ diff --git a/tests/test-class-plugin-eventprime.php b/tests/includes/activitypub/transformer/class-test-eventprime.php similarity index 98% rename from tests/test-class-plugin-eventprime.php rename to tests/includes/activitypub/transformer/class-test-eventprime.php index 1e750ff..50830c2 100644 --- a/tests/test-class-plugin-eventprime.php +++ b/tests/includes/activitypub/transformer/class-test-eventprime.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Sample test case. */ -class Test_EventPrime extends WP_UnitTestCase { +class Test_EventPrime extends \WP_UnitTestCase { /** * Mockup venues of certain complexity. * diff --git a/tests/test-class-plugin-events-manger.php b/tests/includes/activitypub/transformer/class-test-events-manger.php similarity index 98% rename from tests/test-class-plugin-events-manger.php rename to tests/includes/activitypub/transformer/class-test-events-manger.php index 956a332..9031f88 100644 --- a/tests/test-class-plugin-events-manger.php +++ b/tests/includes/activitypub/transformer/class-test-events-manger.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Sample test case. */ -class Test_Events_Manager extends WP_UnitTestCase { +class Test_Events_Manager extends \WP_UnitTestCase { /** * Override the setup function, so that tests don't run if the Events Calendar is not active. */ diff --git a/tests/test-class-plugin-gatherpress.php b/tests/includes/activitypub/transformer/class-test-gatherpress.php similarity index 96% rename from tests/test-class-plugin-gatherpress.php rename to tests/includes/activitypub/transformer/class-test-gatherpress.php index 2fae248..b7f0636 100644 --- a/tests/test-class-plugin-gatherpress.php +++ b/tests/includes/activitypub/transformer/class-test-gatherpress.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Sample test case. */ -class Test_GatherPress extends WP_UnitTestCase { +class Test_GatherPress extends \WP_UnitTestCase { /** * Override the setup function, so that tests don't run if the Events Calendar is not active. */ diff --git a/tests/test-class-plugin-modern-events-calendar-lite.php b/tests/includes/activitypub/transformer/class-test-modern-events-calendar-lite.php similarity index 98% rename from tests/test-class-plugin-modern-events-calendar-lite.php rename to tests/includes/activitypub/transformer/class-test-modern-events-calendar-lite.php index 3e84a81..35e3221 100644 --- a/tests/test-class-plugin-modern-events-calendar-lite.php +++ b/tests/includes/activitypub/transformer/class-test-modern-events-calendar-lite.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Sample test case. */ -class Test_Modern_Events_Calendar_Lite extends WP_UnitTestCase { +class Test_Modern_Events_Calendar_Lite extends \WP_UnitTestCase { /** * The MEC main instance. * diff --git a/tests/test-class-plugin-vs-event-list.php b/tests/includes/activitypub/transformer/class-test-plugin-vs-event-list.php similarity index 98% rename from tests/test-class-plugin-vs-event-list.php rename to tests/includes/activitypub/transformer/class-test-plugin-vs-event-list.php index a99d43f..4cb764d 100644 --- a/tests/test-class-plugin-vs-event-list.php +++ b/tests/includes/activitypub/transformer/class-test-plugin-vs-event-list.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Sample test case. */ -class Test_VS_Event_List extends WP_UnitTestCase { +class Test_VS_Event_List extends \WP_UnitTestCase { /** * Override the setup function, so that tests don't run if the Events Calendar is not active. */ diff --git a/tests/test-class-plugin-the-events-calendar.php b/tests/includes/activitypub/transformer/class-test-the-events-calendar.php similarity index 98% rename from tests/test-class-plugin-the-events-calendar.php rename to tests/includes/activitypub/transformer/class-test-the-events-calendar.php index c88bef7..c01254e 100644 --- a/tests/test-class-plugin-the-events-calendar.php +++ b/tests/includes/activitypub/transformer/class-test-the-events-calendar.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Sample test case. */ -class Test_The_Events_Calendar extends WP_UnitTestCase { +class Test_The_Events_Calendar extends \WP_UnitTestCase { /** * Mockup events of certain complexity. */ diff --git a/tests/test-class-plugin-wp-event-manager.php b/tests/includes/activitypub/transformer/class-test-wp-event-manager.php similarity index 98% rename from tests/test-class-plugin-wp-event-manager.php rename to tests/includes/activitypub/transformer/class-test-wp-event-manager.php index af68b20..025c3fe 100644 --- a/tests/test-class-plugin-wp-event-manager.php +++ b/tests/includes/activitypub/transformer/class-test-wp-event-manager.php @@ -5,10 +5,12 @@ * @package Event_Bridge_For_ActivityPub */ +namespace Event_Bridge_For_ActivityPub\Tests\ActivityPub\Transformer; + /** * Sample test case. */ -class Test_WP_Event_Manager extends WP_UnitTestCase { +class Test_WP_Event_Manager extends \WP_UnitTestCase { /** * Override the setup function, so that tests don't run if the Events Calendar is not active. */ diff --git a/tests/includes/class-test-event-sources.php b/tests/includes/class-test-event-sources.php new file mode 100644 index 0000000..b9afeea --- /dev/null +++ b/tests/includes/class-test-event-sources.php @@ -0,0 +1,226 @@ + 'https://remote.example/@organizer', + 'type' => 'Person', + 'inbox' => 'https://remote.example/@organizer/inbox', + 'outbox' => 'https://remote.example/@organizer/outbox', + 'name' => 'The Organizer', + 'summary' => 'Just a random organizer of events in the Fediverse', + ); + + /** + * Post ID. + * + * @var int + */ + protected static $event_source_post_id; + + /** + * REST Server. + * + * @var WP_REST_Server + */ + protected $server; + + /** + * Create fake data before tests run. + * + * @param WP_UnitTest_Factory $factory Helper that creates fake data. + */ + public static function wpSetUpBeforeClass( $factory ) { + // Follow actor. + $event_source = new \Event_Bridge_For_ActivityPub\ActivityPub\Model\Event_Source(); + $event_source->from_array( self::FOLLOWED_ACTOR ); + $post_id = $event_source->save(); + self::$event_source_post_id = $post_id; + } + + + /** + * Set up the test. + */ + public function set_up() { + \add_option( 'permalink_structure', '/%postname%/' ); + + global $wp_rest_server; + $wp_rest_server = new WP_REST_Server(); + $this->server = $wp_rest_server; + + do_action( 'rest_api_init' ); + + \Activitypub\Rest\Server::add_hooks(); + } + + /** + * Tear down the test. + */ + public function tear_down() { + \delete_option( 'permalink_structure' ); + \add_filter( 'activitypub_defer_signature_verification', '__return_false' ); + } + + /** + * Test receiving event from followed actor. + */ + public function test_incoming_event() { + \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); + + $json = array( + 'id' => 'https://remote.example/@organizer/events/new-year-party#create', + 'type' => 'Create', + 'actor' => 'https://remote.example/@organizer', + 'object' => array( + 'id' => 'https://remote.example/@organizer/events/new-year-party', + 'type' => 'Event', + 'startTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS ), + 'name' => 'New Years Party 50/51', + 'to' => 'https://www.w3.org/ns/activitystreams#Public', + 'published' => '2020-01-01T00:00:00Z', + ), + ); + + $request = new WP_REST_Request( 'POST', '/activitypub/1.0/users/0/inbox' ); + $request->set_header( 'Content-Type', 'application/activity+json' ); + $request->set_body( \wp_json_encode( $json ) ); + + // Dispatch the request. + $response = \rest_do_request( $request ); + $this->assertEquals( 202, $response->get_status() ); + } + + /** + * Test receiving event from followed actor with missing start time. + */ + public function test_incoming_create_with_missing_start_time() { + \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); + + $json = array( + 'id' => 'https://remote.example/@organizer/events/new-year-party#create', + 'type' => 'Create', + 'actor' => 'https://remote.example/@organizer', + 'object' => array( + 'id' => 'https://remote.example/@organizer/events/new-year-party', + 'type' => 'Event', + 'name' => 'New Years Party 50/51', + 'to' => 'https://www.w3.org/ns/activitystreams#Public', + 'published' => '2020-01-01T00:00:00Z', + ), + ); + + $request = new WP_REST_Request( 'POST', '/activitypub/1.0/users/0/inbox' ); + $request->set_header( 'Content-Type', 'application/activity+json' ); + $request->set_body( \wp_json_encode( $json ) ); + + // Dispatch the request. + $response = \rest_do_request( $request ); + $this->assertEquals( 400, $response->get_status() ); + } + + /** + * Test receiving event from followed actor with wrongly formatted start time. + */ + public function test_incoming_event_with_faulty_start_time() { + \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); + + $json = array( + 'id' => 'https://remote.example/@organizer/events/new-year-party#create', + 'type' => 'Create', + 'actor' => 'https://remote.example/@organizer', + 'object' => array( + 'id' => 'https://remote.example/@organizer/events/new-year-party', + 'type' => 'Event', + 'name' => 'New Years Party 50/51', + 'startTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() + WEEK_IN_SECONDS ), + 'to' => 'https://www.w3.org/ns/activitystreams#Public', + 'published' => '2020-01-01T00:00:00Z', + ), + ); + + $request = new WP_REST_Request( 'POST', '/activitypub/1.0/users/0/inbox' ); + $request->set_header( 'Content-Type', 'application/activity+json' ); + $request->set_body( \wp_json_encode( $json ) ); + + // Dispatch the request. + $response = \rest_do_request( $request ); + $this->assertEquals( 401, $response->get_status() ); + } + + /** + * We do understand, but do not care about incoming events that happened in the past. + */ + public function test_incoming_event_which_took_place_in_the_past() { + \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); + + $json = array( + 'id' => 'https://remote.example/@organizer/events/new-year-party#create', + 'type' => 'Create', + 'actor' => 'https://remote.example/@organizer', + 'object' => array( + 'id' => 'https://remote.example/@organizer/events/new-year-party', + 'type' => 'Event', + 'name' => 'New Years Party 50/51', + 'startTime' => \gmdate( 'Y-m-d\TH:i:s\Z', time() - WEEK_IN_SECONDS ), + 'to' => 'https://www.w3.org/ns/activitystreams#Public', + 'published' => '2020-01-01T00:00:00Z', + ), + ); + + $request = new WP_REST_Request( 'POST', '/activitypub/1.0/users/0/inbox' ); + $request->set_header( 'Content-Type', 'application/activity+json' ); + $request->set_body( \wp_json_encode( $json ) ); + + // Dispatch the request. + $response = \rest_do_request( $request ); + // This should be 403 but it is not possible without lots of hacks at the moment. + $this->assertEquals( 401, $response->get_status() ); + } + + + /** + * Test receiving event from actor we do not follow. + */ + public function test_incoming_create_from_non_followed_actor() { + \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); + + $json = array( + 'id' => 'https://remote.example/@another_organizer/events/new-year-party#create', + 'type' => 'Create', + 'actor' => 'https://remote.example/@another_organizer', + 'object' => array( + 'id' => 'https://remote.example/@another_organizer/events/new-year-party', + 'type' => 'Event', + 'startTime' => '2050-12-31T18:00:00Z', + 'name' => 'New Years Party 50/51', + 'to' => 'https://www.w3.org/ns/activitystreams#Public', + 'published' => '2020-01-01T00:00:00Z', + ), + ); + + $request = new WP_REST_Request( 'POST', '/activitypub/1.0/users/0/inbox' ); + $request->set_header( 'Content-Type', 'application/activity+json' ); + $request->set_body( \wp_json_encode( $json ) ); + + // Dispatch the request. + $response = \rest_do_request( $request ); + $this->assertEquals( 401, $response->get_status() ); + } +} diff --git a/tests/test-class-event-bridge-for-activitypub-event-sources.php b/tests/test-class-event-bridge-for-activitypub-event-sources.php deleted file mode 100644 index 212d1c4..0000000 --- a/tests/test-class-event-bridge-for-activitypub-event-sources.php +++ /dev/null @@ -1,356 +0,0 @@ - 'https://remote.example/@id', - 'type' => 'Follow', - 'actor' => 'https://remote.example/@test', - 'object' => 'https://local.example/@test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - $response = \rest_do_request( $request ); - - $this->assertEquals( 401, $response->get_status() ); - $this->assertEquals( 'activitypub_signature_verification', $response->get_data()['code'] ); - } - - /** - * Test missing attribute. - */ - public function test_missing_attribute() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Follow', - 'actor' => 'https://remote.example/@test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - $response = \rest_do_request( $request ); - - $this->assertEquals( 400, $response->get_status() ); - $this->assertEquals( 'rest_missing_callback_param', $response->get_data()['code'] ); - $this->assertEquals( 'object', $response->get_data()['data']['params'][0] ); - } - - /** - * Test follow request. - */ - public function test_follow_request() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Follow', - 'actor' => 'https://remote.example/@test', - 'object' => 'https://local.example/@test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 202, $response->get_status() ); - } - - /** - * Test follow request global inbox. - */ - public function test_follow_request_global_inbox() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Follow', - 'actor' => 'https://remote.example/@test', - 'object' => 'https://local.example/@test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 202, $response->get_status() ); - } - - /** - * Test create request with a remote actor. - */ - public function test_create_request() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - // Invalid request, because of an invalid object. - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Create', - 'actor' => 'https://remote.example/@test', - 'object' => 'https://local.example/@test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 400, $response->get_status() ); - $this->assertEquals( 'rest_invalid_param', $response->get_data()['code'] ); - - // Valid request, because of a valid object. - $json['object'] = array( - 'id' => 'https://remote.example/post/test', - 'type' => 'Note', - 'content' => 'Hello, World!', - 'inReplyTo' => 'https://local.example/post/test', - 'published' => '2020-01-01T00:00:00Z', - ); - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 202, $response->get_status() ); - } - - /** - * Test create request global inbox. - */ - public function test_create_request_global_inbox() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - // Invalid request, because of an invalid object. - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Create', - 'actor' => 'https://remote.example/@test', - 'object' => 'https://local.example/@test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 400, $response->get_status() ); - $this->assertEquals( 'rest_invalid_param', $response->get_data()['code'] ); - - // Valid request, because of a valid object. - $json['object'] = array( - 'id' => 'https://remote.example/post/test', - 'type' => 'Note', - 'content' => 'Hello, World!', - 'inReplyTo' => 'https://local.example/post/test', - 'published' => '2020-01-01T00:00:00Z', - ); - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 202, $response->get_status() ); - } - - /** - * Test update request. - */ - public function test_update_request() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Update', - 'actor' => 'https://remote.example/@test', - 'object' => array( - 'id' => 'https://remote.example/post/test', - 'type' => 'Note', - 'content' => 'Hello, World!', - 'inReplyTo' => 'https://local.example/post/test', - 'published' => '2020-01-01T00:00:00Z', - ), - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 202, $response->get_status() ); - } - - /** - * Test like request. - */ - public function test_like_request() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Like', - 'actor' => 'https://remote.example/@test', - 'object' => 'https://local.example/post/test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 202, $response->get_status() ); - } - - /** - * Test announce request. - */ - public function test_announce_request() { - \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - - $json = array( - 'id' => 'https://remote.example/@id', - 'type' => 'Announce', - 'actor' => 'https://remote.example/@test', - 'object' => 'https://local.example/post/test', - ); - - $request = new \WP_REST_Request( 'POST', '/activitypub/1.0/users/1/inbox' ); - $request->set_header( 'Content-Type', 'application/activity+json' ); - $request->set_body( \wp_json_encode( $json ) ); - - // Dispatch the request. - $response = \rest_do_request( $request ); - $this->assertEquals( 202, $response->get_status() ); - } - - /** - * Test whether an activity is public. - * - * @dataProvider the_data_provider - * - * @param array $data The data. - * @param bool $check The check. - */ - public function test_is_activity_public( $data, $check ) { - $this->assertEquals( $check, Activitypub\is_activity_public( $data ) ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function the_data_provider() { - return array( - array( - array( - 'cc' => array( - 'https://example.org/@test', - 'https://example.com/@test2', - ), - 'to' => 'https://www.w3.org/ns/activitystreams#Public', - 'object' => array(), - ), - true, - ), - array( - array( - 'cc' => array( - 'https://example.org/@test', - 'https://example.com/@test2', - ), - 'to' => array( - 'https://www.w3.org/ns/activitystreams#Public', - ), - 'object' => array(), - ), - true, - ), - array( - array( - 'cc' => array( - 'https://example.org/@test', - 'https://example.com/@test2', - ), - 'object' => array(), - ), - false, - ), - array( - array( - 'cc' => array( - 'https://example.org/@test', - 'https://example.com/@test2', - ), - 'object' => array( - 'to' => 'https://www.w3.org/ns/activitystreams#Public', - ), - ), - true, - ), - array( - array( - 'cc' => array( - 'https://example.org/@test', - 'https://example.com/@test2', - ), - 'object' => array( - 'to' => array( - 'https://www.w3.org/ns/activitystreams#Public', - ), - ), - ), - true, - ), - ); - } -}