feat(changelog): Hook lit_mcp_changed
ChangeLog::record() löst nach jeder erfolgreichen oder teilweise gespeicherten Änderung lit_mcp_changed(ability, objectType, objectId, logId) aus, nie bei abgelehnten. Die Werte sind gekürzt wie im Protokoll; Ausnahmen von Listenern werden protokolliert und ändern das Ergebnis des Tools nicht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1a294b7881
commit
c383b99e7d
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\ChangeLog;
|
||||
|
||||
use Brain\Monkey\Actions;
|
||||
use Brain\Monkey\Functions;
|
||||
use LIT\MCP\ChangeLog\ChangeLog;
|
||||
use LIT\MCP\Server\RequestContext;
|
||||
@@ -67,6 +68,106 @@ final class ChangeLogTest extends TestCase
|
||||
unlink($log);
|
||||
}
|
||||
|
||||
private function dbInserting(int|false $outcome): \wpdb
|
||||
{
|
||||
Functions\when('current_time')->justReturn('2026-09-25 08:00:00');
|
||||
Functions\when('get_current_user_id')->justReturn(3);
|
||||
$db = Mockery::mock(\wpdb::class)->makePartial();
|
||||
$db->allows('insert')->andReturnUsing(static function () use ($db, $outcome): int|false {
|
||||
$db->insert_id = 77;
|
||||
|
||||
return $outcome;
|
||||
});
|
||||
|
||||
return $db;
|
||||
}
|
||||
|
||||
public function testAnOkEntryAnnouncesTheChange(): void
|
||||
{
|
||||
$seen = [];
|
||||
Actions\expectDone('lit_mcp_changed')->once()->whenHappen(static function (...$args) use (&$seen): void {
|
||||
$seen = $args;
|
||||
});
|
||||
|
||||
(new ChangeLog($this->dbInserting(1)))->record('lit/content-update', 'page', '12', 'x');
|
||||
|
||||
self::assertSame(['lit/content-update', 'page', '12', 77], $seen);
|
||||
}
|
||||
|
||||
public function testARefusedEntryDoesNotAnnounceAChange(): void
|
||||
{
|
||||
Actions\expectDone('lit_mcp_changed')->never();
|
||||
|
||||
(new ChangeLog($this->dbInserting(1)))->record('lit/x', 'page', '1', 'x', result: 'refused');
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
/** A partly executed change changed something: listeners (cache purge) must hear of it. */
|
||||
public function testAPartialEntryAnnouncesTheChange(): void
|
||||
{
|
||||
$seen = [];
|
||||
Actions\expectDone('lit_mcp_changed')->once()->whenHappen(static function (...$args) use (&$seen): void {
|
||||
$seen = $args;
|
||||
});
|
||||
|
||||
(new ChangeLog($this->dbInserting(1)))->record('lit/x', 'page', '1', 'x', result: 'partial');
|
||||
|
||||
self::assertSame(['lit/x', 'page', '1', 77], $seen);
|
||||
}
|
||||
|
||||
/** Listeners get the same shortened values as the log row (ability 100, type 50, id 191). */
|
||||
public function testTheAnnouncementCarriesTheShortenedValuesOfTheRow(): void
|
||||
{
|
||||
$seen = [];
|
||||
Actions\expectDone('lit_mcp_changed')->once()->whenHappen(static function (...$args) use (&$seen): void {
|
||||
$seen = $args;
|
||||
});
|
||||
|
||||
(new ChangeLog($this->dbInserting(1)))
|
||||
->record(str_repeat('a', 150), str_repeat('t', 80), str_repeat('ä', 300), 'x');
|
||||
|
||||
self::assertSame([str_repeat('a', 100), str_repeat('t', 50), str_repeat('ä', 191), 77], $seen);
|
||||
}
|
||||
|
||||
/** The write happened even when its log row did not: listeners (cache purge) still need to know. */
|
||||
public function testAFailedInsertStillAnnouncesTheChangeWithoutALogId(): void
|
||||
{
|
||||
$seen = [];
|
||||
Actions\expectDone('lit_mcp_changed')->once()->whenHappen(static function (...$args) use (&$seen): void {
|
||||
$seen = $args;
|
||||
});
|
||||
$previous = ini_set('error_log', (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log'));
|
||||
|
||||
try {
|
||||
$id = (new ChangeLog($this->dbInserting(false)))->record('lit/options', 'option', 'blogname', 'x');
|
||||
} finally {
|
||||
ini_set('error_log', (string) $previous);
|
||||
}
|
||||
|
||||
self::assertNull($id);
|
||||
self::assertSame(['lit/options', 'option', 'blogname', null], $seen);
|
||||
}
|
||||
|
||||
public function testAThrowingListenerChangesNothingAndIsLogged(): void
|
||||
{
|
||||
Actions\expectDone('lit_mcp_changed')->once()->whenHappen(static function (): void {
|
||||
throw new \RuntimeException('listener broke');
|
||||
});
|
||||
$log = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||
$previous = ini_set('error_log', $log);
|
||||
|
||||
try {
|
||||
$id = (new ChangeLog($this->dbInserting(1)))->record('lit/content-update', 'page', '12', 'x');
|
||||
} finally {
|
||||
ini_set('error_log', (string) $previous);
|
||||
}
|
||||
|
||||
self::assertSame(77, $id);
|
||||
self::assertStringContainsString('lit_mcp_changed failed', (string) file_get_contents($log));
|
||||
self::assertStringContainsString('listener broke', (string) file_get_contents($log));
|
||||
unlink($log);
|
||||
}
|
||||
|
||||
public function testRecordTruncatesObjectIdAndAbilityToTheColumnSizes(): void
|
||||
{
|
||||
Functions\when('current_time')->justReturn('2026-09-25 08:00:00');
|
||||
|
||||
Reference in New Issue
Block a user