From 7a30bbf1de7246e8d91ff5fd054544ae4093f80d Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Tue, 28 Apr 2026 18:09:42 -0500 Subject: [PATCH] test: add wordpress content stubs --- .../Unit/Content/WordPressContentStubTest.php | 131 +++++++ tests/bootstrap.php | 352 ++++++++++++++++++ 2 files changed, 483 insertions(+) create mode 100644 tests/Unit/Content/WordPressContentStubTest.php diff --git a/tests/Unit/Content/WordPressContentStubTest.php b/tests/Unit/Content/WordPressContentStubTest.php new file mode 100644 index 0000000..13a00f8 --- /dev/null +++ b/tests/Unit/Content/WordPressContentStubTest.php @@ -0,0 +1,131 @@ + 'Hello', + 'post_type' => 'post', + ), + true + ); + + wp_update_post( + array( + 'ID' => $post_id, + 'post_title' => 'Updated', + ), + true + ); + + self::assertSame( 'Updated', get_post( $post_id )['post_title'] ); + } + + public function test_meta_stubs_replace_values(): void { + update_post_meta( 10, '_source_url', 'https://source.test/page' ); + update_post_meta( 10, '_source_url', 'https://destination.test/page' ); + + self::assertSame( array( 'https://destination.test/page' ), get_post_meta( 10, '_source_url', false ) ); + self::assertSame( 'https://destination.test/page', get_post_meta( 10, '_source_url', true ) ); + } + + public function test_term_stubs_insert_update_and_read_terms(): void { + $result = wp_insert_term( 'News', 'category', array( 'slug' => 'news' ) ); + + wp_update_term( $result['term_id'], 'category', array( 'name' => 'Latest News' ) ); + + $term = get_term_by( 'slug', 'news', 'category' ); + self::assertSame( 'Latest News', $term['name'] ); + } + + public function test_attachment_stubs_store_metadata(): void { + $attachment_id = wp_insert_attachment( + array( + 'post_title' => 'Image', + 'post_mime_type' => 'image/jpeg', + ), + false, + 44, + true + ); + + wp_update_attachment_metadata( + $attachment_id, + array( + 'width' => 1200, + ) + ); + + self::assertSame( 44, get_post( $attachment_id )['post_parent'] ); + self::assertSame( array( 'width' => 1200 ), wp_get_attachment_metadata( $attachment_id ) ); + } + + public function test_query_delete_and_object_term_stubs(): void { + $first_post_id = wp_insert_post( + array( + 'post_title' => 'First', + 'post_type' => 'post', + ), + true + ); + $second_post_id = wp_insert_post( + array( + 'post_title' => 'Second', + 'post_type' => 'page', + ), + true + ); + + update_post_meta( $first_post_id, '_wpcs_source_id', 10 ); + update_post_meta( $second_post_id, '_wpcs_source_id', 20 ); + wp_set_object_terms( $first_post_id, array( 'news', 'updates' ), 'category' ); + delete_post_meta( $second_post_id, '_wpcs_source_id' ); + wp_delete_post( $second_post_id, true ); + + $posts = get_posts( + array( + 'post_type' => 'post', + // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Verifies the unit-test get_posts meta query stub. + 'meta_key' => '_wpcs_source_id', + 'meta_value' => 10, + // phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value + ) + ); + + self::assertCount( 1, $posts ); + self::assertSame( $first_post_id, $posts[0]['ID'] ); + self::assertSame( array(), get_post_meta( $second_post_id, '_wpcs_source_id', false ) ); + self::assertNull( get_post( $second_post_id ) ); + self::assertTrue( $GLOBALS['wpcs_test_force_delete'][ $second_post_id ] ); + self::assertSame( + array( 'news', 'updates' ), + $GLOBALS['wpcs_test_object_terms'][ $first_post_id ]['category'] + ); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 648ecf6..110d6d4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -536,6 +536,358 @@ if ( ! function_exists( 'rest_ensure_response' ) ) { } } +if ( ! function_exists( 'wp_insert_post' ) ) { + /** + * Minimal post inserter for unit tests. + * + * @param array $postarr Post data. + * @param bool $wp_error Whether to return WP_Error on failure. + * @return int|\WP_Error + */ + function wp_insert_post( array $postarr, $wp_error = false ) { + if ( isset( $postarr['ID'] ) && (int) $postarr['ID'] > 0 ) { + $post_id = (int) $postarr['ID']; + } else { + $post_id = (int) ( $GLOBALS['wpcs_test_next_post_id'] ?? 1 ); + $GLOBALS['wpcs_test_next_post_id'] = $post_id + 1; + } + + if ( $post_id <= 0 && $wp_error ) { + return new WP_Error( 'invalid_post_id', 'Post ID is invalid.' ); + } + + $GLOBALS['wpcs_test_posts'][ $post_id ] = array_merge( + array( + 'ID' => $post_id, + 'post_title' => '', + 'post_content' => '', + 'post_excerpt' => '', + 'post_status' => 'draft', + 'post_type' => 'post', + 'post_name' => '', + 'post_parent' => 0, + 'menu_order' => 0, + 'post_mime_type' => '', + ), + $postarr, + array( 'ID' => $post_id ) + ); + + return $post_id; + } +} + +if ( ! function_exists( 'wp_update_post' ) ) { + /** + * Minimal post updater for unit tests. + * + * @param array $postarr Post data. + * @param bool $wp_error Whether to return WP_Error on failure. + * @return int|\WP_Error + */ + function wp_update_post( array $postarr, $wp_error = false ) { + $post_id = (int) ( $postarr['ID'] ?? 0 ); + + if ( $post_id <= 0 || ! isset( $GLOBALS['wpcs_test_posts'][ $post_id ] ) ) { + return $wp_error ? new WP_Error( 'invalid_post_id', 'Post does not exist.' ) : 0; + } + + $GLOBALS['wpcs_test_posts'][ $post_id ] = array_merge( + $GLOBALS['wpcs_test_posts'][ $post_id ], + $postarr, + array( 'ID' => $post_id ) + ); + + return $post_id; + } +} + +if ( ! function_exists( 'get_post' ) ) { + /** + * Minimal post reader for unit tests. + * + * @param mixed $post Post ID. + * @param string $output Output format. + * @param string $filter Filter context. + * @return array|object|null + */ + function get_post( $post = null, $output = 'ARRAY_A', $filter = 'raw' ) { + $GLOBALS['wpcs_test_post_filter'] = $filter; + $post_id = (int) $post; + $data = $GLOBALS['wpcs_test_posts'][ $post_id ] ?? null; + + if ( null === $data ) { + return null; + } + + return 'OBJECT' === $output ? (object) $data : $data; + } +} + +if ( ! function_exists( 'get_posts' ) ) { + /** + * Minimal posts query for unit tests. + * + * @param array $args Query args. + * @return array> + */ + function get_posts( array $args = array() ) { + $posts = array_values( $GLOBALS['wpcs_test_posts'] ?? array() ); + + if ( isset( $args['post_type'] ) && 'any' !== $args['post_type'] ) { + $post_types = is_array( $args['post_type'] ) ? $args['post_type'] : array( $args['post_type'] ); + $posts = array_filter( + $posts, + static function ( array $post ) use ( $post_types ): bool { + return in_array( $post['post_type'] ?? '', $post_types, true ); + } + ); + } + + if ( isset( $args['meta_key'], $args['meta_value'] ) ) { + $posts = array_filter( + $posts, + static function ( array $post ) use ( $args ): bool { + $values = $GLOBALS['wpcs_test_post_meta'][ (int) $post['ID'] ][ (string) $args['meta_key'] ] ?? array(); + + return in_array( $args['meta_value'], $values, true ); + } + ); + } + + return array_values( $posts ); + } +} + +if ( ! function_exists( 'wp_delete_post' ) ) { + /** + * Minimal post deleter for unit tests. + * + * @param int $post_id Post ID. + * @param bool $force_delete Force delete flag. + * @return bool + */ + function wp_delete_post( $post_id, $force_delete = false ) { + $GLOBALS['wpcs_test_force_delete'][ (int) $post_id ] = (bool) $force_delete; + unset( $GLOBALS['wpcs_test_posts'][ (int) $post_id ] ); + unset( $GLOBALS['wpcs_test_post_meta'][ (int) $post_id ] ); + + return true; + } +} + +if ( ! function_exists( 'update_post_meta' ) ) { + /** + * Minimal post meta updater for unit tests. + * + * @param int $post_id Post ID. + * @param string $meta_key Meta key. + * @param mixed $meta_value Meta value. + * @return bool + */ + function update_post_meta( $post_id, $meta_key, $meta_value ) { + $GLOBALS['wpcs_test_post_meta'][ (int) $post_id ][ (string) $meta_key ] = array( $meta_value ); + + return true; + } +} + +if ( ! function_exists( 'get_post_meta' ) ) { + /** + * Minimal post meta reader for unit tests. + * + * @param int $post_id Post ID. + * @param string $key Meta key. + * @param bool $single Whether to return single value. + * @return mixed + */ + function get_post_meta( $post_id, $key = '', $single = false ) { + $meta = $GLOBALS['wpcs_test_post_meta'][ (int) $post_id ] ?? array(); + + if ( '' === $key ) { + return $meta; + } + + $values = $meta[ $key ] ?? array(); + + if ( $single ) { + return $values[0] ?? ''; + } + + return $values; + } +} + +if ( ! function_exists( 'delete_post_meta' ) ) { + /** + * Minimal post meta deleter for unit tests. + * + * @param int $post_id Post ID. + * @param string $meta_key Meta key. + * @return bool + */ + function delete_post_meta( $post_id, $meta_key ) { + unset( $GLOBALS['wpcs_test_post_meta'][ (int) $post_id ][ (string) $meta_key ] ); + + return true; + } +} + +if ( ! function_exists( 'wp_insert_term' ) ) { + /** + * Minimal term inserter for unit tests. + * + * @param string $term Term name. + * @param string $taxonomy Taxonomy. + * @param array $args Term args. + * @return array|\WP_Error + */ + function wp_insert_term( $term, $taxonomy, array $args = array() ) { + if ( '' === (string) $term || '' === (string) $taxonomy ) { + return new WP_Error( 'invalid_term', 'Term name and taxonomy are required.' ); + } + + $term_id = (int) ( $GLOBALS['wpcs_test_next_term_id'] ?? 1 ); + $GLOBALS['wpcs_test_next_term_id'] = $term_id + 1; + $slug = (string) ( $args['slug'] ?? sanitize_key( $term ) ); + + $GLOBALS['wpcs_test_terms'][ $term_id ] = array( + 'term_id' => $term_id, + 'term_taxonomy_id' => $term_id, + 'name' => (string) $term, + 'taxonomy' => (string) $taxonomy, + 'slug' => $slug, + 'description' => (string) ( $args['description'] ?? '' ), + 'parent' => (int) ( $args['parent'] ?? 0 ), + ); + + return array( + 'term_id' => $term_id, + 'term_taxonomy_id' => $term_id, + ); + } +} + +if ( ! function_exists( 'wp_update_term' ) ) { + /** + * Minimal term updater for unit tests. + * + * @param int $term_id Term ID. + * @param string $taxonomy Taxonomy. + * @param array $args Term args. + * @return array|\WP_Error + */ + function wp_update_term( $term_id, $taxonomy, array $args = array() ) { + $term_id = (int) $term_id; + + if ( ! isset( $GLOBALS['wpcs_test_terms'][ $term_id ] ) ) { + return new WP_Error( 'invalid_term_id', 'Term does not exist.' ); + } + + $GLOBALS['wpcs_test_terms'][ $term_id ] = array_merge( + $GLOBALS['wpcs_test_terms'][ $term_id ], + $args, + array( + 'term_id' => $term_id, + 'term_taxonomy_id' => $term_id, + 'taxonomy' => (string) $taxonomy, + ) + ); + + return array( + 'term_id' => $term_id, + 'term_taxonomy_id' => $term_id, + ); + } +} + +if ( ! function_exists( 'get_term_by' ) ) { + /** + * Minimal term reader for unit tests. + * + * @param string $field Field name. + * @param mixed $value Field value. + * @param string $taxonomy Taxonomy. + * @return array|false + */ + function get_term_by( $field, $value, $taxonomy ) { + foreach ( $GLOBALS['wpcs_test_terms'] ?? array() as $term ) { + if ( (string) ( $term['taxonomy'] ?? '' ) !== (string) $taxonomy ) { + continue; + } + + if ( isset( $term[ $field ] ) && (string) $value === (string) $term[ $field ] ) { + return $term; + } + } + + return false; + } +} + +if ( ! function_exists( 'wp_set_object_terms' ) ) { + /** + * Minimal object term relationship setter for unit tests. + * + * @param int $object_id Object ID. + * @param string|array $terms Terms. + * @param string $taxonomy Taxonomy. + * @return array + */ + function wp_set_object_terms( $object_id, $terms, $taxonomy ) { + $term_values = is_array( $terms ) ? array_values( $terms ) : array( $terms ); + $GLOBALS['wpcs_test_object_terms'][ (int) $object_id ][ (string) $taxonomy ] = $term_values; + + return $term_values; + } +} + +if ( ! function_exists( 'wp_insert_attachment' ) ) { + /** + * Minimal attachment inserter for unit tests. + * + * @param array $args Attachment args. + * @param mixed $file File path. + * @param int $parent_post_id Parent post ID. + * @param bool $wp_error Whether to return WP_Error on failure. + * @return int|\WP_Error + */ + function wp_insert_attachment( array $args, $file = false, $parent_post_id = 0, $wp_error = false ) { + $GLOBALS['wpcs_test_attachment_files'][] = $file; + $args['post_type'] = 'attachment'; + $args['post_parent'] = (int) $parent_post_id; + + return wp_insert_post( $args, $wp_error ); + } +} + +if ( ! function_exists( 'wp_update_attachment_metadata' ) ) { + /** + * Minimal attachment metadata updater for unit tests. + * + * @param int $attachment_id Attachment ID. + * @param mixed $data Metadata. + * @return bool + */ + function wp_update_attachment_metadata( $attachment_id, $data ) { + $GLOBALS['wpcs_test_attachment_metadata'][ (int) $attachment_id ] = $data; + + return true; + } +} + +if ( ! function_exists( 'wp_get_attachment_metadata' ) ) { + /** + * Minimal attachment metadata reader for unit tests. + * + * @param int $attachment_id Attachment ID. + * @return mixed + */ + function wp_get_attachment_metadata( $attachment_id ) { + return $GLOBALS['wpcs_test_attachment_metadata'][ (int) $attachment_id ] ?? false; + } +} + if ( ! function_exists( 'admin_url' ) ) { /** * Minimal admin URL helper for unit tests.