diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c86fd79bd0a16a215eb60fd58c4115ad7a5d5052..1d51483c78d1ed4fa8326440e61a58cf31bd1590 100755
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -88,7 +88,7 @@ PHP7.3:
 
 PluginSVN:
   stage: deploy
-  image: containers.ethitter.com:443/docker/images/php:7.3
+  image: containers.ethitter.com:443/docker/wp-org-plugin-deploy:latest
   before_script:
     - curl -o ./bin/deploy.sh https://git-cdn.e15r.co/open-source/wp-org-plugin-deploy/raw/master/scripts/deploy.sh
     - chmod +x ./bin/deploy.sh
diff --git a/external-permalinks-redux.php b/external-permalinks-redux.php
index 70571546a2f81848d44b2f30849b32b43c168397..fd4eb1d3733c8837f3f6ff81a3abf9e39552e963 100644
--- a/external-permalinks-redux.php
+++ b/external-permalinks-redux.php
@@ -230,21 +230,43 @@ class external_permalinks_redux {
 			return;
 		}
 
-		$link = get_post_meta( $post->ID, $this->meta_key_target, true );
+		$redirect = $this->get_redirect_data( $post->ID );
 
-		if ( ! empty( $link ) ) {
-			$type = (int) get_post_meta( $post->ID, $this->meta_key_type, true );
-			$type = apply_filters( 'epr_status_code', $type, $link, $post );
+		if ( false === $redirect ) {
+			return;
+		}
 
-			if ( ! $type ) {
-				$type = 302;
-			}
+		// Unreasonable to validate redirect destination.
+		// phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
+		wp_redirect( $redirect['link'], $redirect['type'] );
+		exit;
+	}
 
-			// Unreasonable to validate redirect destination.
-			// phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
-			wp_redirect( $link, $type );
-			exit;
+	/**
+	 * Retrieve redirect data for a given post ID.
+	 *
+	 * @param int $post_id Post ID.
+	 * @return array|bool
+	 */
+	public function get_redirect_data( $post_id ) {
+		if ( ! is_numeric( $post_id ) ) {
+			return false;
 		}
+
+		$link = get_post_meta( $post_id, $this->meta_key_target, true );
+
+		if ( empty( $link ) ) {
+			return false;
+		}
+
+		$type = (int) get_post_meta( $post_id, $this->meta_key_type, true );
+		$type = apply_filters( 'epr_status_code', $type, $link, get_post( $post_id ) );
+
+		if ( ! $type ) {
+			$type = 302;
+		}
+
+		return compact( 'link', 'type' );
 	}
 }
 
diff --git a/tests/test-admin-callbacks.php b/tests/test-admin-callbacks.php
new file mode 100755
index 0000000000000000000000000000000000000000..fa3787fcf71543b87dc5e66ce196f2ac2002c39b
--- /dev/null
+++ b/tests/test-admin-callbacks.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Class AdminCallbacks
+ *
+ * @package External_Permalinks_Redux
+ */
+
+/**
+ * Test admin callbacks
+ */
+class AdminCallbacks extends WP_UnitTestCase {
+	/**
+	 * Redirect destination.
+	 */
+	const DESTINATION = 'https://w.org/';
+
+	/**
+	 * Redirect type.
+	 */
+	const TYPE = 302;
+
+	/**
+	 * Test post ID.
+	 *
+	 * @var int
+	 */
+	protected $post_id;
+
+	/**
+	 * Plugin instance.
+	 *
+	 * @var external_permalinks_redux
+	 */
+	protected $plugin;
+
+	/**
+	 * Metabox nonce.
+	 *
+	 * @var string
+	 */
+	protected $nonce;
+
+	/**
+	 * Create some objects with redirects.
+	 */
+	public function setUp() {
+		parent::setUp();
+
+		$this->plugin = external_permalinks_redux::get_instance();
+
+		$this->post_id = $this->factory->post->create(
+			[
+				'post_type' => 'post',
+			]
+		);
+
+		$this->nonce = wp_create_nonce( 'external-permalinks-redux' );
+	}
+
+	/**
+	 * Test metabox rendering.
+	 */
+	public function test_meta_box() {
+		ob_start();
+		$this->plugin->meta_box( get_post( $this->post_id ) );
+		$meta_box_contents = ob_get_clean();
+
+		$this->assertContains( 'value="' . $this->nonce . '"', $meta_box_contents );
+
+		foreach ( array_keys( $this->plugin->status_codes ) as $code ) {
+			$this->assertContains( 'value="' . $code . '"', $meta_box_contents );
+		}
+	}
+
+	/**
+	 * Test metabox save.
+	 */
+	public function test_save_callback() {
+		$_POST[ $this->plugin->meta_key_target . '_nonce' ] = $this->nonce;
+		$_POST[ $this->plugin->meta_key_target . '_url' ] = static::DESTINATION;
+		$_POST[ $this->plugin->meta_key_target . '_type' ] = static::TYPE;
+
+		$this->plugin->action_save_post( $this->post_id );
+
+		$this->assertEquals( static::DESTINATION, get_post_meta( $this->post_id, $this->plugin->meta_key_target, true ) );
+		$this->assertEquals( static::TYPE, get_post_meta( $this->post_id, $this->plugin->meta_key_type, true ) );
+	}
+}
diff --git a/tests/test-permalink-filters.php b/tests/test-permalink-filters.php
new file mode 100755
index 0000000000000000000000000000000000000000..2b2f4cfcf3d0406baab4d892a4499508f1177a0e
--- /dev/null
+++ b/tests/test-permalink-filters.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Class PermalinkFilters
+ *
+ * @package External_Permalinks_Redux
+ */
+
+/**
+ * Test permalink filters
+ */
+class PermalinkFilters extends WP_UnitTestCase {
+	/**
+	 * Redirect destination.
+	 */
+	const DESTINATION = 'https://w.org/';
+
+	/**
+	 * Test post ID.
+	 *
+	 * @var int
+	 */
+	protected $post_id;
+
+	/**
+	 * Test page ID.
+	 *
+	 * @var int
+	 */
+	protected $page_id;
+
+	/**
+	 * Create some objects with redirects.
+	 */
+	public function setUp() {
+		parent::setUp();
+
+		$plugin = external_permalinks_redux::get_instance();
+
+		$this->post_id = $this->factory->post->create(
+			[
+				'post_type' => 'post',
+			]
+		);
+
+		update_post_meta( $this->post_id, $plugin->meta_key_target, static::DESTINATION );
+
+		$this->page_id = $this->factory->post->create(
+			[
+				'post_type' => 'page',
+			]
+		);
+
+		update_post_meta( $this->page_id, $plugin->meta_key_target, static::DESTINATION );
+	}
+
+	/**
+	 * Test post permalink filter.
+	 */
+	public function test_post() {
+		$this->assertEquals( static::DESTINATION, get_permalink( $this->post_id ) );
+	}
+
+	/**
+	 * Test page link filter.
+	 */
+	public function test_page() {
+		$this->assertEquals( static::DESTINATION, get_page_link( $this->page_id ) );
+	}
+}
diff --git a/tests/test-redirect-callbacks.php b/tests/test-redirect-callbacks.php
new file mode 100755
index 0000000000000000000000000000000000000000..e0b914bf0698d887cba9e82c69aa383b3c3699ee
--- /dev/null
+++ b/tests/test-redirect-callbacks.php
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Class RedirectCallbacks
+ *
+ * @package External_Permalinks_Redux
+ */
+
+/**
+ * Test redirect callbacks
+ */
+class RedirectCallbacks extends WP_UnitTestCase {
+	/**
+	 * Redirect destination.
+	 */
+	const DESTINATION = 'https://w.org/';
+
+	/**
+	 * Plugin instance.
+	 *
+	 * @var external_permalinks_redux
+	 */
+	protected $plugin;
+
+	/**
+	 * Create some objects with redirects.
+	 */
+	public function setUp() {
+		parent::setUp();
+
+		$this->plugin = external_permalinks_redux::get_instance();
+	}
+
+	/**
+	 * Helper to retrieve a clean post.
+	 *
+	 * @return int
+	 */
+	protected function get_new_post() {
+		return $this->factory->post->create(
+			[
+				'post_type' => 'post',
+			]
+		);
+	}
+
+	/**
+	 * Test post with default redirect code.
+	 */
+	public function test_post_redirect_default_status() {
+		$post_id = $this->get_new_post();
+		update_post_meta( $post_id, $this->plugin->meta_key_target, static::DESTINATION );
+
+		$redirect = $this->plugin->get_redirect_data( $post_id );
+
+		$this->assertEquals( static::DESTINATION, $redirect['link'] );
+		$this->assertEquals( 302, $redirect['type'] );
+	}
+
+	/**
+	 * test post with custom redirect code.
+	 */
+	public function test_post_redirect_custom_status() {
+		$post_id = $this->get_new_post();
+		update_post_meta( $post_id, $this->plugin->meta_key_target, static::DESTINATION );
+		update_post_meta( $post_id, $this->plugin->meta_key_type, 307 );
+
+		$redirect = $this->plugin->get_redirect_data( $post_id );
+
+		$this->assertEquals( static::DESTINATION, $redirect['link'] );
+		$this->assertEquals( 307, $redirect['type'] );
+	}
+
+	/**
+	 * Test post with redirect type but no destination.
+	 */
+	public function test_post_redirect_missing_destination() {
+		$post_id = $this->get_new_post();
+		update_post_meta( $post_id, $this->plugin->meta_key_type, 307 );
+
+		$redirect = $this->plugin->get_redirect_data( $post_id );
+
+		$this->assertFalse( $redirect );
+	}
+
+	/**
+	 * Test post without redirect.
+	 */
+	public function test_post_no_redirect() {
+		$post_id = $this->get_new_post();
+		$redirect = $this->plugin->get_redirect_data( $post_id );
+
+		$this->assertFalse( $redirect );
+	}
+}
diff --git a/tests/test-sample.php b/tests/test-sample.php
deleted file mode 100755
index 5df9d59812202dd97afdf56233c21053baf20362..0000000000000000000000000000000000000000
--- a/tests/test-sample.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-/**
- * Class SampleTest
- *
- * @package External_Permalinks_Redux
- */
-
-/**
- * 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 );
-	}
-}