diff --git a/inc/class-eth-simple-shortlinks.php b/inc/class-eth-simple-shortlinks.php index 946be983836d03b6ae82a46f5c522c55aa8687b9..9dbb12da2a015947d3aa4af4e06aa31ff3afa539 100644 --- a/inc/class-eth-simple-shortlinks.php +++ b/inc/class-eth-simple-shortlinks.php @@ -18,7 +18,7 @@ class ETH_Simple_Shortlinks { * * @var self */ - private static $instance = null; + private static $instance; /** * Instantiate singleton @@ -271,14 +271,37 @@ class ETH_Simple_Shortlinks { * @param WP $request WP object. */ public function action_parse_request( $request ) { - if ( ! isset( $request->query_vars[ $this->qv ], $request->query_vars['p'] ) ) { + $redirect = $this->get_redirect_for_request( $request ); + + if ( null === $redirect ) { return; } + /** + * URLs aren't validated in case plugins filter permalinks to point to external URLs. + * + * If validation is desired, hook into the `eth_simple_shortlinks_redirect_url` filter. + */ + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect + wp_redirect( $redirect->destination, $redirect->status_code ); + exit; + } + + /** + * Parse a given WP object for its redirect destination. + * + * @param WP $request WP object. + * @return \stdClass|null + */ + public function get_redirect_for_request( $request ) { + if ( ! isset( $request->query_vars[ $this->qv ], $request->query_vars['p'] ) ) { + return null; + } + $post_object = get_post( $request->query_vars['p'] ); if ( ! $post_object instanceof WP_Post ) { - return; + return null; } /** @@ -297,40 +320,37 @@ class ETH_Simple_Shortlinks { ! $this->is_supported_post_status( $post_object->post_status ) ) ) { - return; + return null; } - $dest = get_permalink( $post_object ); - /** * Filters the redirect URL. * * @since 0.6 * - * @param string $dest Redirect destination. + * @param string $destination Redirect destination. * @param WP_Post $post_object Post being redirected to. * @param WP $request WP object. */ - $dest = apply_filters( 'eth_simple_shortlinks_redirect_url', $dest, $post_object, $request ); - - if ( $dest ) { - /** - * Filters the redirect status code. - * - * @since 0.6 - * - * @param int $status_code Redirect status code. - * @param string $dest Redirect destination. - * @param WP_Post $post_object Post being redirected to. - * @param WP $request WP object. - */ - $status_code = (int) apply_filters( 'eth_simple_shortlinks_redirect_status', 301, $dest, $post_object, $request ); - - // URLs aren't validated in case plugins filter permalinks to point to external URLs. - // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect - wp_redirect( $dest, $status_code ); - exit; + $destination = apply_filters( 'eth_simple_shortlinks_redirect_url', get_permalink( $post_object ), $post_object, $request ); + + /** + * Filters the redirect status code. + * + * @since 0.6 + * + * @param int $status_code Redirect status code. + * @param string $destination Redirect destination. + * @param WP_Post $post_object Post being redirected to. + * @param WP $request WP object. + */ + $status_code = (int) apply_filters( 'eth_simple_shortlinks_redirect_status', 301, $destination, $post_object, $request ); + + if ( empty( $destination ) || empty( $status_code ) ) { + return null; } + + return (object) compact( 'destination', 'status_code' ); } /** diff --git a/tests/bootstrap.php b/tests/bootstrap.php index f40e77271f4e5330b21b07efd0341ac3fb7abf57..f75308951b74aed1523bf7d8e90be1dac897a958 100755 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -23,7 +23,10 @@ require_once $_tests_dir . '/includes/functions.php'; * Manually load the plugin being tested. */ function _manually_load_plugin() { - require dirname( dirname( __FILE__ ) ) . '/eth-simple-shortlinks.php'; + // Plugin requires a permalink structure to operate. + _set_default_permalink_structure_for_tests(); + + require dirname( __FILE__, 2 ) . '/eth-simple-shortlinks.php'; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); diff --git a/tests/test-plugin.php b/tests/test-plugin.php new file mode 100755 index 0000000000000000000000000000000000000000..f74470b3d915d7df563327a20527332c0facbaf1 --- /dev/null +++ b/tests/test-plugin.php @@ -0,0 +1,104 @@ +<?php +/** + * Class PluginTest + * + * @package ETH_Simple_Shortlinks + */ + +/** + * Plugin test case. + */ +class PluginTest extends WP_UnitTestCase { + /** + * Post ID for published tests. + * + * @var int + */ + protected static $post_id_published; + + /** + * Post ID for draft tests. + * + * @var int + */ + protected static $post_id_draft; + + /** + * Create a post to test with. + */ + public function setUp(): void { + parent::setUp(); + + static::$post_id_published = $this->factory->post->create(); + static::$post_id_draft = $this->factory->post->create( + [ + 'post_status' => 'draft', + ] + ); + } + + /** + * Test shortlink overrides. + */ + public function test_shortlink_filters(): void { + $expected_published = user_trailingslashit( home_url( 'p/' . static::$post_id_published ) ); + $expected_draft = add_query_arg( 'p', static::$post_id_draft, user_trailingslashit( home_url() ) ); + + $this->assertEquals( $expected_published, wp_get_shortlink( static::$post_id_published ), 'Failed to assert that published post has a simple shortlink.' ); + $this->assertEquals( $expected_draft, wp_get_shortlink( static::$post_id_draft ), 'Failed to assert that draft post did not have its shortlink modified.' ); + } + + /** + * Test redirect parsing for supported post. + */ + public function test_published_post_redirect(): void { + $fake_request = new \stdClass(); + $fake_request->query_vars = [ + 'p' => static::$post_id_published, + 'eth-shortlink' => true, + ]; + + $redirect = ETH_Simple_Shortlinks::get_instance()->get_redirect_for_request( $fake_request ); + + $this->assertEquals( get_permalink( static::$post_id_published ), $redirect->destination, 'Failed to assert that redirect destination is post\'s permalink.' ); + $this->assertEquals( 301, $redirect->status_code, 'Failed to assert that redirect status code is that for a permanent redirect.' ); + } + + /** + * Test redirect parsing for unsupported post. + */ + public function test_draft_post_redirect(): void { + $fake_request = new \stdClass(); + $fake_request->query_vars = [ + 'p' => static::$post_id_draft, + 'eth-shortlink' => true, + ]; + + $redirect = ETH_Simple_Shortlinks::get_instance()->get_redirect_for_request( $fake_request ); + + $this->assertNull( $redirect, 'Failed to assert that redirect is not generated for unsupported post status.' ); + } + + /** + * Indirect test of redirection. + */ + public function test_published_post_redirect_through_artifacts(): void { + add_filter( 'eth_simple_shortlinks_verify_requested_post_support', function( $verify ) { + $this->assertEquals( static::$post_id_published, get_query_var( 'p' ), 'Failed to assert that requested post ID matches ID of published post.' ); + + return $verify; + } ); + + add_filter( 'eth_simple_shortlinks_redirect_url', function( $url ) { + $this->assertEquals( get_permalink( static::$post_id_published ), $url, 'Failed to assert that redirect URL is published post\'s permalink.' ); + + return $url; + } ); + + $this->go_to( wp_get_shortlink( static::$post_id_published ) ); + + $this->assertQueryTrue( + 'is_404' + ); + } +} diff --git a/tests/test-sample.php b/tests/test-sample.php deleted file mode 100755 index d2da28996e7c1ae676df62c9fd21fc2459aa7e15..0000000000000000000000000000000000000000 --- a/tests/test-sample.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Class SampleTest - * - * @package ETH_Simple_Shortlinks - */ - -/** - * Sample test case. - */ -class SampleTest extends WP_UnitTestCase { - - /** - * A single example test. - */ - public function test_sample() { - // Replace this with some actual testing code. - $this->assertTrue( true ); - } -}