fix: admin UI review round 1 (S47) - Art labels, usage only on success, result fallback
- Art column: only lit/media-delete and lit/divi-delete say "löscht", lit/post-trash "Papierkorb", other destructive tools "überschreibt". - ConnectionUsage records only responses with status < 400 (mcp-oauth answers auth failures with WP_REST_Response 401, not WP_Error). - Log result column: translated "unbekannt" instead of the raw value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,11 @@ use Brain\Monkey\Functions;
|
||||
use LIT\MCP\Admin\ConnectionUsage;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use WP_Error;
|
||||
use WP_HTTP_Response;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
require_once __DIR__ . '/Fixtures/HttpResponses.php';
|
||||
|
||||
final class ConnectionUsageTest extends TestCase
|
||||
{
|
||||
@@ -64,6 +68,37 @@ final class ConnectionUsageTest extends TestCase
|
||||
self::assertSame([], $this->recorded);
|
||||
}
|
||||
|
||||
/** @dataProvider failedStatuses */
|
||||
public function testDoesNotRecordRequestsAnsweredWithAnErrorStatus(int $status): void
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(7);
|
||||
$claims = ['sub' => 7, 'app_pass_id' => 'uuid-1'];
|
||||
// mcp-oauth answers auth failures with a WP_REST_Response (OAuthHttpTransport::build_auth_error_response).
|
||||
$rest = new WP_REST_Response(['error' => 'invalid_token'], $status);
|
||||
|
||||
self::assertSame($rest, $this->usage($claims)->record($rest, [], $this->request()));
|
||||
$this->usage($claims)->record(new WP_HTTP_Response(null, $status), [], $this->request());
|
||||
|
||||
self::assertSame([], $this->recorded);
|
||||
}
|
||||
|
||||
/** @return list<list<int>> */
|
||||
public static function failedStatuses(): array
|
||||
{
|
||||
return [[401], [403], [500]];
|
||||
}
|
||||
|
||||
public function testRecordsSuccessfulRestResponses(): void
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(7);
|
||||
$claims = ['sub' => 7, 'app_pass_id' => 'uuid-1'];
|
||||
|
||||
$this->usage($claims)->record(new WP_REST_Response([], 200), [], $this->request());
|
||||
$this->usage($claims)->record(new WP_REST_Response([], 202), [], $this->request());
|
||||
|
||||
self::assertSame([[7, 'uuid-1'], [7, 'uuid-1']], $this->recorded);
|
||||
}
|
||||
|
||||
public function testNeverBreaksTheResponseWhenDecodingOrRecordingFails(): void
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(7);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Minimal stand-ins for WordPress' response classes (only the status part), for ConnectionUsageTest.
|
||||
|
||||
if (! class_exists('WP_HTTP_Response')) {
|
||||
class WP_HTTP_Response
|
||||
{
|
||||
public function __construct(public mixed $data = null, public int $status = 200)
|
||||
{
|
||||
}
|
||||
|
||||
public function get_status(): int
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! class_exists('WP_REST_Response')) {
|
||||
class WP_REST_Response extends WP_HTTP_Response
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@ final class LogListTableTest extends TestCase
|
||||
|
||||
self::assertStringContainsString('dashicons-' . $icon, $html);
|
||||
self::assertStringContainsString($word, $html);
|
||||
self::assertStringNotContainsString('error', $html);
|
||||
}
|
||||
|
||||
/** @return list<list<string>> */
|
||||
@@ -226,7 +227,7 @@ final class LogListTableTest extends TestCase
|
||||
return [
|
||||
['ok', 'yes-alt', 'erfolgreich'],
|
||||
['partial', 'warning', 'teilweise'],
|
||||
['error<b>', 'dismiss', 'error<b>'],
|
||||
['error<b>', 'dismiss', 'unbekannt'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -255,16 +255,45 @@ final class ToolsTabTest extends TestCase
|
||||
$registry = new IntegrationRegistry($this->integration('media', [
|
||||
['lit/media-preview', true, true],
|
||||
['lit/media-upload', false, false],
|
||||
['lit/media-delete', false, false, true],
|
||||
]));
|
||||
|
||||
$html = $this->render([], $registry);
|
||||
|
||||
self::assertStringContainsString('<th scope="col" class="column-kind">Art</th>', $html);
|
||||
self::assertStringContainsString('liest', $this->row($html, 'lit/media-preview'));
|
||||
self::assertStringContainsString('schreibt', $this->row($html, 'lit/media-upload'));
|
||||
self::assertStringContainsString('löscht', $this->row($html, 'lit/media-delete'));
|
||||
self::assertStringNotContainsString('schreibt', $this->row($html, 'lit/media-delete'));
|
||||
self::assertStringContainsString('dashicons-visibility', $this->row($html, 'lit/media-preview'));
|
||||
self::assertStringContainsString('>liest<', $this->row($html, 'lit/media-preview'));
|
||||
self::assertStringContainsString('dashicons-edit', $this->row($html, 'lit/media-upload'));
|
||||
self::assertStringContainsString('>schreibt<', $this->row($html, 'lit/media-upload'));
|
||||
}
|
||||
|
||||
/**
|
||||
* "destructive" is a broad hint (deleting, replacing, swapping) – only explicit deletes say "löscht".
|
||||
*
|
||||
* @dataProvider destructiveAbilities
|
||||
*/
|
||||
public function testArtColumnForEveryDestructiveAbility(string $name, string $icon, string $word): void
|
||||
{
|
||||
$registry = new IntegrationRegistry($this->integration('wordpress', [[$name, false, false, true]]));
|
||||
|
||||
$row = $this->row($this->render([], $registry), $name);
|
||||
|
||||
self::assertStringContainsString('dashicons-' . $icon, $row);
|
||||
self::assertStringContainsString('>' . $word . '<', $row);
|
||||
}
|
||||
|
||||
/** @return list<list<string>> */
|
||||
public static function destructiveAbilities(): array
|
||||
{
|
||||
return [
|
||||
['lit/media-delete', 'trash', 'löscht'],
|
||||
['lit/divi-delete', 'trash', 'löscht'],
|
||||
['lit/post-trash', 'trash', 'Papierkorb'],
|
||||
['lit/post-replace-with-copy', 'update', 'überschreibt'],
|
||||
['lit/revision-restore', 'update', 'überschreibt'],
|
||||
['lit/menu-set-items', 'update', 'überschreibt'],
|
||||
['lit/menu-assign-location', 'update', 'überschreibt'],
|
||||
['lit/divi-replace-text', 'update', 'überschreibt'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testToolCellShowsLabelAndTheNameAsMutedTextWithALabelledSelect(): void
|
||||
|
||||
Reference in New Issue
Block a user