feat(wpmc 1.1.0): Redirect::handle reverse-slug lookup on is_404

Replace old triggering-on-page_new semantics with washanyanya-style:
- template_redirect@1 + is_404() trigger
- get_posts(name=$slug, post_type=page, status=[private,publish])
- match candidate.ID against page_unic[i].page_new
- 301 to /<town>/<orig-canonical-path>/

Closes Gitea issue #1.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Vladimir Bryzgalov
2026-05-19 02:22:24 +05:00
parent 9586810507
commit 43e3efc5a2
3 changed files with 332 additions and 54 deletions
+256 -33
View File
@@ -18,71 +18,86 @@ final class RedirectTest extends TestCase
);
}
public function test_handle_skips_when_current_town_equals_main(): void
public function test_bail_when_is_admin(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(true);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
$_SERVER['REQUEST_URI'] = '/spb/about/';
\Brain\Monkey\Functions\when('get_terms')->justReturn([10 => 'spb']);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
return false;
});
Redirect::handle();
self::assertTrue(true);
}
public function test_handle_skips_when_post_not_set(): void
public function test_bail_when_not_404(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
$_SERVER['REQUEST_URI'] = '/about/';
\Brain\Monkey\Functions\when('get_terms')->justReturn([11 => 'tyumen']);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
return false;
});
unset($GLOBALS['post']);
\Brain\Monkey\Functions\when('is_404')->justReturn(false);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
Redirect::handle();
self::assertTrue(true);
}
public function test_handle_skips_when_url_already_has_town_prefix(): void
public function test_bail_when_main_town_slug_null(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
$_SERVER['REQUEST_URI'] = '/tyumen/about/';
\Brain\Monkey\Functions\when('get_terms')->justReturn([11 => 'tyumen']);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'main_town_slug') return null;
return false;
});
$GLOBALS['post'] = (object) ['ID' => 84];
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
Redirect::handle();
self::assertTrue(true);
}
public function test_handle_redirects_when_post_is_page_unic_clone_without_prefix(): void
public function test_bail_when_request_uri_root(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
$_SERVER['REQUEST_URI'] = '/about/';
\Brain\Monkey\Functions\when('get_terms')->justReturn([11 => 'tyumen']);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
return false;
});
$_SERVER['REQUEST_URI'] = '/';
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
\Brain\Monkey\Functions\expect('get_posts')->never();
$term = new \WP_Term();
$term->term_id = 11;
$term->slug = 'tyumen';
\Brain\Monkey\Functions\when('get_term_by')->justReturn($term);
Redirect::handle();
self::assertTrue(true);
}
public function test_bail_when_no_post_with_slug(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
return false;
});
$_SERVER['REQUEST_URI'] = '/tarif-tyumen/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([]);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
Redirect::handle();
self::assertTrue(true);
}
public function test_bail_when_post_not_in_page_unic(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'page_unic') return [
@@ -90,12 +105,94 @@ final class RedirectTest extends TestCase
];
return false;
});
$GLOBALS['post'] = (object) ['ID' => 84];
\Brain\Monkey\Functions\when('get_permalink')->justReturn('https://site.com/about/');
$_SERVER['REQUEST_URI'] = '/orphan-slug/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([
(object) ['ID' => 999],
]);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
Redirect::handle();
self::assertTrue(true);
}
public function test_bail_when_page_unic_empty(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'page_unic') return null;
return false;
});
$_SERVER['REQUEST_URI'] = '/tarif-tyumen/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([
(object) ['ID' => 84],
]);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
Redirect::handle();
self::assertTrue(true);
}
public function test_bail_when_matched_town_is_main(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'page_unic') return [
['town' => 10, 'page_old' => 42, 'page_new' => 84],
];
return false;
});
$_SERVER['REQUEST_URI'] = '/tarif-spb/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([
(object) ['ID' => 84],
]);
$term = new \WP_Term();
$term->term_id = 10;
$term->slug = 'spb';
\Brain\Monkey\Functions\when('get_term_by')->justReturn($term);
\Brain\Monkey\Functions\expect('wp_safe_redirect')->never();
Redirect::handle();
self::assertTrue(true);
}
public function test_redirect_for_private_clone(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'page_unic') return [
['town' => 11, 'page_old' => 42, 'page_new' => 84],
];
return false;
});
$_SERVER['REQUEST_URI'] = '/tarif-tyumen/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([
(object) ['ID' => 84, 'post_status' => 'private'],
]);
$term = new \WP_Term();
$term->term_id = 11;
$term->slug = 'tyumen';
\Brain\Monkey\Functions\when('get_term_by')->justReturn($term);
\Brain\Monkey\Functions\when('get_permalink')->justReturn('https://site.com/tarif/');
\Brain\Monkey\Functions\expect('wp_safe_redirect')
->once()
->with('/tyumen/about/', 301)
->with('/tyumen/tarif/', 301)
->andThrow(new \RuntimeException('redirect-called'));
try {
@@ -105,4 +202,130 @@ final class RedirectTest extends TestCase
self::assertSame('redirect-called', $e->getMessage());
}
}
public function test_redirect_for_published_clone_with_own_url(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'page_unic') return [
['town' => 11, 'page_old' => 42, 'page_new' => 84],
];
return false;
});
$_SERVER['REQUEST_URI'] = '/tarif-tyumen/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([
(object) ['ID' => 84, 'post_status' => 'publish'],
]);
$term = new \WP_Term();
$term->term_id = 11;
$term->slug = 'tyumen';
\Brain\Monkey\Functions\when('get_term_by')->justReturn($term);
\Brain\Monkey\Functions\when('get_permalink')->justReturn('https://site.com/tarif/');
\Brain\Monkey\Functions\expect('wp_safe_redirect')
->once()
->with('/tyumen/tarif/', 301)
->andThrow(new \RuntimeException('redirect-called'));
try {
Redirect::handle();
self::fail('Expected redirect to be issued');
} catch (\RuntimeException $e) {
self::assertSame('redirect-called', $e->getMessage());
}
}
public function test_redirect_strips_domain_from_get_permalink(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'page_unic') return [
['town' => 11, 'page_old' => 42, 'page_new' => 84],
];
return false;
});
$_SERVER['REQUEST_URI'] = '/tarif-tyumen/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([
(object) ['ID' => 84],
]);
$term = new \WP_Term();
$term->term_id = 11;
$term->slug = 'tyumen';
\Brain\Monkey\Functions\when('get_term_by')->justReturn($term);
\Brain\Monkey\Functions\when('get_permalink')->justReturn('https://site.com/tarif/');
\Brain\Monkey\Functions\expect('wp_safe_redirect')
->once()
->with('/tyumen/tarif/', 301)
->andThrow(new \RuntimeException('redirect-called'));
try {
Redirect::handle();
self::fail('Expected redirect to be issued');
} catch (\RuntimeException $e) {
self::assertSame('redirect-called', $e->getMessage());
}
}
public function test_get_permalink_called_inside_without_url_filters(): void
{
\Brain\Monkey\Functions\stubs(['did_action' => 1]);
\Brain\Monkey\Functions\when('is_admin')->justReturn(false);
\Brain\Monkey\Functions\when('is_404')->justReturn(true);
\Brain\Monkey\Functions\when('get_field')->alias(function ($name) {
if ($name === 'main_town_slug') return 'spb';
if ($name === 'page_unic') return [
['town' => 11, 'page_old' => 42, 'page_new' => 84],
];
return false;
});
$_SERVER['REQUEST_URI'] = '/tarif-tyumen/';
\Brain\Monkey\Functions\when('get_posts')->justReturn([
(object) ['ID' => 84],
]);
$term = new \WP_Term();
$term->term_id = 11;
$term->slug = 'tyumen';
\Brain\Monkey\Functions\when('get_term_by')->justReturn($term);
$depth_during_call = -1;
\Brain\Monkey\Functions\when('get_permalink')->alias(
function () use (&$depth_during_call) {
$depth_during_call = \WPMultiCity\Helpers::is_bypassed() ? 1 : 0;
return 'https://site.com/tarif/';
}
);
\Brain\Monkey\Functions\expect('wp_safe_redirect')
->once()
->andThrow(new \RuntimeException('redirect-called'));
try {
Redirect::handle();
} catch (\RuntimeException $e) {
// expected
}
self::assertSame(
1,
$depth_during_call,
'get_permalink must be wrapped in Helpers::without_url_filters'
);
self::assertFalse(
\WPMultiCity\Helpers::is_bypassed(),
'bypass depth must be restored to 0 after handle()'
);
}
}