diff --git a/tests/phpunit/Unit/Modules/MediaLibrary/AdminTest.php b/tests/phpunit/Unit/Modules/MediaLibrary/AdminTest.php new file mode 100644 index 0000000..32254ed --- /dev/null +++ b/tests/phpunit/Unit/Modules/MediaLibrary/AdminTest.php @@ -0,0 +1,178 @@ + + */ + private array $original_request; + + /** + * {@inheritDoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->original_request = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + } + + /** + * {@inheritDoc} + */ + protected function tearDown(): void { + $_REQUEST = $this->original_request; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + parent::tearDown(); + } + + /** + * Tests no errors on hook registration. + */ + public function test_register_hooks_adds_expected_hooks(): void { + $admin = new Admin(); + $admin->register_hooks(); + + $this->assertTrue( true ); + } + + /** + * Tests filter_ajax_query_attachments_args returns unchanged when meta_query is already set. + */ + public function test_filter_ajax_query_attachments_args_passthrough_when_meta_query_present(): void { + $admin = new Admin(); + $query = [ + 'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + [ + 'key' => 'custom_key', + 'value' => '1', + 'compare' => '=', + ], + ], + ]; + + $this->assertSame( $query, $admin->filter_ajax_query_attachments_args( $query ) ); + } + + /** + * Tests filter_ajax_query_attachments_args returns unchanged when no sync filter is in the request. + */ + public function test_filter_ajax_query_attachments_args_passthrough_without_sync_filter(): void { + $admin = new Admin(); + $query = [ 'post_type' => 'attachment' ]; + + $this->assertSame( $query, $admin->filter_ajax_query_attachments_args( $query ) ); + } + + /** + * Tests filter_ajax_query_attachments_args adds sync meta_query for onemedia_sync_status = sync. + */ + public function test_filter_ajax_query_attachments_args_adds_meta_query_for_sync_status(): void { + $_REQUEST['query'] = [ 'onemedia_sync_status' => Attachment::SYNC_STATUS_SYNC ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + $admin = new Admin(); + $result = $admin->filter_ajax_query_attachments_args( [ 'post_type' => 'attachment' ] ); + + $this->assertSame( + [ + [ + 'key' => Attachment::IS_SYNC_POSTMETA_KEY, + 'value' => '1', + 'compare' => '=', + ], + ], + $result['meta_query'] // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + ); + } + + /** + * Tests filter_ajax_query_attachments_args adds no_sync meta_query for onemedia_sync_status = no_sync. + */ + public function test_filter_ajax_query_attachments_args_adds_meta_query_for_no_sync_status(): void { + $_REQUEST['query'] = [ 'onemedia_sync_status' => Attachment::SYNC_STATUS_NO_SYNC ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + $admin = new Admin(); + $result = $admin->filter_ajax_query_attachments_args( [ 'post_type' => 'attachment' ] ); + + $this->assertSame( + [ + 'relation' => 'OR', + [ + 'key' => Attachment::IS_SYNC_POSTMETA_KEY, + 'value' => '0', + 'compare' => '=', + ], + [ + 'key' => Attachment::IS_SYNC_POSTMETA_KEY, + 'compare' => 'NOT EXISTS', + ], + ], + $result['meta_query'] // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + ); + } + + /** + * Tests filter_ajax_query_attachments_args adds sync meta_query for is_onemedia_sync = true. + */ + public function test_filter_ajax_query_attachments_args_adds_meta_query_for_is_onemedia_sync_true(): void { + $_REQUEST['query'] = [ 'is_onemedia_sync' => 'true' ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + $admin = new Admin(); + $result = $admin->filter_ajax_query_attachments_args( [ 'post_type' => 'attachment' ] ); + + $this->assertSame( + [ + [ + 'key' => Attachment::IS_SYNC_POSTMETA_KEY, + 'value' => '1', + 'compare' => '=', + ], + ], + $result['meta_query'] // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + ); + } + + /** + * Tests filter_ajax_query_attachments_args adds no_sync meta_query for is_onemedia_sync = false. + */ + public function test_filter_ajax_query_attachments_args_adds_meta_query_for_is_onemedia_sync_false(): void { + $_REQUEST['query'] = [ 'is_onemedia_sync' => 'false' ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + $admin = new Admin(); + $result = $admin->filter_ajax_query_attachments_args( [ 'post_type' => 'attachment' ] ); + + $this->assertSame( + [ + 'relation' => 'OR', + [ + 'key' => Attachment::IS_SYNC_POSTMETA_KEY, + 'value' => '0', + 'compare' => '=', + ], + [ + 'key' => Attachment::IS_SYNC_POSTMETA_KEY, + 'compare' => 'NOT EXISTS', + ], + ], + $result['meta_query'] // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + ); + } +} diff --git a/tests/phpunit/Unit/Modules/MediaLibrary/ConsumerAdminTest.php b/tests/phpunit/Unit/Modules/MediaLibrary/ConsumerAdminTest.php new file mode 100644 index 0000000..27184f1 --- /dev/null +++ b/tests/phpunit/Unit/Modules/MediaLibrary/ConsumerAdminTest.php @@ -0,0 +1,103 @@ +attachment = get_post( self::factory()->attachment->create() ); + } + + /** + * {@inheritDoc} + */ + protected function tearDown(): void { + delete_option( Settings::OPTION_SITE_TYPE ); + + parent::tearDown(); + } + + /** + * Tests no errors on hook registration when not on a governing site. + */ + public function test_register_hooks_adds_expected_hooks(): void { + $consumer_admin = new ConsumerAdmin(); + $consumer_admin->register_hooks(); + + $this->assertTrue( true ); + } + + /** + * Tests register_hooks skips all hooks when on a governing site. + */ + public function test_register_hooks_skips_hooks_on_governing_site(): void { + update_option( Settings::OPTION_SITE_TYPE, Settings::SITE_TYPE_GOVERNING ); + + $consumer_admin = new ConsumerAdmin(); + $consumer_admin->register_hooks(); + + $this->assertFalse( has_filter( 'delete_attachment', [ $consumer_admin, 'prevent_attachment_deletion' ] ) ); + } + + /** + * Tests remove_edit_delete_links removes edit and delete actions for a synced attachment. + */ + public function test_remove_edit_delete_links_removes_actions_for_synced(): void { + Attachment::set_is_synced( $this->attachment->ID, true ); + + $consumer_admin = new ConsumerAdmin(); + $result = $consumer_admin->remove_edit_delete_links( + [ + 'edit' => 'Edit', + 'delete' => 'Delete', + ], + $this->attachment + ); + + $this->assertArrayNotHasKey( 'edit', $result ); + $this->assertArrayNotHasKey( 'delete', $result ); + } + + /** + * Tests remove_edit_delete_links keeps all actions for a non-synced attachment. + */ + public function test_remove_edit_delete_links_keeps_actions_for_non_synced(): void { + $consumer_admin = new ConsumerAdmin(); + $actions = [ + 'edit' => 'Edit', + 'delete' => 'Delete', + ]; + + $result = $consumer_admin->remove_edit_delete_links( $actions, $this->attachment ); + + $this->assertSame( $actions, $result ); + } +} diff --git a/tests/phpunit/Unit/Modules/MediaSharing/AttachmentTest.php b/tests/phpunit/Unit/Modules/MediaSharing/AttachmentTest.php new file mode 100644 index 0000000..225fa90 --- /dev/null +++ b/tests/phpunit/Unit/Modules/MediaSharing/AttachmentTest.php @@ -0,0 +1,108 @@ +attachment_id = self::factory()->attachment->create(); + } + + /** + * Tests no errors on hook registration. + */ + public function test_register_hooks_adds_expected_hooks(): void { + $attachment = new Attachment(); + $attachment->register_hooks(); + + $this->assertTrue( true ); + } + + /** + * Tests is_sync_attachment returns false for a new attachment with no meta. + */ + public function test_is_sync_attachment_returns_false_for_new_attachment(): void { + $this->assertFalse( Attachment::is_sync_attachment( $this->attachment_id ) ); + } + + /** + * Tests set_is_synced and is_sync_attachment round-trip. + */ + public function test_set_is_synced_and_is_sync_attachment(): void { + Attachment::set_is_synced( $this->attachment_id, true ); + $this->assertTrue( Attachment::is_sync_attachment( $this->attachment_id ) ); + + Attachment::set_is_synced( $this->attachment_id, false ); + $this->assertFalse( Attachment::is_sync_attachment( $this->attachment_id ) ); + } + + /** + * Tests get_sync_sites returns empty array when not on a governing site. + */ + public function test_get_sync_sites_returns_empty_when_not_governing(): void { + $this->assertSame( [], Attachment::get_sync_sites( $this->attachment_id ) ); + } + + /** + * Tests get_sync_sites returns empty array on governing site when no meta is stored. + */ + public function test_get_sync_sites_returns_empty_on_governing_when_no_meta(): void { + update_option( Settings::OPTION_SITE_TYPE, Settings::SITE_TYPE_GOVERNING ); + + $this->assertSame( [], Attachment::get_sync_sites( $this->attachment_id ) ); + + delete_option( Settings::OPTION_SITE_TYPE ); + } + + /** + * Tests update_sync_attachment_versions and get_sync_attachment_versions round-trip. + */ + public function test_update_and_get_sync_attachment_versions(): void { + $versions = [ + [ + 'last_used' => 1000000, + 'file' => [ + 'path' => '/var/www/html/wp-content/uploads/test.jpg', + 'url' => 'https://example.com/wp-content/uploads/test.jpg', + ], + ], + ]; + + $this->assertTrue( Attachment::update_sync_attachment_versions( $this->attachment_id, $versions ) ); + $this->assertSame( $versions, Attachment::get_sync_attachment_versions( $this->attachment_id ) ); + } + + /** + * Tests get_sync_attachment_versions returns empty array when no versions are stored. + */ + public function test_get_sync_attachment_versions_returns_empty_when_not_set(): void { + $this->assertSame( [], Attachment::get_sync_attachment_versions( $this->attachment_id ) ); + } +} diff --git a/tests/phpunit/Unit/Modules/MediaSharing/MediaProtectionTest.php b/tests/phpunit/Unit/Modules/MediaSharing/MediaProtectionTest.php new file mode 100644 index 0000000..30144e8 --- /dev/null +++ b/tests/phpunit/Unit/Modules/MediaSharing/MediaProtectionTest.php @@ -0,0 +1,100 @@ +attachment_id = self::factory()->attachment->create(); + } + + /** + * Tests no errors on hook registration. + */ + public function test_register_hooks_adds_expected_hooks(): void { + $protection = new MediaProtection(); + $protection->register_hooks(); + + $this->assertTrue( true ); + } + + /** + * Tests prevent_sync_media_editing returns do_not_allow for a synced attachment on edit_post. + */ + public function test_prevent_sync_media_editing_returns_do_not_allow_for_synced(): void { + Attachment::set_is_synced( $this->attachment_id, true ); + + $protection = new MediaProtection(); + $result = $protection->prevent_sync_media_editing( + [ 'edit_posts' ], + 'edit_post', + 1, + [ $this->attachment_id ] + ); + + $this->assertSame( [ 'do_not_allow' ], $result ); + } + + /** + * Tests prevent_sync_media_editing passes through caps for a non-synced attachment. + */ + public function test_prevent_sync_media_editing_passes_through_for_non_synced(): void { + $caps = [ 'edit_posts' ]; + $protection = new MediaProtection(); + + $result = $protection->prevent_sync_media_editing( + $caps, + 'edit_post', + 1, + [ $this->attachment_id ] + ); + + $this->assertSame( $caps, $result ); + } + + /** + * Tests prevent_sync_media_editing passes through caps for non-edit capabilities. + */ + public function test_prevent_sync_media_editing_passes_through_for_non_edit_cap(): void { + Attachment::set_is_synced( $this->attachment_id, true ); + + $caps = [ 'upload_files' ]; + $protection = new MediaProtection(); + + $result = $protection->prevent_sync_media_editing( + $caps, + 'upload_files', + 1, + [ $this->attachment_id ] + ); + + $this->assertSame( $caps, $result ); + } +} diff --git a/tests/phpunit/Unit/Modules/MediaSharing/UserInterfaceTest.php b/tests/phpunit/Unit/Modules/MediaSharing/UserInterfaceTest.php new file mode 100644 index 0000000..5940e1f --- /dev/null +++ b/tests/phpunit/Unit/Modules/MediaSharing/UserInterfaceTest.php @@ -0,0 +1,96 @@ +attachment = get_post( self::factory()->attachment->create() ); + } + + /** + * Tests no errors on hook registration. + */ + public function test_register_hooks_adds_expected_hooks(): void { + $ui = new UserInterface(); + $ui->register_hooks(); + + $this->assertTrue( true ); + } + + /** + * Tests add_sync_column appends the sync status column. + */ + public function test_add_sync_column_appends_column(): void { + $ui = new UserInterface(); + + $result = $ui->add_sync_column( + [ + 'title' => 'Title', + 'date' => 'Date', + ] + ); + + $this->assertArrayHasKey( 'onemedia_sync_status', $result ); + } + + /** + * Tests filter_media_row_actions removes delete action for synced attachment. + */ + public function test_filter_media_row_actions_removes_delete_for_synced(): void { + Attachment::set_is_synced( $this->attachment->ID, true ); + + $ui = new UserInterface(); + $result = $ui->filter_media_row_actions( + [ + 'edit' => 'Edit', + 'delete' => 'Delete', + ], + $this->attachment + ); + + $this->assertArrayNotHasKey( 'delete', $result ); + } + + /** + * Tests filter_media_row_actions keeps all actions for non-synced attachment. + */ + public function test_filter_media_row_actions_keeps_actions_for_non_synced(): void { + $ui = new UserInterface(); + $actions = [ + 'edit' => 'Edit', + 'delete' => 'Delete', + ]; + + $result = $ui->filter_media_row_actions( $actions, $this->attachment ); + + $this->assertSame( $actions, $result ); + } +} diff --git a/tests/phpunit/Unit/Modules/Settings/AdminTest.php b/tests/phpunit/Unit/Modules/Settings/AdminTest.php new file mode 100644 index 0000000..8ecf959 --- /dev/null +++ b/tests/phpunit/Unit/Modules/Settings/AdminTest.php @@ -0,0 +1,77 @@ +register_hooks(); + + $this->assertTrue( true ); + } + + /** + * Tests screen_callback outputs the settings page mount point. + */ + public function test_screen_callback_outputs_expected_html(): void { + $admin = new Admin(); + + ob_start(); + $admin->screen_callback(); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'onemedia-settings-page', (string) $output ); + } + + /** + * Tests add_action_links appends a settings link to an empty array. + */ + public function test_add_action_links_appends_settings_link(): void { + $admin = new Admin(); + + $result = $admin->add_action_links( [] ); + + $this->assertCount( 1, $result ); + $this->assertStringContainsString( 'Settings', $result[0] ); + } + + /** + * Tests add_action_links preserves existing links. + */ + public function test_add_action_links_preserves_existing_links(): void { + $admin = new Admin(); + + $result = $admin->add_action_links( [ 'Existing' ] ); + + $this->assertCount( 2, $result ); + } + + /** + * Tests add_body_classes returns a string. + */ + public function test_add_body_classes_returns_string(): void { + $admin = new Admin(); + + $result = $admin->add_body_classes( 'existing-class' ); + + $this->assertIsString( $result ); + } +}