Resolve double redirects automatically
And test that it does so.
This commit is contained in:
parent
75cf6e4e77
commit
c3fd28afaf
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "fwdekker/death-notifier",
|
"name": "fwdekker/death-notifier",
|
||||||
"description": "Get notified when a famous person dies.",
|
"description": "Get notified when a famous person dies.",
|
||||||
"version": "0.19.10", "_comment_version": "Also update version in `package.json`!",
|
"version": "0.19.11", "_comment_version": "Also update version in `package.json`!",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "death-notifier",
|
"name": "death-notifier",
|
||||||
"version": "0.19.10", "_comment_version": "Also update version in `composer.json`!",
|
"version": "0.19.11", "_comment_version": "Also update version in `composer.json`!",
|
||||||
"description": "Get notified when a famous person dies.",
|
"description": "Get notified when a famous person dies.",
|
||||||
"author": "Florine W. Dekker",
|
"author": "Florine W. Dekker",
|
||||||
"browser": "dist/bundle.js",
|
"browser": "dist/bundle.js",
|
||||||
|
|
|
@ -408,7 +408,7 @@
|
||||||
<small id="delete-account-email-hint" data-hint-for="delete-account-email"></small>
|
<small id="delete-account-email-hint" data-hint-for="delete-account-email"></small>
|
||||||
|
|
||||||
<input type="checkbox" role="switch" id="delete-account-confirm" />
|
<input type="checkbox" role="switch" id="delete-account-confirm" />
|
||||||
<label for="delete-account-confirm">I want to permanently delete my account</label>
|
<label for="delete-account-confirm">I want to permanently delete my account.</label>
|
||||||
<br />
|
<br />
|
||||||
<small id="delete-account-confirm-hint" data-hint-for="delete-account-confirm"></small>
|
<small id="delete-account-confirm-hint" data-hint-for="delete-account-confirm"></small>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
|
@ -19,22 +19,12 @@ class Redirects implements IteratorAggregate
|
||||||
/**
|
/**
|
||||||
* @var array<string, string> the known redirects
|
* @var array<string, string> the known redirects
|
||||||
*/
|
*/
|
||||||
private readonly array $redirects;
|
private array $redirects = [];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new `Redirects`.
|
* Adds new redirects and resolves double redirects, and returns a new `Redirects` containing the old and new
|
||||||
*
|
* redirects.
|
||||||
* @param array<string, string> $redirects the redirects to start with
|
|
||||||
*/
|
|
||||||
public function __construct(array $redirects = [])
|
|
||||||
{
|
|
||||||
$this->redirects = $redirects;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a single redirect, and returns a new `Redirects` containing the current redirects and the new redirects.
|
|
||||||
*
|
*
|
||||||
* If there already exists a redirect `a -> b`, and the redirect `b -> c` is added, then `a -> b` is changed to
|
* If there already exists a redirect `a -> b`, and the redirect `b -> c` is added, then `a -> b` is changed to
|
||||||
* `a -> c`.
|
* `a -> c`.
|
||||||
|
@ -43,18 +33,24 @@ class Redirects implements IteratorAggregate
|
||||||
* `a -> c`.
|
* `a -> c`.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $redirects the redirects to add
|
* @param array<string, string> $redirects the redirects to add
|
||||||
* @return Redirects a new `Redirects` containing the current redirects and the new redirects
|
* @return Redirects a new `Redirects` containing the old and new redirects
|
||||||
*/
|
*/
|
||||||
public function add_redirects(array $redirects): Redirects
|
public function add(array $redirects): Redirects
|
||||||
{
|
{
|
||||||
$new_redirects = $this->redirects;
|
$all_redirects = $this->redirects;
|
||||||
|
|
||||||
foreach ($redirects as $from => $to) {
|
foreach ($redirects as $from => $to) {
|
||||||
$already_redirected = array_search($from, $new_redirects);
|
$to_redirects_to = $all_redirects[$to] ?? $to;
|
||||||
$new_redirects[$already_redirected === false ? $from : $already_redirected] = $to;
|
$all_redirects[$from] = $to_redirects_to;
|
||||||
|
|
||||||
|
$redirects_to_from = array_keys($all_redirects, $from);
|
||||||
|
foreach ($redirects_to_from as $redirect_to_from)
|
||||||
|
$all_redirects[$redirect_to_from] = $to_redirects_to;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Redirects($new_redirects);
|
$new_object = new Redirects();
|
||||||
|
$new_object->redirects = $all_redirects;
|
||||||
|
return $new_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,7 +61,7 @@ class Redirects implements IteratorAggregate
|
||||||
*/
|
*/
|
||||||
public function resolve(string $from): string
|
public function resolve(string $from): string
|
||||||
{
|
{
|
||||||
return !isset($this->redirects[$from]) ? $from : $this->redirects[$from];
|
return $this->redirects[$from] ?? $from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -133,14 +133,14 @@ class Wikipedia
|
||||||
array_map(fn($it) => strval($it), array_column($response["normalized"] ?? [], "from")),
|
array_map(fn($it) => strval($it), array_column($response["normalized"] ?? [], "from")),
|
||||||
array_map(fn($it) => strval($it), array_column($response["normalized"] ?? [], "to"))
|
array_map(fn($it) => strval($it), array_column($response["normalized"] ?? [], "to"))
|
||||||
);
|
);
|
||||||
$redirects = $redirects->add_redirects($new_normalizations);
|
$redirects = $redirects->add($new_normalizations);
|
||||||
|
|
||||||
$new_redirects =
|
$new_redirects =
|
||||||
array_combine(
|
array_combine(
|
||||||
array_map(fn($it) => strval($it), array_column($response["redirects"] ?? [], "from")),
|
array_map(fn($it) => strval($it), array_column($response["redirects"] ?? [], "from")),
|
||||||
array_map(fn($it) => strval($it), array_column($response["redirects"] ?? [], "to"))
|
array_map(fn($it) => strval($it), array_column($response["redirects"] ?? [], "to"))
|
||||||
);
|
);
|
||||||
$redirects = $redirects->add_redirects($new_redirects);
|
$redirects = $redirects->add($new_redirects);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new QueryOutput($articles, $redirects, $missing);
|
return new QueryOutput($articles, $redirects, $missing);
|
||||||
|
@ -220,7 +220,7 @@ class Wikipedia
|
||||||
|
|
||||||
return new QueryOutput(
|
return new QueryOutput(
|
||||||
array_replace($output_base->results, $output_of_moves->results),
|
array_replace($output_base->results, $output_of_moves->results),
|
||||||
$output_base->redirects->add_redirects($moves),
|
$output_base->redirects->add($moves),
|
||||||
$not_moved
|
$not_moved
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace com\fwdekker\deathnotifier\wikipedia;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@see Redirects}.
|
||||||
|
*/
|
||||||
|
class RedirectsTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_resolve_returns_key_if_empty(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
|
||||||
|
self::assertEquals("does-not-exist", $redirects->resolve("does-not-exist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_resolve_returns_key_if_key_not_added(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add(["from" => "to"]);
|
||||||
|
|
||||||
|
self::assertEquals("does-not-exist", $redirects->resolve("does-not-exist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_resolve_returns_target_if_key_added(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add(["from" => "to"]);
|
||||||
|
|
||||||
|
self::assertEquals("to", $redirects->resolve("from"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_resolve_returns_new_target_if_key_overridden(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add(["from" => "to"]);
|
||||||
|
|
||||||
|
$redirects = $redirects->add(["from" => "new"]);
|
||||||
|
|
||||||
|
self::assertEquals("new", $redirects->resolve("from"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function test_add_does_not_alter_old_object(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add(["from" => "to"]);
|
||||||
|
|
||||||
|
$new_redirects = $redirects->add(["from" => "new"]);
|
||||||
|
|
||||||
|
self::assertEquals("to", $redirects->resolve("from"));
|
||||||
|
self::assertEquals("new", $new_redirects->resolve("from"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_add_inserts_new_key(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
|
||||||
|
$redirects = $redirects->add(["from" => "to"]);
|
||||||
|
|
||||||
|
self::assertEquals("to", $redirects->resolve("from"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_add_inserts_multiple_new_keys(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
|
||||||
|
$redirects = $redirects->add(["a" => "one", "b" => "two", "c" => "three"]);
|
||||||
|
|
||||||
|
self::assertEquals("one", $redirects->resolve("a"));
|
||||||
|
self::assertEquals("two", $redirects->resolve("b"));
|
||||||
|
self::assertEquals("three", $redirects->resolve("c"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_merges_ab_and_bc_to_ac(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
|
||||||
|
$redirects = $redirects->add([
|
||||||
|
"a" => "b",
|
||||||
|
"b" => "c",
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertEquals("c", $redirects->resolve("a"));
|
||||||
|
self::assertEquals("c", $redirects->resolve("b"));
|
||||||
|
self::assertEquals("c", $redirects->resolve("c"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_merges_bc_and_ab_to_ac(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
|
||||||
|
$redirects = $redirects->add([
|
||||||
|
"b" => "c",
|
||||||
|
"a" => "b",
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertEquals("c", $redirects->resolve("a"));
|
||||||
|
self::assertEquals("c", $redirects->resolve("b"));
|
||||||
|
self::assertEquals("c", $redirects->resolve("c"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_merges_ab_cd_and_bc_to_ad(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
|
||||||
|
$redirects = $redirects->add([
|
||||||
|
"a" => "b",
|
||||||
|
"c" => "d",
|
||||||
|
"b" => "c",
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertEquals("d", $redirects->resolve("a"));
|
||||||
|
self::assertEquals("d", $redirects->resolve("b"));
|
||||||
|
self::assertEquals("d", $redirects->resolve("c"));
|
||||||
|
self::assertEquals("d", $redirects->resolve("d"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function test_has_non_reflexive_redirects_is_false_if_empty(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
|
||||||
|
self::assertFalse($redirects->has_non_reflexive_redirects());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_has_non_reflexive_redirects_is_false_if_all_are_reflexive(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add(["a" => "a", "b" => "b", "c" => "c"]);
|
||||||
|
|
||||||
|
self::assertFalse($redirects->has_non_reflexive_redirects());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_has_non_reflexive_redirects_is_true_if_all_are_non_reflexive(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add(["a" => "one", "b" => "two", "c" => "three"]);
|
||||||
|
|
||||||
|
self::assertTrue($redirects->has_non_reflexive_redirects());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_has_non_reflexive_redirects_is_true_if_some_are_non_reflexive(): void
|
||||||
|
{
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add(["a" => "one", "self" => "self", "c" => "three"]);
|
||||||
|
|
||||||
|
self::assertTrue($redirects->has_non_reflexive_redirects());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function test_iterator_iterates_over_all_redirects(): void
|
||||||
|
{
|
||||||
|
$inserted = ["a" => "one", "b" => "two", "c" => "three"];
|
||||||
|
|
||||||
|
$redirects = new Redirects();
|
||||||
|
$redirects = $redirects->add($inserted);
|
||||||
|
|
||||||
|
$extracted = [];
|
||||||
|
foreach ($redirects as $from => $to)
|
||||||
|
$extracted[$from] = $to;
|
||||||
|
|
||||||
|
self::assertTrue($extracted === $inserted);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue