test: FakeChangeLog and SchemaFixture instead of repeated mocks (T2)
- tests/Support/FakeChangeLog extends ChangeLog: records record()/recordPartial() calls, increasing ids, optional failure; new FakeChangeLog(id) / partialsOnly() / any(id) / forbidden() mirror what the replaced mocks allowed. assertRecorded() / assertPartials() check count and the named arguments. Calls a fake does not allow throw and are also verified in TestCase::tearDown (one assertion per test that uses a fake), so swallowed change-log calls still fail like a never() expectation. - All ChangeLog mocks of ability/integration tests replaced (the admin tests that stub query/purge/clear keep their mocks); expectException tests that checked the partial log now catch and assert. - SchemaFixture::managed(?divi) replaces the 21 isManaged-only ContentSchema mocks; DiviFixtures::schema() uses it. Tests 1397 -> 1404 (FakeChangeLogTest); no test has fewer assertions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,6 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\Integrations\Divi\Design\DesignSystem;
|
use LIT\MCP\Integrations\Divi\Design\DesignSystem;
|
||||||
use LIT\MCP\Integrations\Divi\Schema\ModuleCatalog;
|
use LIT\MCP\Integrations\Divi\Schema\ModuleCatalog;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
use Mockery;
|
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,11 +40,7 @@ final class DiviFixtures
|
|||||||
|
|
||||||
public static function schema(bool $diviEnabled = true): ContentSchema
|
public static function schema(bool $diviEnabled = true): ContentSchema
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
return SchemaFixture::managed($diviEnabled);
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
$schema->allows('diviEnabled')->andReturn($diviEnabled);
|
|
||||||
|
|
||||||
return $schema;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shapes as GlobalData / GlobalPreset return them (Divi 5.2.1, verified in DDEV). */
|
/** Shapes as GlobalData / GlobalPreset return them (Divi 5.2.1, verified in DDEV). */
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace LIT\MCP\Tests\Support;
|
namespace LIT\MCP\Tests\Support;
|
||||||
|
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
||||||
use LIT\MCP\Integrations\Divi\Pages\DiviComposition;
|
use LIT\MCP\Integrations\Divi\Pages\DiviComposition;
|
||||||
use LIT\MCP\Integrations\Divi\Pages\DiviPageLoader;
|
use LIT\MCP\Integrations\Divi\Pages\DiviPageLoader;
|
||||||
@@ -83,13 +82,10 @@ final class DiviWriterFixture
|
|||||||
return $saver;
|
return $saver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A ChangeLog that accepts every record (log id 4711). */
|
/** A ChangeLog that accepts records (log ids from 4711). */
|
||||||
public static function log(): ChangeLog
|
public static function log(): FakeChangeLog
|
||||||
{
|
{
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
return new FakeChangeLog(4711);
|
||||||
$log->allows('record')->andReturn(4711);
|
|
||||||
|
|
||||||
return $log;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Tests\Support;
|
||||||
|
|
||||||
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
|
use LogicException;
|
||||||
|
use PHPUnit\Framework\Assert;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChangeLog without a database. Allowed calls are kept in $records / $partials (arguments by
|
||||||
|
* name); record() returns increasing ids from $firstId, or throws $failure after noting the call.
|
||||||
|
*
|
||||||
|
* What a fake allows mirrors the Mockery mocks it replaces:
|
||||||
|
* - new FakeChangeLog(id) record() only (allows('record')->andReturn(id))
|
||||||
|
* - FakeChangeLog::partialsOnly() recordPartial() only (expects('recordPartial'))
|
||||||
|
* - FakeChangeLog::any(id) both
|
||||||
|
* - FakeChangeLog::forbidden() nothing (a mock without expectations)
|
||||||
|
* A call that is not allowed throws – and, because the code under test often swallows
|
||||||
|
* change-log failures (FollowUp), it is also noted and fails the test in TestCase::tearDown()
|
||||||
|
* via verify().
|
||||||
|
*/
|
||||||
|
class FakeChangeLog extends ChangeLog
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var list<array{ability: string, object_type: string, object_id: string, summary: string,
|
||||||
|
* revision_id: ?int, snapshot_id: ?string, result: string}>
|
||||||
|
*/
|
||||||
|
public array $records = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var list<array{ability: string, object_type: string, object_id: string, summary: string,
|
||||||
|
* revision_id: ?int}>
|
||||||
|
*/
|
||||||
|
public array $partials = [];
|
||||||
|
|
||||||
|
/** @var list<string> Calls that were not allowed, across all fakes of the running test. */
|
||||||
|
private static array $unexpected = [];
|
||||||
|
|
||||||
|
private static bool $used = false;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private int $firstId = 1,
|
||||||
|
private ?Throwable $failure = null,
|
||||||
|
private bool $allowsRecord = true,
|
||||||
|
private bool $allowsPartial = false
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
self::$used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function forbidden(): self
|
||||||
|
{
|
||||||
|
return new self(1, null, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function partialsOnly(): self
|
||||||
|
{
|
||||||
|
return new self(1, null, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function any(int $firstId = 1): self
|
||||||
|
{
|
||||||
|
return new self($firstId, null, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails the running test if any fake got a call it does not allow; resets for the next test.
|
||||||
|
* Counts as one assertion in tests that created a fake (like a Mockery never() expectation).
|
||||||
|
*/
|
||||||
|
public static function verify(): void
|
||||||
|
{
|
||||||
|
$unexpected = self::$unexpected;
|
||||||
|
$used = self::$used;
|
||||||
|
self::$unexpected = [];
|
||||||
|
self::$used = false;
|
||||||
|
if ($used || $unexpected !== []) {
|
||||||
|
Assert::assertSame([], $unexpected, 'Unexpected change log calls: ' . implode('; ', $unexpected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function record(
|
||||||
|
string $ability,
|
||||||
|
string $objectType,
|
||||||
|
string $objectId,
|
||||||
|
string $summary,
|
||||||
|
?int $revisionId = null,
|
||||||
|
?string $snapshotId = null,
|
||||||
|
string $result = 'ok'
|
||||||
|
): ?int {
|
||||||
|
$this->refuseUnless($this->allowsRecord, sprintf('record(%s, %s, %s)', $ability, $objectType, $objectId));
|
||||||
|
$this->records[] = [
|
||||||
|
'ability' => $ability,
|
||||||
|
'object_type' => $objectType,
|
||||||
|
'object_id' => $objectId,
|
||||||
|
'summary' => $summary,
|
||||||
|
'revision_id' => $revisionId,
|
||||||
|
'snapshot_id' => $snapshotId,
|
||||||
|
'result' => $result,
|
||||||
|
];
|
||||||
|
if ($this->failure !== null) {
|
||||||
|
throw $this->failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->firstId + count($this->records) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recordPartial(
|
||||||
|
string $ability,
|
||||||
|
string $objectType,
|
||||||
|
string $objectId,
|
||||||
|
string $summary,
|
||||||
|
?int $revisionId = null
|
||||||
|
): void {
|
||||||
|
$this->refuseUnless(
|
||||||
|
$this->allowsPartial,
|
||||||
|
sprintf('recordPartial(%s, %s, %s)', $ability, $objectType, $objectId)
|
||||||
|
);
|
||||||
|
$this->partials[] = [
|
||||||
|
'ability' => $ability,
|
||||||
|
'object_type' => $objectType,
|
||||||
|
'object_id' => $objectId,
|
||||||
|
'summary' => $summary,
|
||||||
|
'revision_id' => $revisionId,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the record() calls: one $expected per call, in order; each names only the
|
||||||
|
* arguments it checks (ability, object_type, object_id, summary, revision_id, …).
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> ...$expected
|
||||||
|
*/
|
||||||
|
public function assertRecorded(array ...$expected): void
|
||||||
|
{
|
||||||
|
self::assertCalls('record()', $expected, $this->records);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the recordPartial() calls like assertRecorded().
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> ...$expected
|
||||||
|
*/
|
||||||
|
public function assertPartials(array ...$expected): void
|
||||||
|
{
|
||||||
|
self::assertCalls('recordPartial()', $expected, $this->partials);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<array<string, mixed>> $expected
|
||||||
|
* @param list<array<string, mixed>> $actual
|
||||||
|
*/
|
||||||
|
private static function assertCalls(string $method, array $expected, array $actual): void
|
||||||
|
{
|
||||||
|
Assert::assertCount(count($expected), $actual, 'Number of ChangeLog::' . $method . ' calls');
|
||||||
|
foreach ($expected as $i => $call) {
|
||||||
|
$checked = [];
|
||||||
|
foreach (array_keys($call) as $key) {
|
||||||
|
Assert::assertArrayHasKey($key, $actual[$i], 'Unknown ChangeLog argument');
|
||||||
|
$checked[$key] = $actual[$i][$key];
|
||||||
|
}
|
||||||
|
Assert::assertSame($call, $checked, 'ChangeLog::' . $method . ' call ' . $i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return list<string> The summaries of record(), in order. */
|
||||||
|
public function summaries(): array
|
||||||
|
{
|
||||||
|
return array_column($this->records, 'summary');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function refuseUnless(bool $allowed, string $call): void
|
||||||
|
{
|
||||||
|
if (! $allowed) {
|
||||||
|
self::$unexpected[] = $call;
|
||||||
|
throw new LogicException('Unexpected ChangeLog::' . $call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Tests\Support;
|
||||||
|
|
||||||
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
|
use Mockery;
|
||||||
|
use Mockery\MockInterface;
|
||||||
|
|
||||||
|
/** ContentSchema stand-ins for ability tests. */
|
||||||
|
final class SchemaFixture
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Every post type is managed. $divi answers diviEnabled(); null = the test does not expect
|
||||||
|
* that question (a call fails the test, as with a plain mock).
|
||||||
|
*
|
||||||
|
* @return ContentSchema&MockInterface
|
||||||
|
*/
|
||||||
|
public static function managed(?bool $divi = null): ContentSchema
|
||||||
|
{
|
||||||
|
$schema = Mockery::mock(ContentSchema::class);
|
||||||
|
$schema->allows('isManaged')->andReturn(true);
|
||||||
|
if ($divi !== null) {
|
||||||
|
$schema->allows('diviEnabled')->andReturn($divi);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi;
|
namespace LIT\MCP\Tests\Unit\Integrations\Divi;
|
||||||
|
|
||||||
use LIT\MCP\Abilities\Ability;
|
use LIT\MCP\Abilities\Ability;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\Divi\DiviIntegration;
|
use LIT\MCP\Integrations\Divi\DiviIntegration;
|
||||||
use LIT\MCP\Integrations\Divi\Pages\DiviContentComposer;
|
use LIT\MCP\Integrations\Divi\Pages\DiviContentComposer;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ final class DiviIntegrationTest extends TestCase
|
|||||||
|
|
||||||
public function testProvidesTheDiviAbilitiesOnlyWithDivi5(): void
|
public function testProvidesTheDiviAbilitiesOnlyWithDivi5(): void
|
||||||
{
|
{
|
||||||
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), Mockery::mock(ChangeLog::class));
|
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('divi', $integration->id());
|
self::assertSame('divi', $integration->id());
|
||||||
self::assertFalse($integration->isAvailable(), 'no Divi classes in unit tests');
|
self::assertFalse($integration->isAvailable(), 'no Divi classes in unit tests');
|
||||||
@@ -48,7 +48,7 @@ final class DiviIntegrationTest extends TestCase
|
|||||||
|
|
||||||
public function testGivesContentCreateTheDiviShortForm(): void
|
public function testGivesContentCreateTheDiviShortForm(): void
|
||||||
{
|
{
|
||||||
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), Mockery::mock(ChangeLog::class));
|
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$composer = $integration->contentComposer();
|
$composer = $integration->contentComposer();
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use LIT\MCP\Integrations\Divi\Pages\DiviWriter;
|
|||||||
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
|
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
|
||||||
use LIT\MCP\Tests\Support\DiviFixtures;
|
use LIT\MCP\Tests\Support\DiviFixtures;
|
||||||
use LIT\MCP\Tests\Support\DiviWriterFixture;
|
use LIT\MCP\Tests\Support\DiviWriterFixture;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
@@ -50,7 +51,7 @@ final class DiviWriterTest extends TestCase
|
|||||||
{
|
{
|
||||||
$content = DiviFixtures::markup('synthetic-basic.html');
|
$content = DiviFixtures::markup('synthetic-basic.html');
|
||||||
DiviFixtures::givenPost($content);
|
DiviFixtures::givenPost($content);
|
||||||
$writer = $this->writer(Mockery::mock(DiviSaver::class), Mockery::mock(ChangeLog::class));
|
$writer = $this->writer(Mockery::mock(DiviSaver::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertInstanceOf(DiviPage::class, $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]));
|
self::assertInstanceOf(DiviPage::class, $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]));
|
||||||
self::assertSame('lit_rev_mismatch', $writer->page(['post_id' => 7])->get_error_code());
|
self::assertSame('lit_rev_mismatch', $writer->page(['post_id' => 7])->get_error_code());
|
||||||
@@ -66,7 +67,7 @@ final class DiviWriterTest extends TestCase
|
|||||||
$writer = new DiviWriter(
|
$writer = new DiviWriter(
|
||||||
new DiviPageLoader(DiviFixtures::schema(false)),
|
new DiviPageLoader(DiviFixtures::schema(false)),
|
||||||
Mockery::mock(DiviSaver::class),
|
Mockery::mock(DiviSaver::class),
|
||||||
Mockery::mock(ChangeLog::class),
|
FakeChangeLog::forbidden(),
|
||||||
Mockery::mock(DesignSystemReader::class),
|
Mockery::mock(DesignSystemReader::class),
|
||||||
Mockery::mock(DiviComposition::class)
|
Mockery::mock(DiviComposition::class)
|
||||||
);
|
);
|
||||||
@@ -87,7 +88,7 @@ final class DiviWriterTest extends TestCase
|
|||||||
{
|
{
|
||||||
$content = DiviFixtures::markup('synthetic-basic.html');
|
$content = DiviFixtures::markup('synthetic-basic.html');
|
||||||
DiviFixtures::givenPost($content);
|
DiviFixtures::givenPost($content);
|
||||||
$writer = $this->writer(Mockery::mock(DiviSaver::class), Mockery::mock(ChangeLog::class));
|
$writer = $this->writer(Mockery::mock(DiviSaver::class), FakeChangeLog::forbidden());
|
||||||
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
||||||
|
|
||||||
$writer->composition($page)->placeholders();
|
$writer->composition($page)->placeholders();
|
||||||
@@ -101,7 +102,7 @@ final class DiviWriterTest extends TestCase
|
|||||||
DiviFixtures::givenPost($content);
|
DiviFixtures::givenPost($content);
|
||||||
$saver = Mockery::mock(DiviSaver::class);
|
$saver = Mockery::mock(DiviSaver::class);
|
||||||
$saver->expects('save')->never();
|
$saver->expects('save')->never();
|
||||||
$writer = $this->writer($saver, Mockery::mock(ChangeLog::class));
|
$writer = $this->writer($saver, FakeChangeLog::forbidden());
|
||||||
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
||||||
|
|
||||||
$result = $writer->save('lit/divi-update', $page, new DiviChange('nichts'), []);
|
$result = $writer->save('lit/divi-update', $page, new DiviChange('nichts'), []);
|
||||||
@@ -122,9 +123,7 @@ final class DiviWriterTest extends TestCase
|
|||||||
|
|
||||||
return ['revision_id' => 811, 'warnings' => ['vom Speichern']];
|
return ['revision_id' => 811, 'warnings' => ['vom Speichern']];
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(55);
|
||||||
$log->expects('record')->with('lit/divi-update', 'page', '7', 'Divi "Start": Button geändert', 811)
|
|
||||||
->andReturn(55);
|
|
||||||
$input = ['post_id' => 7, 'tree_rev' => Rev::of($content)];
|
$input = ['post_id' => 7, 'tree_rev' => Rev::of($content)];
|
||||||
Functions\expect('do_action')->once()->with('lit_mcp_after_save', 7, 'lit/divi-update', $input);
|
Functions\expect('do_action')->once()->with('lit_mcp_after_save', 7, 'lit/divi-update', $input);
|
||||||
Functions\when('get_preview_post_link')->justReturn('https://kunde.test/?page_id=7&preview=true');
|
Functions\when('get_preview_post_link')->justReturn('https://kunde.test/?page_id=7&preview=true');
|
||||||
@@ -158,6 +157,13 @@ final class DiviWriterTest extends TestCase
|
|||||||
self::assertContains('eigene Warnung', $result['warnings']);
|
self::assertContains('eigene Warnung', $result['warnings']);
|
||||||
self::assertContains('vom Speichern', $result['warnings']);
|
self::assertContains('vom Speichern', $result['warnings']);
|
||||||
self::assertSame(Rev::ofPost(DiviFixtures::givenPost($stored)), $result['rev']);
|
self::assertSame(Rev::ofPost(DiviFixtures::givenPost($stored)), $result['rev']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/divi-update',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '7',
|
||||||
|
'summary' => 'Divi "Start": Button geändert',
|
||||||
|
'revision_id' => 811,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailedRollbackIsLoggedAsPartial(): void
|
public function testFailedRollbackIsLoggedAsPartial(): void
|
||||||
@@ -166,14 +172,24 @@ final class DiviWriterTest extends TestCase
|
|||||||
DiviFixtures::givenPost($content);
|
DiviFixtures::givenPost($content);
|
||||||
$saver = Mockery::mock(DiviSaver::class);
|
$saver = Mockery::mock(DiviSaver::class);
|
||||||
$saver->allows('save')->andThrow(WriteFailed::partial(DiviSaver::REASON, postId: 7, revisionId: 811));
|
$saver->allows('save')->andThrow(WriteFailed::partial(DiviSaver::REASON, postId: 7, revisionId: 811));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->with('lit/divi-delete', 'page', '7', 'Divi "Start": Änderung nur teilweise zurückgenommen', 811);
|
|
||||||
$writer = $this->writer($saver, $log);
|
$writer = $this->writer($saver, $log);
|
||||||
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
||||||
$page->document->children()[0]->removeChild($page->index->find('s1'));
|
$page->document->children()[0]->removeChild($page->index->find('s1'));
|
||||||
|
|
||||||
$this->expectException(WriteFailed::class);
|
try {
|
||||||
$writer->save('lit/divi-delete', $page, new DiviChange('s1 gelöscht'), []);
|
$writer->save('lit/divi-delete', $page, new DiviChange('s1 gelöscht'), []);
|
||||||
|
self::fail('WriteFailed expected');
|
||||||
|
} catch (WriteFailed $e) {
|
||||||
|
self::assertFalse($e->rolledBack);
|
||||||
|
}
|
||||||
|
$log->assertPartials([
|
||||||
|
'ability' => 'lit/divi-delete',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '7',
|
||||||
|
'summary' => 'Divi "Start": Änderung nur teilweise zurückgenommen',
|
||||||
|
'revision_id' => 811,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCleanFailureIsNotLogged(): void
|
public function testCleanFailureIsNotLogged(): void
|
||||||
@@ -182,14 +198,18 @@ final class DiviWriterTest extends TestCase
|
|||||||
DiviFixtures::givenPost($content);
|
DiviFixtures::givenPost($content);
|
||||||
$saver = Mockery::mock(DiviSaver::class);
|
$saver = Mockery::mock(DiviSaver::class);
|
||||||
$saver->allows('save')->andThrow(WriteFailed::rolledBack(DiviSaver::REASON, postId: 7, revisionId: 811));
|
$saver->allows('save')->andThrow(WriteFailed::rolledBack(DiviSaver::REASON, postId: 7, revisionId: 811));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->expects('recordPartial')->never();
|
|
||||||
$log->expects('record')->never();
|
|
||||||
$writer = $this->writer($saver, $log);
|
$writer = $this->writer($saver, $log);
|
||||||
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
$page = $writer->page(['post_id' => 7, 'tree_rev' => Rev::of($content)]);
|
||||||
$page->document->children()[0]->removeChild($page->index->find('s1'));
|
$page->document->children()[0]->removeChild($page->index->find('s1'));
|
||||||
|
|
||||||
$this->expectException(WriteFailed::class);
|
try {
|
||||||
$writer->save('lit/divi-delete', $page, new DiviChange('s1 gelöscht'), []);
|
$writer->save('lit/divi-delete', $page, new DiviChange('s1 gelöscht'), []);
|
||||||
|
self::fail('WriteFailed expected');
|
||||||
|
} catch (WriteFailed $e) {
|
||||||
|
self::assertTrue($e->rolledBack);
|
||||||
|
}
|
||||||
|
$log->assertRecorded();
|
||||||
|
$log->assertPartials();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\Media\MediaDeleteAbility;
|
use LIT\MCP\Integrations\Media\MediaDeleteAbility;
|
||||||
use LIT\MCP\Integrations\Media\MediaUsageFinder;
|
use LIT\MCP\Integrations\Media\MediaUsageFinder;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -38,7 +39,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_delete_attachment')->never();
|
Functions\expect('wp_delete_attachment')->never();
|
||||||
|
|
||||||
$result = $this->ability(self::USAGE, Mockery::mock(ChangeLog::class))->execute(['id' => 33]);
|
$result = $this->ability(self::USAGE, FakeChangeLog::forbidden())->execute(['id' => 33]);
|
||||||
|
|
||||||
self::assertSame('lit_confirm_required', $result->get_error_code());
|
self::assertSame('lit_confirm_required', $result->get_error_code());
|
||||||
self::assertStringContainsString('endgültig', $result->get_error_message());
|
self::assertStringContainsString('endgültig', $result->get_error_message());
|
||||||
@@ -48,7 +49,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
|
|
||||||
public function testConfirmationMentionsTheTrashWhenMediaTrashIsOn(): void
|
public function testConfirmationMentionsTheTrashWhenMediaTrashIsOn(): void
|
||||||
{
|
{
|
||||||
$result = $this->ability([], Mockery::mock(ChangeLog::class), true)->execute(['id' => 33, 'confirm' => false]);
|
$result = $this->ability([], FakeChangeLog::forbidden(), true)->execute(['id' => 33, 'confirm' => false]);
|
||||||
|
|
||||||
self::assertSame('lit_confirm_required', $result->get_error_code());
|
self::assertSame('lit_confirm_required', $result->get_error_code());
|
||||||
self::assertStringContainsString('Papierkorb', $result->get_error_message());
|
self::assertStringContainsString('Papierkorb', $result->get_error_message());
|
||||||
@@ -58,7 +59,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_delete_attachment')->never();
|
Functions\expect('wp_delete_attachment')->never();
|
||||||
|
|
||||||
$result = $this->ability(self::USAGE, Mockery::mock(ChangeLog::class))->execute(['id' => 33, 'confirm' => true]);
|
$result = $this->ability(self::USAGE, FakeChangeLog::forbidden())->execute(['id' => 33, 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('lit_in_use', $result->get_error_code());
|
self::assertSame('lit_in_use', $result->get_error_code());
|
||||||
self::assertSame(self::USAGE, $result->get_error_data()['usages']);
|
self::assertSame(self::USAGE, $result->get_error_data()['usages']);
|
||||||
@@ -68,7 +69,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_delete_attachment')->never();
|
Functions\expect('wp_delete_attachment')->never();
|
||||||
|
|
||||||
$result = $this->ability(self::POSSIBLE, Mockery::mock(ChangeLog::class))->execute(['id' => 33, 'confirm' => true]);
|
$result = $this->ability(self::POSSIBLE, FakeChangeLog::forbidden())->execute(['id' => 33, 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('lit_in_use', $result->get_error_code());
|
self::assertSame('lit_in_use', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -76,25 +77,35 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
public function testForceDeletesUsedMedia(): void
|
public function testForceDeletesUsedMedia(): void
|
||||||
{
|
{
|
||||||
Functions\expect('wp_delete_attachment')->once()->with(33, false)->andReturn(new WP_Post(['ID' => 33]));
|
Functions\expect('wp_delete_attachment')->once()->with(33, false)->andReturn(new WP_Post(['ID' => 33]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(50);
|
||||||
$log->expects('record')->with('lit/media-delete', 'attachment', '33', 'Medium "Foto" gelöscht')->andReturn(50);
|
|
||||||
|
|
||||||
$result = $this->ability(self::USAGE, $log)->execute(['id' => 33, 'confirm' => true, 'force' => true]);
|
$result = $this->ability(self::USAGE, $log)->execute(['id' => 33, 'confirm' => true, 'force' => true]);
|
||||||
|
|
||||||
self::assertSame(['id' => '33', 'type' => 'attachment', 'action' => 'deleted', 'label' => 'Foto'], $result['changed'][0]);
|
self::assertSame(['id' => '33', 'type' => 'attachment', 'action' => 'deleted', 'label' => 'Foto'], $result['changed'][0]);
|
||||||
self::assertSame(50, $result['log_id']);
|
self::assertSame(50, $result['log_id']);
|
||||||
self::assertSame(self::USAGE, $result['usages']);
|
self::assertSame(self::USAGE, $result['usages']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/media-delete',
|
||||||
|
'object_type' => 'attachment',
|
||||||
|
'object_id' => '33',
|
||||||
|
'summary' => 'Medium "Foto" gelöscht',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnusedMediaGoesToTheTrashWhenMediaTrashIsOn(): void
|
public function testUnusedMediaGoesToTheTrashWhenMediaTrashIsOn(): void
|
||||||
{
|
{
|
||||||
Functions\expect('wp_delete_attachment')->once()->with(33, false)->andReturn(new WP_Post(['ID' => 33]));
|
Functions\expect('wp_delete_attachment')->once()->with(33, false)->andReturn(new WP_Post(['ID' => 33]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(51);
|
||||||
$log->expects('record')->with('lit/media-delete', 'attachment', '33', 'Medium "Foto" in den Papierkorb gelegt')->andReturn(51);
|
|
||||||
|
|
||||||
$result = $this->ability([], $log, true)->execute(['id' => 33, 'confirm' => true]);
|
$result = $this->ability([], $log, true)->execute(['id' => 33, 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('trashed', $result['changed'][0]['action']);
|
self::assertSame('trashed', $result['changed'][0]['action']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/media-delete',
|
||||||
|
'object_type' => 'attachment',
|
||||||
|
'object_id' => '33',
|
||||||
|
'summary' => 'Medium "Foto" in den Papierkorb gelegt',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRefusedDeleteWithTheAttachmentIntactIsAWriteFailure(): void
|
public function testRefusedDeleteWithTheAttachmentIntactIsAWriteFailure(): void
|
||||||
@@ -102,7 +113,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
Functions\when('get_post_meta')->justReturn('2026/09/foto.jpg');
|
Functions\when('get_post_meta')->justReturn('2026/09/foto.jpg');
|
||||||
Functions\expect('wp_delete_attachment')->once()->andReturn(false);
|
Functions\expect('wp_delete_attachment')->once()->andReturn(false);
|
||||||
|
|
||||||
$result = $this->ability([], Mockery::mock(ChangeLog::class))->execute(['id' => 33, 'confirm' => true]);
|
$result = $this->ability([], FakeChangeLog::forbidden())->execute(['id' => 33, 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('lit_write_failed', $result->get_error_code());
|
self::assertSame('lit_write_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -118,13 +129,13 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->once()->with('lit/media-delete', 'attachment', '33', Mockery::type('string'));
|
|
||||||
|
|
||||||
$result = $this->ability([], $log)->execute(['id' => 33, 'confirm' => true]);
|
$result = $this->ability([], $log)->execute(['id' => 33, 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('lit_partial_write', $result->get_error_code());
|
self::assertSame('lit_partial_write', $result->get_error_code());
|
||||||
self::assertSame(33, $result->get_error_data()['attachment_id']);
|
self::assertSame(33, $result->get_error_data()['attachment_id']);
|
||||||
|
$log->assertPartials(['ability' => 'lit/media-delete', 'object_type' => 'attachment', 'object_id' => '33']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testADatabaseErrorInTheUsageCheckRefusesWithoutForce(): void
|
public function testADatabaseErrorInTheUsageCheckRefusesWithoutForce(): void
|
||||||
@@ -138,7 +149,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
$db->allows('get_results')->andReturn(null);
|
$db->allows('get_results')->andReturn(null);
|
||||||
$ability = new MediaDeleteAbility(
|
$ability = new MediaDeleteAbility(
|
||||||
new MediaUsageFinder(static fn (): array => [], $db),
|
new MediaUsageFinder(static fn (): array => [], $db),
|
||||||
Mockery::mock(ChangeLog::class),
|
FakeChangeLog::forbidden(),
|
||||||
static fn (): bool => false
|
static fn (): bool => false
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -158,8 +169,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
public function testChangeLogFailureIsOnlyAWarning(): void
|
public function testChangeLogFailureIsOnlyAWarning(): void
|
||||||
{
|
{
|
||||||
Functions\expect('wp_delete_attachment')->once()->andReturn(new WP_Post(['ID' => 33]));
|
Functions\expect('wp_delete_attachment')->once()->andReturn(new WP_Post(['ID' => 33]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('db down'));
|
||||||
$log->expects('record')->andThrow(new RuntimeException('db down'));
|
|
||||||
|
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||||
$previous = ini_set('error_log', $file);
|
$previous = ini_set('error_log', $file);
|
||||||
@@ -173,6 +183,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
self::assertSame('deleted', $result['changed'][0]['action']);
|
self::assertSame('deleted', $result['changed'][0]['action']);
|
||||||
self::assertNull($result['log_id']);
|
self::assertNull($result['log_id']);
|
||||||
self::assertCount(1, $result['warnings']);
|
self::assertCount(1, $result['warnings']);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTrashedAttachmentIsRefused(): void
|
public function testTrashedAttachmentIsRefused(): void
|
||||||
@@ -180,7 +191,7 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 33, 'post_type' => 'attachment', 'post_status' => 'trash']));
|
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 33, 'post_type' => 'attachment', 'post_status' => 'trash']));
|
||||||
Functions\expect('wp_delete_attachment')->never();
|
Functions\expect('wp_delete_attachment')->never();
|
||||||
|
|
||||||
$ability = $this->ability([], Mockery::mock(ChangeLog::class));
|
$ability = $this->ability([], FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $ability->permission(['id' => 33])->get_error_code());
|
self::assertSame('lit_validation_failed', $ability->permission(['id' => 33])->get_error_code());
|
||||||
self::assertSame('lit_validation_failed', $ability->execute(['id' => 33, 'confirm' => true])->get_error_code());
|
self::assertSame('lit_validation_failed', $ability->execute(['id' => 33, 'confirm' => true])->get_error_code());
|
||||||
@@ -190,14 +201,14 @@ final class MediaDeleteAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('current_user_can')->alias(static fn (string $cap, int $id): bool => ! ($cap === 'delete_post' && $id === 33));
|
Functions\when('current_user_can')->alias(static fn (string $cap, int $id): bool => ! ($cap === 'delete_post' && $id === 33));
|
||||||
|
|
||||||
$result = $this->ability([], Mockery::mock(ChangeLog::class))->permission(['id' => 33]);
|
$result = $this->ability([], FakeChangeLog::forbidden())->permission(['id' => 33]);
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $result->get_error_code());
|
self::assertSame('lit_forbidden', $result->get_error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsDestructiveAndDescribesWhatTheUsageCheckMisses(): void
|
public function testIsDestructiveAndDescribesWhatTheUsageCheckMisses(): void
|
||||||
{
|
{
|
||||||
$ability = $this->ability([], Mockery::mock(ChangeLog::class));
|
$ability = $this->ability([], FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame(['readonly' => false, 'destructive' => true, 'idempotent' => false], $ability->annotations());
|
self::assertSame(['readonly' => false, 'destructive' => true, 'idempotent' => false], $ability->annotations());
|
||||||
self::assertFalse($ability->isDirect());
|
self::assertFalse($ability->isDirect());
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||||||
namespace LIT\MCP\Tests\Unit\Integrations\Media;
|
namespace LIT\MCP\Tests\Unit\Integrations\Media;
|
||||||
|
|
||||||
use LIT\MCP\Abilities\Ability;
|
use LIT\MCP\Abilities\Ability;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\Media\MediaIntegration;
|
use LIT\MCP\Integrations\Media\MediaIntegration;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
use LIT\MCP\Limits;
|
use LIT\MCP\Limits;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ final class MediaIntegrationTest extends TestCase
|
|||||||
|
|
||||||
public function testProvidesTheMediaAbilities(): void
|
public function testProvidesTheMediaAbilities(): void
|
||||||
{
|
{
|
||||||
$integration = new MediaIntegration(Mockery::mock(ContentSchema::class), Mockery::mock(ChangeLog::class), new Limits());
|
$integration = new MediaIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden(), new Limits());
|
||||||
|
|
||||||
self::assertSame('media', $integration->id());
|
self::assertSame('media', $integration->id());
|
||||||
self::assertTrue($integration->isAvailable());
|
self::assertTrue($integration->isAvailable());
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\Media\MediaUpdateAbility;
|
use LIT\MCP\Integrations\Media\MediaUpdateAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
@@ -53,25 +53,15 @@ final class MediaUpdateAbilityTest extends TestCase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function log(int ...$ids): ChangeLog
|
/** For paths that must not log. */
|
||||||
|
private function log(): ChangeLog
|
||||||
{
|
{
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
return FakeChangeLog::forbidden();
|
||||||
if ($ids !== []) {
|
|
||||||
$log->expects('record')->times(count($ids))->andReturn(...$ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $log;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUpdatesEveryItemAndReportsPerItem(): void
|
public function testUpdatesEveryItemAndReportsPerItem(): void
|
||||||
{
|
{
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(41);
|
||||||
$log->expects('record')
|
|
||||||
->with('lit/media-update', 'attachment', '33', 'Metadaten von "Foto" geändert: alt, caption')
|
|
||||||
->andReturn(41);
|
|
||||||
$log->expects('record')
|
|
||||||
->with('lit/media-update', 'attachment', '34', 'Metadaten von "Logo" geändert: title')
|
|
||||||
->andReturn(42);
|
|
||||||
|
|
||||||
$result = (new MediaUpdateAbility($log))->execute(['items' => [
|
$result = (new MediaUpdateAbility($log))->execute(['items' => [
|
||||||
['id' => 33, 'alt' => 'Team vor dem Haus', 'caption' => 'Unser <script>x</script>Team'],
|
['id' => 33, 'alt' => 'Team vor dem Haus', 'caption' => 'Unser <script>x</script>Team'],
|
||||||
@@ -84,6 +74,12 @@ final class MediaUpdateAbilityTest extends TestCase
|
|||||||
self::assertSame(42, $result['results'][1]['log_id']);
|
self::assertSame(42, $result['results'][1]['log_id']);
|
||||||
self::assertSame(['id' => '34', 'type' => 'attachment', 'action' => 'updated', 'label' => 'Firmenlogo'], $result['changed'][1]);
|
self::assertSame(['id' => '34', 'type' => 'attachment', 'action' => 'updated', 'label' => 'Firmenlogo'], $result['changed'][1]);
|
||||||
self::assertSame('2 von 2 Medien aktualisiert.', $result['summary']);
|
self::assertSame('2 von 2 Medien aktualisiert.', $result['summary']);
|
||||||
|
$log->assertRecorded(
|
||||||
|
['ability' => 'lit/media-update', 'object_type' => 'attachment', 'object_id' => '33',
|
||||||
|
'summary' => 'Metadaten von "Foto" geändert: alt, caption'],
|
||||||
|
['ability' => 'lit/media-update', 'object_type' => 'attachment', 'object_id' => '34',
|
||||||
|
'summary' => 'Metadaten von "Logo" geändert: title']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAnInvalidItemRefusesTheWholeBatchAndNamesEveryFailure(): void
|
public function testAnInvalidItemRefusesTheWholeBatchAndNamesEveryFailure(): void
|
||||||
@@ -226,8 +222,7 @@ final class MediaUpdateAbilityTest extends TestCase
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->once()->with('lit/media-update', 'attachment', '33', Mockery::type('string'));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
(new MediaUpdateAbility($log))->execute(['items' => [
|
(new MediaUpdateAbility($log))->execute(['items' => [
|
||||||
@@ -241,12 +236,12 @@ final class MediaUpdateAbilityTest extends TestCase
|
|||||||
self::assertSame([33], $error->get_error_data()['changed_ids']);
|
self::assertSame([33], $error->get_error_data()['changed_ids']);
|
||||||
self::assertStringContainsString('33', $error->get_error_message());
|
self::assertStringContainsString('33', $error->get_error_message());
|
||||||
}
|
}
|
||||||
|
$log->assertPartials(['ability' => 'lit/media-update', 'object_type' => 'attachment', 'object_id' => '33']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testChangeLogFailureIsOnlyAWarning(): void
|
public function testChangeLogFailureIsOnlyAWarning(): void
|
||||||
{
|
{
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('db down'));
|
||||||
$log->expects('record')->andThrow(new RuntimeException('db down'));
|
|
||||||
// error_log() is a PHP internal and cannot be stubbed; redirect it to a temp file instead.
|
// error_log() is a PHP internal and cannot be stubbed; redirect it to a temp file instead.
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||||
$previous = ini_set('error_log', $file);
|
$previous = ini_set('error_log', $file);
|
||||||
@@ -260,6 +255,7 @@ final class MediaUpdateAbilityTest extends TestCase
|
|||||||
self::assertTrue($result['results'][0]['ok']);
|
self::assertTrue($result['results'][0]['ok']);
|
||||||
self::assertNull($result['results'][0]['log_id']);
|
self::assertNull($result['results'][0]['log_id']);
|
||||||
self::assertCount(1, $result['warnings']);
|
self::assertCount(1, $result['warnings']);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsIdempotentIndirectAndNotDestructive(): void
|
public function testIsIdempotentIndirectAndNotDestructive(): void
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ use LIT\MCP\Integrations\Media\MediaPresenter;
|
|||||||
use LIT\MCP\Integrations\Media\MediaUploadAbility;
|
use LIT\MCP\Integrations\Media\MediaUploadAbility;
|
||||||
use LIT\MCP\Integrations\Media\MediaUploadFromUrlAbility;
|
use LIT\MCP\Integrations\Media\MediaUploadFromUrlAbility;
|
||||||
use LIT\MCP\Integrations\Media\MediaUploads;
|
use LIT\MCP\Integrations\Media\MediaUploads;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
||||||
use LIT\MCP\Limits;
|
use LIT\MCP\Limits;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -26,10 +27,9 @@ final class MediaUploadAbilitiesTest extends TestCase
|
|||||||
{
|
{
|
||||||
$presenter = Mockery::mock(MediaPresenter::class);
|
$presenter = Mockery::mock(MediaPresenter::class);
|
||||||
$presenter->allows('summary')->andReturn(['id' => 55, 'title' => 'Logo']);
|
$presenter->allows('summary')->andReturn(['id' => 55, 'title' => 'Logo']);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new MediaUploads($presenter, $log ?? Mockery::mock(ChangeLog::class), $schema);
|
return new MediaUploads($presenter, $log ?? FakeChangeLog::forbidden(), $schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function savedAttachment(): void
|
private function savedAttachment(): void
|
||||||
@@ -43,8 +43,7 @@ final class MediaUploadAbilitiesTest extends TestCase
|
|||||||
$this->savedAttachment();
|
$this->savedAttachment();
|
||||||
$importer = Mockery::mock(MediaImporter::class);
|
$importer = Mockery::mock(MediaImporter::class);
|
||||||
$importer->expects('fromUrl')->with('https://example.com/logo.png', null, ['alt' => 'Logo'])->andReturn(55);
|
$importer->expects('fromUrl')->with('https://example.com/logo.png', null, ['alt' => 'Logo'])->andReturn(55);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(3);
|
||||||
$log->expects('record')->with('lit/media-upload-from-url', 'attachment', '55', Mockery::type('string'))->andReturn(3);
|
|
||||||
|
|
||||||
$result = (new MediaUploadFromUrlAbility($importer, $this->uploads($log)))
|
$result = (new MediaUploadFromUrlAbility($importer, $this->uploads($log)))
|
||||||
->execute(['url' => 'https://example.com/logo.png', 'alt' => 'Logo']);
|
->execute(['url' => 'https://example.com/logo.png', 'alt' => 'Logo']);
|
||||||
@@ -54,6 +53,11 @@ final class MediaUploadAbilitiesTest extends TestCase
|
|||||||
self::assertSame(3, $result['log_id']);
|
self::assertSame(3, $result['log_id']);
|
||||||
self::assertSame('https://kunde.de/wp-admin/post.php?post=55&action=edit', $result['edit_url']);
|
self::assertSame('https://kunde.de/wp-admin/post.php?post=55&action=edit', $result['edit_url']);
|
||||||
self::assertSame([], $result['warnings']);
|
self::assertSame([], $result['warnings']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/media-upload-from-url',
|
||||||
|
'object_type' => 'attachment',
|
||||||
|
'object_id' => '55',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBase64UploadPassesFilenameDataAndMeta(): void
|
public function testBase64UploadPassesFilenameDataAndMeta(): void
|
||||||
@@ -61,14 +65,14 @@ final class MediaUploadAbilitiesTest extends TestCase
|
|||||||
$this->savedAttachment();
|
$this->savedAttachment();
|
||||||
$importer = Mockery::mock(MediaImporter::class);
|
$importer = Mockery::mock(MediaImporter::class);
|
||||||
$importer->expects('fromBase64')->with('AAAA', 'x.png', ['title' => 'Logo', 'post_id' => 5])->andReturn(55);
|
$importer->expects('fromBase64')->with('AAAA', 'x.png', ['title' => 'Logo', 'post_id' => 5])->andReturn(55);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(4);
|
||||||
$log->expects('record')->with('lit/media-upload', 'attachment', '55', Mockery::type('string'))->andReturn(4);
|
|
||||||
|
|
||||||
$result = (new MediaUploadAbility($importer, $this->uploads($log), new Limits()))
|
$result = (new MediaUploadAbility($importer, $this->uploads($log), new Limits()))
|
||||||
->execute(['filename' => 'x.png', 'data' => 'AAAA', 'title' => 'Logo', 'post_id' => 5]);
|
->execute(['filename' => 'x.png', 'data' => 'AAAA', 'title' => 'Logo', 'post_id' => 5]);
|
||||||
|
|
||||||
self::assertTrue($result['ok']);
|
self::assertTrue($result['ok']);
|
||||||
self::assertSame(4, $result['log_id']);
|
self::assertSame(4, $result['log_id']);
|
||||||
|
$log->assertRecorded(['ability' => 'lit/media-upload', 'object_type' => 'attachment', 'object_id' => '55']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAFailingChangeLogIsOnlyAWarning(): void
|
public function testAFailingChangeLogIsOnlyAWarning(): void
|
||||||
@@ -76,8 +80,7 @@ final class MediaUploadAbilitiesTest extends TestCase
|
|||||||
$this->savedAttachment();
|
$this->savedAttachment();
|
||||||
$importer = Mockery::mock(MediaImporter::class);
|
$importer = Mockery::mock(MediaImporter::class);
|
||||||
$importer->allows('fromUrl')->andReturn(55);
|
$importer->allows('fromUrl')->andReturn(55);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('db down'));
|
||||||
$log->allows('record')->andThrow(new RuntimeException('db down'));
|
|
||||||
// error_log() is a PHP internal and cannot be stubbed; redirect it to a temp file instead.
|
// error_log() is a PHP internal and cannot be stubbed; redirect it to a temp file instead.
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit-log');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit-log');
|
||||||
$previous = ini_set('error_log', $file);
|
$previous = ini_set('error_log', $file);
|
||||||
@@ -111,11 +114,19 @@ final class MediaUploadAbilitiesTest extends TestCase
|
|||||||
$failure = WriteFailed::partial('Alt-Text fehlt.', postId: 55);
|
$failure = WriteFailed::partial('Alt-Text fehlt.', postId: 55);
|
||||||
$importer = Mockery::mock(MediaImporter::class);
|
$importer = Mockery::mock(MediaImporter::class);
|
||||||
$importer->allows('fromUrl')->andThrow($failure);
|
$importer->allows('fromUrl')->andThrow($failure);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->with('lit/media-upload-from-url', 'attachment', '55', Mockery::type('string'));
|
|
||||||
|
|
||||||
$this->expectExceptionObject($failure);
|
try {
|
||||||
(new MediaUploadFromUrlAbility($importer, $this->uploads($log)))->execute(['url' => 'https://example.com/a.png']);
|
(new MediaUploadFromUrlAbility($importer, $this->uploads($log)))->execute(['url' => 'https://example.com/a.png']);
|
||||||
|
self::fail('WriteFailed expected');
|
||||||
|
} catch (WriteFailed $e) {
|
||||||
|
self::assertSame($failure, $e);
|
||||||
|
}
|
||||||
|
$log->assertPartials([
|
||||||
|
'ability' => 'lit/media-upload-from-url',
|
||||||
|
'object_type' => 'attachment',
|
||||||
|
'object_id' => '55',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPermissionNeedsUploadRightsAndEditRightsOnTheParent(): void
|
public function testPermissionNeedsUploadRightsAndEditRightsOnTheParent(): void
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ use LIT\MCP\ChangeLog\ChangeLog;
|
|||||||
use LIT\MCP\Integrations\WordPress\Content\ContentCreateAbility;
|
use LIT\MCP\Integrations\WordPress\Content\ContentCreateAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use Mockery\MockInterface;
|
use Mockery\MockInterface;
|
||||||
@@ -27,8 +29,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['create_posts' => 'edit_pages']]);
|
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['create_posts' => 'edit_pages']]);
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
@@ -72,7 +73,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
$writer->allows('prepare')->andReturn(Errors::invalidInput('ungültig', ['fields.role' => 'Pflichtfeld fehlt.']));
|
$writer->allows('prepare')->andReturn(Errors::invalidInput('ungültig', ['fields.role' => 'Pflichtfeld fehlt.']));
|
||||||
Functions\expect('wp_insert_post')->never();
|
Functions\expect('wp_insert_post')->never();
|
||||||
|
|
||||||
$result = (new ContentCreateAbility($this->schema(), $writer, Mockery::mock(ChangeLog::class)))
|
$result = (new ContentCreateAbility($this->schema(), $writer, FakeChangeLog::forbidden()))
|
||||||
->execute(['post_type' => 'lit_person', 'title' => 'Anna']);
|
->execute(['post_type' => 'lit_person', 'title' => 'Anna']);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
@@ -97,8 +98,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
], true)->andReturn(42);
|
], true)->andReturn(42);
|
||||||
Actions\expectDone('lit_mcp_after_save')->once()->with(42, 'lit/content-create', $input);
|
Actions\expectDone('lit_mcp_after_save')->once()->with(42, 'lit/content-create', $input);
|
||||||
$post = $this->stubResultLinks();
|
$post = $this->stubResultLinks();
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(7);
|
||||||
$log->expects('record')->with('lit/content-create', 'lit_person', '42', 'Entwurf "Anna" angelegt', null)->andReturn(7);
|
|
||||||
|
|
||||||
$result = (new ContentCreateAbility($schema, $writer, $log))->execute($input);
|
$result = (new ContentCreateAbility($schema, $writer, $log))->execute($input);
|
||||||
|
|
||||||
@@ -108,6 +108,13 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
self::assertSame(['w'], $result['warnings']);
|
self::assertSame(['w'], $result['warnings']);
|
||||||
self::assertStringContainsString('Entwurf', $result['summary']);
|
self::assertStringContainsString('Entwurf', $result['summary']);
|
||||||
self::assertSame(Rev::ofPost($post), $result['rev']);
|
self::assertSame(Rev::ofPost($post), $result['rev']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/content-create',
|
||||||
|
'object_type' => 'lit_person',
|
||||||
|
'object_id' => '42',
|
||||||
|
'summary' => 'Entwurf "Anna" angelegt',
|
||||||
|
'revision_id' => null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailingExtrasDeleteCreatedTermsAndTheNewDraft(): void
|
public function testFailingExtrasDeleteCreatedTermsAndTheNewDraft(): void
|
||||||
@@ -118,8 +125,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
Functions\when('wp_insert_post')->justReturn(42);
|
Functions\when('wp_insert_post')->justReturn(42);
|
||||||
Functions\expect('wp_delete_post')->once()->with(42, true)->andReturn(new WP_Post(['ID' => 42]));
|
Functions\expect('wp_delete_post')->once()->with(42, true)->andReturn(new WP_Post(['ID' => 42]));
|
||||||
Actions\expectDone('lit_mcp_after_save')->never();
|
Actions\expectDone('lit_mcp_after_save')->never();
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->expects('record')->never();
|
|
||||||
|
|
||||||
$failure = $this->execute($writer, $log);
|
$failure = $this->execute($writer, $log);
|
||||||
|
|
||||||
@@ -139,8 +145,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
Functions\when('get_current_user_id')->justReturn(3);
|
Functions\when('get_current_user_id')->justReturn(3);
|
||||||
Functions\when('wp_insert_post')->justReturn(42);
|
Functions\when('wp_insert_post')->justReturn(42);
|
||||||
Functions\expect('wp_delete_post')->once()->with(42, true)->andReturn(new WP_Post(['ID' => 42]));
|
Functions\expect('wp_delete_post')->once()->with(42, true)->andReturn(new WP_Post(['ID' => 42]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->expects('record')->never();
|
|
||||||
|
|
||||||
$result = (new ContentCreateAbility($this->schema(), $writer, $log))->execute(self::INPUT);
|
$result = (new ContentCreateAbility($this->schema(), $writer, $log))->execute(self::INPUT);
|
||||||
|
|
||||||
@@ -157,8 +162,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
Functions\when('get_current_user_id')->justReturn(3);
|
Functions\when('get_current_user_id')->justReturn(3);
|
||||||
Functions\when('wp_insert_post')->justReturn(42);
|
Functions\when('wp_insert_post')->justReturn(42);
|
||||||
Functions\when('wp_delete_post')->justReturn(false);
|
Functions\when('wp_delete_post')->justReturn(false);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
(new ContentCreateAbility($this->schema(), $writer, $log))->execute(self::INPUT);
|
(new ContentCreateAbility($this->schema(), $writer, $log))->execute(self::INPUT);
|
||||||
@@ -167,6 +171,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
self::assertFalse($e->rolledBack);
|
self::assertFalse($e->rolledBack);
|
||||||
self::assertSame(42, $e->postId);
|
self::assertSame(42, $e->postId);
|
||||||
}
|
}
|
||||||
|
$log->assertPartials([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testADraftThatCannotBeDeletedIsAPartialWriteAndLogged(): void
|
public function testADraftThatCannotBeDeletedIsAPartialWriteAndLogged(): void
|
||||||
@@ -176,16 +181,14 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
$writer->expects('deleteCreatedTerms');
|
$writer->expects('deleteCreatedTerms');
|
||||||
Functions\when('wp_insert_post')->justReturn(42);
|
Functions\when('wp_insert_post')->justReturn(42);
|
||||||
Functions\when('wp_delete_post')->justReturn(false);
|
Functions\when('wp_delete_post')->justReturn(false);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')
|
|
||||||
->with('lit/content-create', 'lit_person', '42', Mockery::type('string'))
|
|
||||||
->andReturn(9);
|
|
||||||
|
|
||||||
$failure = $this->execute($writer, $log);
|
$failure = $this->execute($writer, $log);
|
||||||
|
|
||||||
self::assertFalse($failure->rolledBack);
|
self::assertFalse($failure->rolledBack);
|
||||||
self::assertSame(42, $failure->postId);
|
self::assertSame(42, $failure->postId);
|
||||||
self::assertStringContainsString('Eintrag 42', $failure->toError()->get_error_message());
|
self::assertStringContainsString('Eintrag 42', $failure->toError()->get_error_message());
|
||||||
|
$log->assertPartials(['ability' => 'lit/content-create', 'object_type' => 'lit_person', 'object_id' => '42']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLeftoverCreatedTermsMakeItAPartialWrite(): void
|
public function testLeftoverCreatedTermsMakeItAPartialWrite(): void
|
||||||
@@ -195,13 +198,13 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
$writer->expects('deleteCreatedTerms')->andThrow(new RuntimeException('term 15 bleibt'));
|
$writer->expects('deleteCreatedTerms')->andThrow(new RuntimeException('term 15 bleibt'));
|
||||||
Functions\when('wp_insert_post')->justReturn(42);
|
Functions\when('wp_insert_post')->justReturn(42);
|
||||||
Functions\when('wp_delete_post')->justReturn(new WP_Post(['ID' => 42]));
|
Functions\when('wp_delete_post')->justReturn(new WP_Post(['ID' => 42]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial');
|
|
||||||
|
|
||||||
$failure = $this->execute($writer, $log);
|
$failure = $this->execute($writer, $log);
|
||||||
|
|
||||||
self::assertFalse($failure->rolledBack);
|
self::assertFalse($failure->rolledBack);
|
||||||
self::assertStringContainsString('term 15 bleibt', $failure->getMessage());
|
self::assertStringContainsString('term 15 bleibt', $failure->getMessage());
|
||||||
|
$log->assertPartials([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAnInsertThatThrowsIsAPartialWriteWithoutId(): void
|
public function testAnInsertThatThrowsIsAPartialWriteWithoutId(): void
|
||||||
@@ -210,16 +213,14 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
Functions\when('wp_insert_post')->alias(static function (): never {
|
Functions\when('wp_insert_post')->alias(static function (): never {
|
||||||
throw new RuntimeException('save_post hook kaputt');
|
throw new RuntimeException('save_post hook kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')
|
|
||||||
->with('lit/content-create', 'lit_person', '', Mockery::type('string'))
|
|
||||||
->andReturn(9);
|
|
||||||
|
|
||||||
$failure = $this->execute($writer, $log);
|
$failure = $this->execute($writer, $log);
|
||||||
|
|
||||||
self::assertFalse($failure->rolledBack);
|
self::assertFalse($failure->rolledBack);
|
||||||
self::assertNull($failure->postId);
|
self::assertNull($failure->postId);
|
||||||
self::assertSame('lit_partial_write', $failure->toError()->get_error_code());
|
self::assertSame('lit_partial_write', $failure->toError()->get_error_code());
|
||||||
|
$log->assertPartials(['ability' => 'lit/content-create', 'object_type' => 'lit_person', 'object_id' => '']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailuresAfterTheWriteOnlyAddWarnings(): void
|
public function testFailuresAfterTheWriteOnlyAddWarnings(): void
|
||||||
@@ -231,8 +232,7 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
Actions\expectDone('lit_mcp_after_save')->once()->whenHappen(static function (): void {
|
Actions\expectDone('lit_mcp_after_save')->once()->whenHappen(static function (): void {
|
||||||
throw new RuntimeException('listener kaputt');
|
throw new RuntimeException('listener kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('db weg'));
|
||||||
$log->expects('record')->andThrow(new RuntimeException('db weg'));
|
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||||
$previous = ini_set('error_log', $file);
|
$previous = ini_set('error_log', $file);
|
||||||
|
|
||||||
@@ -248,12 +248,13 @@ final class ContentCreateAbilityTest extends TestCase
|
|||||||
self::assertStringContainsString('listener kaputt', (string) file_get_contents($file));
|
self::assertStringContainsString('listener kaputt', (string) file_get_contents($file));
|
||||||
self::assertStringContainsString('db weg', (string) file_get_contents($file));
|
self::assertStringContainsString('db weg', (string) file_get_contents($file));
|
||||||
unlink($file);
|
unlink($file);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testStatusIsNotAnInputAndPermissionNeedsCreateRights(): void
|
public function testStatusIsNotAnInputAndPermissionNeedsCreateRights(): void
|
||||||
{
|
{
|
||||||
$schema = $this->schema();
|
$schema = $this->schema();
|
||||||
$ability = new ContentCreateAbility($schema, Mockery::mock(PostWriter::class), Mockery::mock(ChangeLog::class));
|
$ability = new ContentCreateAbility($schema, Mockery::mock(PostWriter::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertArrayNotHasKey('status', $ability->inputSchema()['properties']);
|
self::assertArrayNotHasKey('status', $ability->inputSchema()['properties']);
|
||||||
self::assertFalse($ability->inputSchema()['additionalProperties']);
|
self::assertFalse($ability->inputSchema()['additionalProperties']);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ use LIT\MCP\Integrations\WordPress\Content\ContentCreateAbility;
|
|||||||
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
use LIT\MCP\Tests\Support\DiviWriterFixture;
|
use LIT\MCP\Tests\Support\DiviWriterFixture;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use Mockery\MockInterface;
|
use Mockery\MockInterface;
|
||||||
@@ -34,9 +36,7 @@ final class ContentCreateDiviTest extends TestCase
|
|||||||
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['create_posts' => 'edit_pages']]);
|
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['create_posts' => 'edit_pages']]);
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
Functions\when('get_current_user_id')->justReturn(3);
|
Functions\when('get_current_user_id')->justReturn(3);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed(true);
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
$schema->allows('diviEnabled')->andReturn(true);
|
|
||||||
|
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
@@ -66,9 +66,7 @@ final class ContentCreateDiviTest extends TestCase
|
|||||||
|
|
||||||
private function log(): ChangeLog
|
private function log(): ChangeLog
|
||||||
{
|
{
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::any(9);
|
||||||
$log->allows('record')->andReturn(9);
|
|
||||||
$log->allows('recordPartial');
|
|
||||||
|
|
||||||
return $log;
|
return $log;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\Integrations\WordPress\Content\ContentGetAbility;
|
use LIT\MCP\Integrations\WordPress\Content\ContentGetAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
@@ -19,8 +20,7 @@ final class ContentGetAbilityTest extends TestCase
|
|||||||
$post = new WP_Post(['ID' => 5, 'post_type' => 'page']);
|
$post = new WP_Post(['ID' => 5, 'post_type' => 'page']);
|
||||||
Functions\when('get_post')->justReturn($post);
|
Functions\when('get_post')->justReturn($post);
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
$presenter = Mockery::mock(PostPresenter::class);
|
$presenter = Mockery::mock(PostPresenter::class);
|
||||||
$presenter->expects('full')->with($post)->andReturn(['id' => 5]);
|
$presenter->expects('full')->with($post)->andReturn(['id' => 5]);
|
||||||
$ability = new ContentGetAbility($schema, $presenter);
|
$ability = new ContentGetAbility($schema, $presenter);
|
||||||
@@ -34,8 +34,7 @@ final class ContentGetAbilityTest extends TestCase
|
|||||||
$post = new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_status' => 'trash']);
|
$post = new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_status' => 'trash']);
|
||||||
Functions\when('get_post')->justReturn($post);
|
Functions\when('get_post')->justReturn($post);
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
$presenter = Mockery::mock(PostPresenter::class);
|
$presenter = Mockery::mock(PostPresenter::class);
|
||||||
$presenter->expects('full')->with($post)->andReturn(['id' => 5]);
|
$presenter->expects('full')->with($post)->andReturn(['id' => 5]);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use LIT\MCP\Integrations\WordPress\Content\ContentQueryAbility;
|
|||||||
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\FieldDefinition;
|
use LIT\MCP\Integrations\WordPress\Schema\FieldDefinition;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
@@ -241,8 +242,7 @@ final class ContentQueryAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_posts']]);
|
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_posts']]);
|
||||||
Functions\when('current_user_can')->justReturn(false);
|
Functions\when('current_user_can')->justReturn(false);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
$ability = new ContentQueryAbility($schema, Mockery::mock(PostPresenter::class));
|
$ability = new ContentQueryAbility($schema, Mockery::mock(PostPresenter::class));
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $ability->permission(['post_type' => 'page'])->get_error_code());
|
self::assertSame('lit_forbidden', $ability->permission(['post_type' => 'page'])->get_error_code());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Content;
|
|||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Integrations\WordPress\Content\ContentTypesAbility;
|
use LIT\MCP\Integrations\WordPress\Content\ContentTypesAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
@@ -89,8 +90,7 @@ final class ContentTypesAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_pages']]);
|
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_pages']]);
|
||||||
Functions\when('current_user_can')->justReturn(false);
|
Functions\when('current_user_can')->justReturn(false);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
$result = (new ContentTypesAbility($schema))->execute(['post_type' => 'page']);
|
$result = (new ContentTypesAbility($schema))->execute(['post_type' => 'page']);
|
||||||
|
|
||||||
@@ -110,8 +110,7 @@ final class ContentTypesAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'edit_posts');
|
Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'edit_posts');
|
||||||
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_pages']]);
|
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_pages']]);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
$result = (new ContentTypesAbility($schema))->permission(['post_type' => 'lit_person']);
|
$result = (new ContentTypesAbility($schema))->permission(['post_type' => 'lit_person']);
|
||||||
|
|
||||||
@@ -123,8 +122,7 @@ final class ContentTypesAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_posts']]);
|
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['edit_posts' => 'edit_posts']]);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
self::assertTrue((new ContentTypesAbility($schema))->permission(['post_type' => 'page']));
|
self::assertTrue((new ContentTypesAbility($schema))->permission(['post_type' => 'page']));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ use LIT\MCP\Integrations\WordPress\Content\ContentUpdateAbility;
|
|||||||
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
||||||
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -39,15 +41,14 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
|
|
||||||
private function schema(): ContentSchema
|
private function schema(): ContentSchema
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function ability(PostWriter $writer, ?ChangeLog $log = null): ContentUpdateAbility
|
private function ability(PostWriter $writer, ?ChangeLog $log = null): ContentUpdateAbility
|
||||||
{
|
{
|
||||||
return new ContentUpdateAbility($this->schema(), $writer, $log ?? Mockery::mock(ChangeLog::class));
|
return new ContentUpdateAbility($this->schema(), $writer, $log ?? FakeChangeLog::forbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRevIsRequired(): void
|
public function testRevIsRequired(): void
|
||||||
@@ -155,8 +156,7 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
Actions\expectDone('lit_mcp_after_save')->once()->with(5, 'lit/content-update', $input);
|
Actions\expectDone('lit_mcp_after_save')->once()->with(5, 'lit/content-update', $input);
|
||||||
Functions\when('get_preview_post_link')->justReturn('https://kunde.de/team/');
|
Functions\when('get_preview_post_link')->justReturn('https://kunde.de/team/');
|
||||||
Functions\when('get_edit_post_link')->justReturn('https://kunde.de/wp-admin/post.php?post=5&action=edit');
|
Functions\when('get_edit_post_link')->justReturn('https://kunde.de/wp-admin/post.php?post=5&action=edit');
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(4711);
|
||||||
$log->expects('record')->with('lit/content-update', 'page', '5', Mockery::type('string'), 811)->andReturn(4711);
|
|
||||||
|
|
||||||
$result = $this->ability($writer, $log)->execute($input);
|
$result = $this->ability($writer, $log)->execute($input);
|
||||||
|
|
||||||
@@ -166,6 +166,12 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
self::assertSame('updated', $result['changed'][0]['action']);
|
self::assertSame('updated', $result['changed'][0]['action']);
|
||||||
self::assertSame(Rev::ofPost($this->post), $result['rev']);
|
self::assertSame(Rev::ofPost($this->post), $result['rev']);
|
||||||
self::assertStringContainsString('Status unverändert: publish', $result['summary']);
|
self::assertStringContainsString('Status unverändert: publish', $result['summary']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/content-update',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'revision_id' => 811,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -197,8 +203,7 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
private function executeExpectingFailure(PostWriter $writer, array $input, ?ChangeLog $log = null): WriteFailed
|
private function executeExpectingFailure(PostWriter $writer, array $input, ?ChangeLog $log = null): WriteFailed
|
||||||
{
|
{
|
||||||
if ($log === null) {
|
if ($log === null) {
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->expects('record')->never();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -298,14 +303,14 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
});
|
});
|
||||||
Functions\when('wp_revisions_enabled')->justReturn(true);
|
Functions\when('wp_revisions_enabled')->justReturn(true);
|
||||||
Functions\expect('wp_restore_post_revision')->never();
|
Functions\expect('wp_restore_post_revision')->never();
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial');
|
|
||||||
|
|
||||||
$failure = $this->executeExpectingFailure($writer, $input, $log);
|
$failure = $this->executeExpectingFailure($writer, $input, $log);
|
||||||
|
|
||||||
self::assertFalse($failure->rolledBack);
|
self::assertFalse($failure->rolledBack);
|
||||||
self::assertCount(1, $updates);
|
self::assertCount(1, $updates);
|
||||||
self::assertStringContainsString('publish', $failure->getMessage());
|
self::assertStringContainsString('publish', $failure->getMessage());
|
||||||
|
$log->assertPartials([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAnIncompleteRollbackIsAPartialWriteAndLogged(): void
|
public function testAnIncompleteRollbackIsAPartialWriteAndLogged(): void
|
||||||
@@ -314,10 +319,7 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
$updates = [];
|
$updates = [];
|
||||||
$this->captureUpdates($updates);
|
$this->captureUpdates($updates);
|
||||||
Functions\when('wp_revisions_enabled')->justReturn(false);
|
Functions\when('wp_revisions_enabled')->justReturn(false);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')
|
|
||||||
->with('lit/content-update', 'page', '5', Mockery::type('string'), 811)
|
|
||||||
->andReturn(12);
|
|
||||||
|
|
||||||
$failure = $this->executeExpectingFailure($writer, $input, $log);
|
$failure = $this->executeExpectingFailure($writer, $input, $log);
|
||||||
|
|
||||||
@@ -327,6 +329,12 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
self::assertSame('lit_partial_write', $error->get_error_code());
|
self::assertSame('lit_partial_write', $error->get_error_code());
|
||||||
self::assertSame(5, $error->get_error_data()['post_id']);
|
self::assertSame(5, $error->get_error_data()['post_id']);
|
||||||
self::assertSame(811, $error->get_error_data()['revision_id']);
|
self::assertSame(811, $error->get_error_data()['revision_id']);
|
||||||
|
$log->assertPartials([
|
||||||
|
'ability' => 'lit/content-update',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'revision_id' => 811,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailuresAfterTheWriteOnlyAddWarnings(): void
|
public function testFailuresAfterTheWriteOnlyAddWarnings(): void
|
||||||
@@ -345,8 +353,7 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
Actions\expectDone('lit_mcp_after_save')->once()->whenHappen(static function (): void {
|
Actions\expectDone('lit_mcp_after_save')->once()->whenHappen(static function (): void {
|
||||||
throw new RuntimeException('listener kaputt');
|
throw new RuntimeException('listener kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('db weg'));
|
||||||
$log->expects('record')->andThrow(new RuntimeException('db weg'));
|
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||||
$previous = ini_set('error_log', $file);
|
$previous = ini_set('error_log', $file);
|
||||||
|
|
||||||
@@ -361,5 +368,6 @@ final class ContentUpdateAbilityTest extends TestCase
|
|||||||
self::assertCount(2, $result['warnings']);
|
self::assertCount(2, $result['warnings']);
|
||||||
self::assertStringContainsString('listener kaputt', (string) file_get_contents($file));
|
self::assertStringContainsString('listener kaputt', (string) file_get_contents($file));
|
||||||
unlink($file);
|
unlink($file);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Content;
|
|||||||
|
|
||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Content\NewDraft;
|
use LIT\MCP\Integrations\WordPress\Content\NewDraft;
|
||||||
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
use LIT\MCP\Integrations\WordPress\Content\PostWriter;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -22,9 +22,7 @@ final class NewDraftTest extends TestCase
|
|||||||
Functions\when('wp_delete_post')->alias(static function (): never {
|
Functions\when('wp_delete_post')->alias(static function (): never {
|
||||||
throw new RuntimeException('delete_post hook kaputt');
|
throw new RuntimeException('delete_post hook kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')
|
|
||||||
->with('lit/content-create', 'page', '42', 'Entwurf 42: Anlegen nur teilweise zurückgenommen');
|
|
||||||
$draft = new NewDraft($writer, $log, 'lit/content-create', 'page');
|
$draft = new NewDraft($writer, $log, 'lit/content-create', 'page');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -40,6 +38,12 @@ final class NewDraftTest extends TestCase
|
|||||||
$e->getMessage()
|
$e->getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
$log->assertPartials([
|
||||||
|
'ability' => 'lit/content-create',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '42',
|
||||||
|
'summary' => 'Entwurf 42: Anlegen nur teilweise zurückgenommen',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testACleanDiscardIsRolledBackWithTheGivenReason(): void
|
public function testACleanDiscardIsRolledBackWithTheGivenReason(): void
|
||||||
@@ -47,8 +51,7 @@ final class NewDraftTest extends TestCase
|
|||||||
$writer = Mockery::mock(PostWriter::class);
|
$writer = Mockery::mock(PostWriter::class);
|
||||||
$writer->expects('deleteCreatedTerms');
|
$writer->expects('deleteCreatedTerms');
|
||||||
Functions\expect('wp_delete_post')->once()->with(42, true)->andReturn(true);
|
Functions\expect('wp_delete_post')->once()->with(42, true)->andReturn(true);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->expects('recordPartial')->never();
|
|
||||||
$draft = new NewDraft($writer, $log, 'lit/content-create', 'page');
|
$draft = new NewDraft($writer, $log, 'lit/content-create', 'page');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ declare(strict_types=1);
|
|||||||
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Menus;
|
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Menus;
|
||||||
|
|
||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Menus\MenuAssignLocationAbility;
|
use LIT\MCP\Integrations\WordPress\Menus\MenuAssignLocationAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
|
|
||||||
final class MenuAssignLocationAbilityTest extends TestCase
|
final class MenuAssignLocationAbilityTest extends TestCase
|
||||||
{
|
{
|
||||||
@@ -29,28 +28,26 @@ final class MenuAssignLocationAbilityTest extends TestCase
|
|||||||
public function testAssignsAndReportsThePreviousMenu(): void
|
public function testAssignsAndReportsThePreviousMenu(): void
|
||||||
{
|
{
|
||||||
Functions\expect('set_theme_mod')->once()->with('nav_menu_locations', ['primary-menu' => 8]);
|
Functions\expect('set_theme_mod')->once()->with('nav_menu_locations', ['primary-menu' => 8]);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(6);
|
||||||
$log->expects('record')
|
|
||||||
->with(
|
|
||||||
'lit/menu-assign-location',
|
|
||||||
'menu_location',
|
|
||||||
'primary-menu',
|
|
||||||
'Menü 8 der Position primary-menu zugewiesen'
|
|
||||||
)
|
|
||||||
->andReturn(6);
|
|
||||||
|
|
||||||
$result = (new MenuAssignLocationAbility($log))->execute(['location' => 'primary-menu', 'menu_id' => 8]);
|
$result = (new MenuAssignLocationAbility($log))->execute(['location' => 'primary-menu', 'menu_id' => 8]);
|
||||||
|
|
||||||
self::assertSame(3, $result['previous_menu_id']);
|
self::assertSame(3, $result['previous_menu_id']);
|
||||||
self::assertSame('assigned', $result['changed'][0]['action']);
|
self::assertSame('assigned', $result['changed'][0]['action']);
|
||||||
self::assertSame(6, $result['log_id']);
|
self::assertSame(6, $result['log_id']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/menu-assign-location',
|
||||||
|
'object_type' => 'menu_location',
|
||||||
|
'object_id' => 'primary-menu',
|
||||||
|
'summary' => 'Menü 8 der Position primary-menu zugewiesen',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRemovingAnAssignmentNeedsConfirmation(): void
|
public function testRemovingAnAssignmentNeedsConfirmation(): void
|
||||||
{
|
{
|
||||||
Functions\expect('set_theme_mod')->never();
|
Functions\expect('set_theme_mod')->never();
|
||||||
|
|
||||||
$result = (new MenuAssignLocationAbility(Mockery::mock(ChangeLog::class)))
|
$result = (new MenuAssignLocationAbility(FakeChangeLog::forbidden()))
|
||||||
->execute(['location' => 'primary-menu', 'menu_id' => 0]);
|
->execute(['location' => 'primary-menu', 'menu_id' => 0]);
|
||||||
|
|
||||||
self::assertSame('lit_confirm_required', $result->get_error_code());
|
self::assertSame('lit_confirm_required', $result->get_error_code());
|
||||||
@@ -63,15 +60,7 @@ final class MenuAssignLocationAbilityTest extends TestCase
|
|||||||
public function testZeroRemovesTheAssignmentAndNamesTheRemovedMenu(): void
|
public function testZeroRemovesTheAssignmentAndNamesTheRemovedMenu(): void
|
||||||
{
|
{
|
||||||
Functions\expect('set_theme_mod')->once()->with('nav_menu_locations', []);
|
Functions\expect('set_theme_mod')->once()->with('nav_menu_locations', []);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(7);
|
||||||
$log->expects('record')
|
|
||||||
->with(
|
|
||||||
'lit/menu-assign-location',
|
|
||||||
'menu_location',
|
|
||||||
'primary-menu',
|
|
||||||
'Menü "Hauptmenü" (ID 3) von der Position primary-menu entfernt'
|
|
||||||
)
|
|
||||||
->andReturn(7);
|
|
||||||
|
|
||||||
$result = (new MenuAssignLocationAbility($log))
|
$result = (new MenuAssignLocationAbility($log))
|
||||||
->execute(['location' => 'primary-menu', 'menu_id' => 0, 'confirm' => true]);
|
->execute(['location' => 'primary-menu', 'menu_id' => 0, 'confirm' => true]);
|
||||||
@@ -80,14 +69,19 @@ final class MenuAssignLocationAbilityTest extends TestCase
|
|||||||
self::assertSame(3, $result['previous_menu_id']);
|
self::assertSame(3, $result['previous_menu_id']);
|
||||||
self::assertStringContainsString('"Hauptmenü" (ID 3)', $result['summary']);
|
self::assertStringContainsString('"Hauptmenü" (ID 3)', $result['summary']);
|
||||||
self::assertSame(7, $result['log_id']);
|
self::assertSame(7, $result['log_id']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/menu-assign-location',
|
||||||
|
'object_type' => 'menu_location',
|
||||||
|
'object_id' => 'primary-menu',
|
||||||
|
'summary' => 'Menü "Hauptmenü" (ID 3) von der Position primary-menu entfernt',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRemovingFromAnEmptyLocationNeedsNoConfirmation(): void
|
public function testRemovingFromAnEmptyLocationNeedsNoConfirmation(): void
|
||||||
{
|
{
|
||||||
Functions\when('get_nav_menu_locations')->justReturn([]);
|
Functions\when('get_nav_menu_locations')->justReturn([]);
|
||||||
Functions\expect('set_theme_mod')->once()->with('nav_menu_locations', []);
|
Functions\expect('set_theme_mod')->once()->with('nav_menu_locations', []);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(8);
|
||||||
$log->allows('record')->andReturn(8);
|
|
||||||
|
|
||||||
$result = (new MenuAssignLocationAbility($log))->execute(['location' => 'primary-menu', 'menu_id' => 0]);
|
$result = (new MenuAssignLocationAbility($log))->execute(['location' => 'primary-menu', 'menu_id' => 0]);
|
||||||
|
|
||||||
@@ -97,7 +91,7 @@ final class MenuAssignLocationAbilityTest extends TestCase
|
|||||||
|
|
||||||
public function testDescribesTheConfirmationForRemoval(): void
|
public function testDescribesTheConfirmationForRemoval(): void
|
||||||
{
|
{
|
||||||
$ability = new MenuAssignLocationAbility(Mockery::mock(ChangeLog::class));
|
$ability = new MenuAssignLocationAbility(FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertArrayHasKey('confirm', $ability->inputSchema()['properties']);
|
self::assertArrayHasKey('confirm', $ability->inputSchema()['properties']);
|
||||||
self::assertStringContainsString('confirm: true', $ability->description());
|
self::assertStringContainsString('confirm: true', $ability->description());
|
||||||
@@ -107,7 +101,7 @@ final class MenuAssignLocationAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('set_theme_mod')->never();
|
Functions\expect('set_theme_mod')->never();
|
||||||
|
|
||||||
$result = (new MenuAssignLocationAbility(Mockery::mock(ChangeLog::class)))
|
$result = (new MenuAssignLocationAbility(FakeChangeLog::forbidden()))
|
||||||
->execute(['location' => 'primary-menu', 'menu_id' => 99]);
|
->execute(['location' => 'primary-menu', 'menu_id' => 99]);
|
||||||
|
|
||||||
self::assertSame('lit_not_found', $result->get_error_code());
|
self::assertSame('lit_not_found', $result->get_error_code());
|
||||||
@@ -117,7 +111,7 @@ final class MenuAssignLocationAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('current_user_can')->justReturn(false);
|
Functions\when('current_user_can')->justReturn(false);
|
||||||
|
|
||||||
$ability = new MenuAssignLocationAbility(Mockery::mock(ChangeLog::class));
|
$ability = new MenuAssignLocationAbility(FakeChangeLog::forbidden());
|
||||||
$input = ['location' => 'primary-menu', 'menu_id' => 8];
|
$input = ['location' => 'primary-menu', 'menu_id' => 8];
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $ability->permission($input)->get_error_code());
|
self::assertSame('lit_forbidden', $ability->permission($input)->get_error_code());
|
||||||
@@ -125,7 +119,7 @@ final class MenuAssignLocationAbilityTest extends TestCase
|
|||||||
|
|
||||||
public function testIsDestructiveBecauseItReplacesTheMenuOfTheLocation(): void
|
public function testIsDestructiveBecauseItReplacesTheMenuOfTheLocation(): void
|
||||||
{
|
{
|
||||||
$ability = new MenuAssignLocationAbility(Mockery::mock(ChangeLog::class));
|
$ability = new MenuAssignLocationAbility(FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame(['readonly' => false, 'destructive' => true, 'idempotent' => true], $ability->annotations());
|
self::assertSame(['readonly' => false, 'destructive' => true, 'idempotent' => true], $ability->annotations());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ declare(strict_types=1);
|
|||||||
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Menus;
|
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Menus;
|
||||||
|
|
||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Menus\MenuCreateAbility;
|
use LIT\MCP\Integrations\WordPress\Menus\MenuCreateAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
|
|
||||||
final class MenuCreateAbilityTest extends TestCase
|
final class MenuCreateAbilityTest extends TestCase
|
||||||
@@ -18,7 +17,7 @@ final class MenuCreateAbilityTest extends TestCase
|
|||||||
Functions\when('get_registered_nav_menus')->justReturn(['primary-menu' => 'Hauptmenü']);
|
Functions\when('get_registered_nav_menus')->justReturn(['primary-menu' => 'Hauptmenü']);
|
||||||
Functions\expect('wp_create_nav_menu')->never();
|
Functions\expect('wp_create_nav_menu')->never();
|
||||||
|
|
||||||
$ability = new MenuCreateAbility(Mockery::mock(ChangeLog::class));
|
$ability = new MenuCreateAbility(FakeChangeLog::forbidden());
|
||||||
$result = $ability->execute(['name' => 'Neu', 'location' => 'nope']);
|
$result = $ability->execute(['name' => 'Neu', 'location' => 'nope']);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
@@ -31,7 +30,7 @@ final class MenuCreateAbilityTest extends TestCase
|
|||||||
new WP_Error('menu_exists', 'Der Menüname existiert bereits.')
|
new WP_Error('menu_exists', 'Der Menüname existiert bereits.')
|
||||||
);
|
);
|
||||||
|
|
||||||
$result = (new MenuCreateAbility(Mockery::mock(ChangeLog::class)))->execute(['name' => 'Hauptmenü']);
|
$result = (new MenuCreateAbility(FakeChangeLog::forbidden()))->execute(['name' => 'Hauptmenü']);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -45,23 +44,26 @@ final class MenuCreateAbilityTest extends TestCase
|
|||||||
'nav_menu_locations',
|
'nav_menu_locations',
|
||||||
['footer-menu' => 2, 'primary-menu' => 8]
|
['footer-menu' => 2, 'primary-menu' => 8]
|
||||||
);
|
);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(5);
|
||||||
$log->expects('record')
|
|
||||||
->with('lit/menu-create', 'nav_menu', '8', 'Menü "Neu" angelegt (Position primary-menu)')
|
|
||||||
->andReturn(5);
|
|
||||||
|
|
||||||
$result = (new MenuCreateAbility($log))->execute(['name' => 'Neu', 'location' => 'primary-menu']);
|
$result = (new MenuCreateAbility($log))->execute(['name' => 'Neu', 'location' => 'primary-menu']);
|
||||||
|
|
||||||
self::assertSame(8, $result['menu_id']);
|
self::assertSame(8, $result['menu_id']);
|
||||||
self::assertSame('created', $result['changed'][0]['action']);
|
self::assertSame('created', $result['changed'][0]['action']);
|
||||||
self::assertSame(5, $result['log_id']);
|
self::assertSame(5, $result['log_id']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/menu-create',
|
||||||
|
'object_type' => 'nav_menu',
|
||||||
|
'object_id' => '8',
|
||||||
|
'summary' => 'Menü "Neu" angelegt (Position primary-menu)',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPermissionNeedsThemeOptionRights(): void
|
public function testPermissionNeedsThemeOptionRights(): void
|
||||||
{
|
{
|
||||||
Functions\when('current_user_can')->justReturn(false);
|
Functions\when('current_user_can')->justReturn(false);
|
||||||
|
|
||||||
$ability = new MenuCreateAbility(Mockery::mock(ChangeLog::class));
|
$ability = new MenuCreateAbility(FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $ability->permission(['name' => 'Neu'])->get_error_code());
|
self::assertSame('lit_forbidden', $ability->permission(['name' => 'Neu'])->get_error_code());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Menus;
|
|||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\Rev;
|
use LIT\MCP\Abilities\Rev;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Menus\MenuOperations;
|
use LIT\MCP\Integrations\WordPress\Menus\MenuOperations;
|
||||||
use LIT\MCP\Integrations\WordPress\Menus\MenuSetItemsAbility;
|
use LIT\MCP\Integrations\WordPress\Menus\MenuSetItemsAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Menus\MenuTree;
|
use LIT\MCP\Integrations\WordPress\Menus\MenuTree;
|
||||||
use LIT\MCP\Integrations\WordPress\Menus\MenuWriter;
|
use LIT\MCP\Integrations\WordPress\Menus\MenuWriter;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -37,7 +37,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->expects('apply')->never();
|
$writer->expects('apply')->never();
|
||||||
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$stale = $ability->execute(['menu_id' => 3, 'menu_rev' => 'abcdefabcdef', 'items' => [['id' => 10]]]);
|
$stale = $ability->execute(['menu_id' => 3, 'menu_rev' => 'abcdefabcdef', 'items' => [['id' => 10]]]);
|
||||||
$missing = $ability->execute(['menu_id' => 3, 'items' => [['id' => 10]]]);
|
$missing = $ability->execute(['menu_id' => 3, 'items' => [['id' => 10]]]);
|
||||||
@@ -55,8 +55,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
||||||
$writer->allows('apply')->andReturn(['created' => 0, 'updated' => 1, 'removed' => 0]);
|
$writer->allows('apply')->andReturn(['created' => 0, 'updated' => 1, 'removed' => 0]);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(30);
|
||||||
$log->allows('record')->andReturn(30);
|
|
||||||
|
|
||||||
$result = (new MenuSetItemsAbility($writer, new MenuOperations(), $log))->execute(
|
$result = (new MenuSetItemsAbility($writer, new MenuOperations(), $log))->execute(
|
||||||
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [['id' => 10]]]
|
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [['id' => 10]]]
|
||||||
@@ -67,7 +66,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
|
|
||||||
public function testItemsAndOperationsAreMutuallyExclusive(): void
|
public function testItemsAndOperationsAreMutuallyExclusive(): void
|
||||||
{
|
{
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $ability->execute(['menu_id' => 3, 'menu_rev' => self::rev()])->get_error_code());
|
self::assertSame('lit_validation_failed', $ability->execute(['menu_id' => 3, 'menu_rev' => self::rev()])->get_error_code());
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
@@ -80,7 +79,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
public function testUnknownMenuIsNotFound(): void
|
public function testUnknownMenuIsNotFound(): void
|
||||||
{
|
{
|
||||||
Functions\when('wp_get_nav_menu_object')->justReturn(false);
|
Functions\when('wp_get_nav_menu_object')->justReturn(false);
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('lit_not_found', $ability->execute(['menu_id' => 9, 'items' => []])->get_error_code());
|
self::assertSame('lit_not_found', $ability->execute(['menu_id' => 9, 'items' => []])->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -89,7 +88,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_update_nav_menu_item')->never();
|
Functions\expect('wp_update_nav_menu_item')->never();
|
||||||
Functions\expect('wp_delete_post')->never();
|
Functions\expect('wp_delete_post')->never();
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$result = $ability->execute(['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => []]);
|
$result = $ability->execute(['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => []]);
|
||||||
|
|
||||||
@@ -102,7 +101,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
Functions\when('wp_parse_url')->alias('parse_url');
|
Functions\when('wp_parse_url')->alias('parse_url');
|
||||||
Functions\expect('wp_update_nav_menu_item')->never();
|
Functions\expect('wp_update_nav_menu_item')->never();
|
||||||
Functions\expect('wp_delete_post')->never();
|
Functions\expect('wp_delete_post')->never();
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$result = $ability->execute([
|
$result = $ability->execute([
|
||||||
'menu_id' => 3, 'menu_rev' => self::rev(),
|
'menu_id' => 3, 'menu_rev' => self::rev(),
|
||||||
@@ -118,7 +117,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->expects('apply')->never();
|
$writer->expects('apply')->never();
|
||||||
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$result = $ability->execute(['menu_id' => 3, 'menu_rev' => self::rev(), 'operations' => [['op' => 'move', 'id' => 99]]]);
|
$result = $ability->execute(['menu_id' => 3, 'menu_rev' => self::rev(), 'operations' => [['op' => 'move', 'id' => 99]]]);
|
||||||
|
|
||||||
@@ -130,8 +129,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
||||||
$writer->expects('apply')->andReturn(['created' => 1, 'updated' => 1, 'removed' => 0]);
|
$writer->expects('apply')->andReturn(['created' => 1, 'updated' => 1, 'removed' => 0]);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(21);
|
||||||
$log->expects('record')->with('lit/menu-set-items', 'nav_menu', '3', Mockery::type('string'))->andReturn(21);
|
|
||||||
|
|
||||||
$result = (new MenuSetItemsAbility($writer, new MenuOperations(), $log))->execute([
|
$result = (new MenuSetItemsAbility($writer, new MenuOperations(), $log))->execute([
|
||||||
'menu_id' => 3, 'menu_rev' => self::rev(),
|
'menu_id' => 3, 'menu_rev' => self::rev(),
|
||||||
@@ -141,6 +139,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
self::assertSame('Home', $result['previous_items'][0]['title']);
|
self::assertSame('Home', $result['previous_items'][0]['title']);
|
||||||
self::assertSame(21, $result['log_id']);
|
self::assertSame(21, $result['log_id']);
|
||||||
self::assertStringContainsString('1 neu, 1 geändert, 0 entfernt', $result['summary']);
|
self::assertStringContainsString('1 neu, 1 geändert, 0 entfernt', $result['summary']);
|
||||||
|
$log->assertRecorded(['ability' => 'lit/menu-set-items', 'object_type' => 'nav_menu', 'object_id' => '3']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConfirmedRemovalsAreWrittenAndTheOldTreeIsReturned(): void
|
public function testConfirmedRemovalsAreWrittenAndTheOldTreeIsReturned(): void
|
||||||
@@ -151,8 +150,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
);
|
);
|
||||||
$writer->expects('apply')->with(3, [], Mockery::type('array'))
|
$writer->expects('apply')->with(3, [], Mockery::type('array'))
|
||||||
->andReturn(['created' => 0, 'updated' => 0, 'removed' => 1]);
|
->andReturn(['created' => 0, 'updated' => 0, 'removed' => 1]);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(22);
|
||||||
$log->allows('record')->andReturn(22);
|
|
||||||
|
|
||||||
$result = (new MenuSetItemsAbility($writer, new MenuOperations(), $log))->execute(
|
$result = (new MenuSetItemsAbility($writer, new MenuOperations(), $log))->execute(
|
||||||
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [], 'confirm' => true]
|
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [], 'confirm' => true]
|
||||||
@@ -169,8 +167,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
Functions\when('wp_get_nav_menu_items')->justReturn($items);
|
Functions\when('wp_get_nav_menu_items')->justReturn($items);
|
||||||
Functions\expect('wp_delete_post')->never();
|
Functions\expect('wp_delete_post')->never();
|
||||||
Functions\expect('wp_update_nav_menu_item')->never();
|
Functions\expect('wp_update_nav_menu_item')->never();
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->expects('record')->never();
|
|
||||||
|
|
||||||
$result = (new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), $log))->execute(
|
$result = (new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), $log))->execute(
|
||||||
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => MenuTree::build($items)]
|
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => MenuTree::build($items)]
|
||||||
@@ -187,7 +184,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->expects('plan')->with(Mockery::type('array'), Mockery::type('array'), true)
|
$writer->expects('plan')->with(Mockery::type('array'), Mockery::type('array'), true)
|
||||||
->andReturn(['errors' => ['items.0' => 'x'], 'removals' => [], 'warnings' => []]);
|
->andReturn(['errors' => ['items.0' => 'x'], 'removals' => [], 'warnings' => []]);
|
||||||
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$result = $ability->execute(
|
$result = $ability->execute(
|
||||||
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [['type' => 'post', 'object_id' => 7]], 'allow_unpublished' => true]
|
['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [['type' => 'post', 'object_id' => 7]], 'allow_unpublished' => true]
|
||||||
@@ -205,8 +202,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
);
|
);
|
||||||
Functions\when('get_post_type')->justReturn('page');
|
Functions\when('get_post_type')->justReturn('page');
|
||||||
Functions\expect('wp_update_nav_menu_item')->once()->andReturn(60);
|
Functions\expect('wp_update_nav_menu_item')->once()->andReturn(60);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(24);
|
||||||
$log->allows('record')->andReturn(24);
|
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), $log);
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), $log);
|
||||||
$input = ['menu_id' => 3, 'menu_rev' => self::rev(), 'operations' => [['op' => 'add', 'item' => ['type' => 'post', 'object_id' => 7]]]];
|
$input = ['menu_id' => 3, 'menu_rev' => self::rev(), 'operations' => [['op' => 'add', 'item' => ['type' => 'post', 'object_id' => 7]]]];
|
||||||
|
|
||||||
@@ -226,7 +222,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
new \WP_Post(['ID' => 7, 'post_type' => 'page', 'post_status' => 'private', 'post_title' => 'Intern'])
|
new \WP_Post(['ID' => 7, 'post_type' => 'page', 'post_status' => 'private', 'post_title' => 'Intern'])
|
||||||
);
|
);
|
||||||
Functions\expect('wp_update_nav_menu_item')->never();
|
Functions\expect('wp_update_nav_menu_item')->never();
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$result = $ability->execute([
|
$result = $ability->execute([
|
||||||
'menu_id' => 3, 'menu_rev' => self::rev(),
|
'menu_id' => 3, 'menu_rev' => self::rev(),
|
||||||
@@ -243,8 +239,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
||||||
$writer->expects('apply')->andThrow(WriteFailed::rolledBack('Menü nicht gespeichert.'));
|
$writer->expects('apply')->andThrow(WriteFailed::rolledBack('Menü nicht gespeichert.'));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->shouldNotReceive('recordPartial');
|
|
||||||
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), $log);
|
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), $log);
|
||||||
|
|
||||||
$this->expectException(WriteFailed::class);
|
$this->expectException(WriteFailed::class);
|
||||||
@@ -256,12 +251,16 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => []]);
|
||||||
$writer->expects('apply')->andThrow(WriteFailed::partial('Menü nicht gespeichert.'));
|
$writer->expects('apply')->andThrow(WriteFailed::partial('Menü nicht gespeichert.'));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->with('lit/menu-set-items', 'nav_menu', '3', Mockery::type('string'));
|
|
||||||
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), $log);
|
$ability = new MenuSetItemsAbility($writer, new MenuOperations(), $log);
|
||||||
|
|
||||||
$this->expectException(WriteFailed::class);
|
try {
|
||||||
$ability->execute(['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [['id' => 10]]]);
|
$ability->execute(['menu_id' => 3, 'menu_rev' => self::rev(), 'items' => [['id' => 10]]]);
|
||||||
|
self::fail('WriteFailed expected');
|
||||||
|
} catch (WriteFailed $e) {
|
||||||
|
self::assertFalse($e->rolledBack);
|
||||||
|
}
|
||||||
|
$log->assertPartials(['ability' => 'lit/menu-set-items', 'object_type' => 'nav_menu', 'object_id' => '3']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAChangeLogFailureIsOnlyAWarning(): void
|
public function testAChangeLogFailureIsOnlyAWarning(): void
|
||||||
@@ -269,8 +268,7 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
$writer = Mockery::mock(MenuWriter::class);
|
$writer = Mockery::mock(MenuWriter::class);
|
||||||
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => ['"X" ist nicht veröffentlicht']]);
|
$writer->allows('plan')->andReturn(['errors' => [], 'removals' => [], 'warnings' => ['"X" ist nicht veröffentlicht']]);
|
||||||
$writer->allows('apply')->andReturn(['created' => 0, 'updated' => 1, 'removed' => 0]);
|
$writer->allows('apply')->andReturn(['created' => 0, 'updated' => 1, 'removed' => 0]);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('db down'));
|
||||||
$log->allows('record')->andThrow(new RuntimeException('db down'));
|
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit');
|
||||||
$errorLog = ini_set('error_log', $file);
|
$errorLog = ini_set('error_log', $file);
|
||||||
|
|
||||||
@@ -290,14 +288,14 @@ final class MenuSetItemsAbilityTest extends TestCase
|
|||||||
public function testPermissionNeedsThemeOptionRights(): void
|
public function testPermissionNeedsThemeOptionRights(): void
|
||||||
{
|
{
|
||||||
Functions\when('current_user_can')->justReturn(false);
|
Functions\when('current_user_can')->justReturn(false);
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $ability->permission(['menu_id' => 3, 'menu_rev' => self::rev()])->get_error_code());
|
self::assertSame('lit_forbidden', $ability->permission(['menu_id' => 3, 'menu_rev' => self::rev()])->get_error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsDestructiveAndDescribesWhenConfirmIsNeeded(): void
|
public function testIsDestructiveAndDescribesWhenConfirmIsNeeded(): void
|
||||||
{
|
{
|
||||||
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), Mockery::mock(ChangeLog::class));
|
$ability = new MenuSetItemsAbility(new MenuWriter(), new MenuOperations(), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame(['readonly' => false, 'destructive' => true, 'idempotent' => false], $ability->annotations());
|
self::assertSame(['readonly' => false, 'destructive' => true, 'idempotent' => false], $ability->annotations());
|
||||||
self::assertStringContainsString('confirm: true', $ability->description());
|
self::assertStringContainsString('confirm: true', $ability->description());
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress;
|
|||||||
use Brain\Monkey\Actions;
|
use Brain\Monkey\Actions;
|
||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\Rev;
|
use LIT\MCP\Abilities\Rev;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\WordPress\PostChangeResult;
|
use LIT\MCP\Integrations\WordPress\PostChangeResult;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
@@ -38,8 +37,7 @@ final class PostChangeResultTest extends TestCase
|
|||||||
$post = new WP_Post(['ID' => 5, 'post_title' => 'Team']);
|
$post = new WP_Post(['ID' => 5, 'post_title' => 'Team']);
|
||||||
Actions\expectDone('lit_mcp_after_save')->once()->with(5, 'lit/content-update', ['post_id' => 5]);
|
Actions\expectDone('lit_mcp_after_save')->once()->with(5, 'lit/content-update', ['post_id' => 5]);
|
||||||
Functions\when('get_post')->justReturn($post);
|
Functions\when('get_post')->justReturn($post);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(9);
|
||||||
$log->expects('record')->with('lit/content-update', 'page', '5', 'geändert', 811)->andReturn(9);
|
|
||||||
|
|
||||||
$change = new PostChangeResult('lit/content-update', $log, 5, 'die Änderung', ['vorher']);
|
$change = new PostChangeResult('lit/content-update', $log, 5, 'die Änderung', ['vorher']);
|
||||||
$change->afterSave(['post_id' => 5]);
|
$change->afterSave(['post_id' => 5]);
|
||||||
@@ -52,6 +50,13 @@ final class PostChangeResultTest extends TestCase
|
|||||||
self::assertSame(Rev::ofPost($post), $result['rev']);
|
self::assertSame(Rev::ofPost($post), $result['rev']);
|
||||||
self::assertSame(9, $result['log_id']);
|
self::assertSame(9, $result['log_id']);
|
||||||
self::assertSame(['vorher'], $result['warnings']);
|
self::assertSame(['vorher'], $result['warnings']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/content-update',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'summary' => 'geändert',
|
||||||
|
'revision_id' => 811,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailuresAfterTheWriteBecomeWarnings(): void
|
public function testFailuresAfterTheWriteBecomeWarnings(): void
|
||||||
@@ -60,8 +65,7 @@ final class PostChangeResultTest extends TestCase
|
|||||||
throw new RuntimeException('listener kaputt');
|
throw new RuntimeException('listener kaputt');
|
||||||
});
|
});
|
||||||
Functions\when('get_post')->justReturn(null);
|
Functions\when('get_post')->justReturn(null);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('log kaputt'));
|
||||||
$log->expects('record')->andThrow(new RuntimeException('log kaputt'));
|
|
||||||
$fallback = new WP_Post(['ID' => 5]);
|
$fallback = new WP_Post(['ID' => 5]);
|
||||||
|
|
||||||
$change = new PostChangeResult('lit/post-publish', $log, 5);
|
$change = new PostChangeResult('lit/post-publish', $log, 5);
|
||||||
@@ -77,13 +81,14 @@ final class PostChangeResultTest extends TestCase
|
|||||||
'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.',
|
'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.',
|
||||||
], $result['warnings']);
|
], $result['warnings']);
|
||||||
self::assertStringContainsString('listener kaputt', (string) file_get_contents($this->errorLog));
|
self::assertStringContainsString('listener kaputt', (string) file_get_contents($this->errorLog));
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWithoutAReadablePostTheRevIsNull(): void
|
public function testWithoutAReadablePostTheRevIsNull(): void
|
||||||
{
|
{
|
||||||
Functions\when('get_post')->justReturn(null);
|
Functions\when('get_post')->justReturn(null);
|
||||||
|
|
||||||
$change = new PostChangeResult('lit/content-create', Mockery::mock(ChangeLog::class), 5, 'der Entwurf');
|
$change = new PostChangeResult('lit/content-create', FakeChangeLog::forbidden(), 5, 'der Entwurf');
|
||||||
|
|
||||||
self::assertNull($change->reread());
|
self::assertNull($change->reread());
|
||||||
self::assertNull($change->result('ok', null)->toArray()['rev']);
|
self::assertNull($change->result('ok', null)->toArray()['rev']);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\Integrations\WordPress\Render\PageRenderer;
|
use LIT\MCP\Integrations\WordPress\Render\PageRenderer;
|
||||||
use LIT\MCP\Integrations\WordPress\Render\PageRenderSummaryAbility;
|
use LIT\MCP\Integrations\WordPress\Render\PageRenderSummaryAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Render\RenderSummary;
|
use LIT\MCP\Integrations\WordPress\Render\RenderSummary;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
@@ -123,8 +123,7 @@ final class PageRenderSummaryAbilityTest extends TestCase
|
|||||||
Functions\when('get_post')->justReturn($post);
|
Functions\when('get_post')->justReturn($post);
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
Functions\when('get_permalink')->justReturn('https://k.test/start/');
|
Functions\when('get_permalink')->justReturn('https://k.test/start/');
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
$renderer = Mockery::mock(PageRenderer::class);
|
$renderer = Mockery::mock(PageRenderer::class);
|
||||||
$renderer->expects('render')->with($post)->andReturn(self::HTML);
|
$renderer->expects('render')->with($post)->andReturn(self::HTML);
|
||||||
$ability = new PageRenderSummaryAbility($schema, $renderer);
|
$ability = new PageRenderSummaryAbility($schema, $renderer);
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\Abilities\Rev;
|
use LIT\MCP\Abilities\Rev;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\WordPress\Revisions\RevisionRestoreAbility;
|
use LIT\MCP\Integrations\WordPress\Revisions\RevisionRestoreAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
@@ -38,8 +38,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
|
|
||||||
private function ability(ChangeLog $log): RevisionRestoreAbility
|
private function ability(ChangeLog $log): RevisionRestoreAbility
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new RevisionRestoreAbility($schema, $log);
|
return new RevisionRestoreAbility($schema, $log);
|
||||||
}
|
}
|
||||||
@@ -51,14 +50,14 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
|
|
||||||
public function testNonRevisionIdIsNotFound(): void
|
public function testNonRevisionIdIsNotFound(): void
|
||||||
{
|
{
|
||||||
self::assertSame('lit_not_found', $this->ability(Mockery::mock(ChangeLog::class))->permission(['revision_id' => 5])->get_error_code());
|
self::assertSame('lit_not_found', $this->ability(FakeChangeLog::forbidden())->permission(['revision_id' => 5])->get_error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRequiresConfirmation(): void
|
public function testRequiresConfirmation(): void
|
||||||
{
|
{
|
||||||
Functions\expect('wp_restore_post_revision')->never();
|
Functions\expect('wp_restore_post_revision')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class))->execute(['revision_id' => 810]);
|
$result = $this->ability(FakeChangeLog::forbidden())->execute(['revision_id' => 810]);
|
||||||
|
|
||||||
self::assertSame('lit_confirm_required', $result->get_error_code());
|
self::assertSame('lit_confirm_required', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -69,7 +68,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
? new WP_Post(['ID' => 810, 'post_type' => 'revision', 'post_parent' => 0])
|
? new WP_Post(['ID' => 810, 'post_type' => 'revision', 'post_parent' => 0])
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
$error = $this->ability(Mockery::mock(ChangeLog::class))
|
$error = $this->ability(FakeChangeLog::forbidden())
|
||||||
->permission(['revision_id' => 810, 'rev' => 'irrelevant', 'confirm' => true]);
|
->permission(['revision_id' => 810, 'rev' => 'irrelevant', 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $error->get_error_code());
|
self::assertSame('lit_validation_failed', $error->get_error_code());
|
||||||
@@ -85,7 +84,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
});
|
});
|
||||||
Functions\expect('wp_restore_post_revision')->never();
|
Functions\expect('wp_restore_post_revision')->never();
|
||||||
|
|
||||||
$error = $this->ability(Mockery::mock(ChangeLog::class))->execute(['revision_id' => 810, 'rev' => 'x', 'confirm' => true]);
|
$error = $this->ability(FakeChangeLog::forbidden())->execute(['revision_id' => 810, 'rev' => 'x', 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $error->get_error_code());
|
self::assertSame('lit_validation_failed', $error->get_error_code());
|
||||||
self::assertArrayHasKey('revision_id', $error->get_error_data()['fields']);
|
self::assertArrayHasKey('revision_id', $error->get_error_data()['fields']);
|
||||||
@@ -105,7 +104,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
Functions\expect('wp_save_post_revision')->never();
|
Functions\expect('wp_save_post_revision')->never();
|
||||||
Functions\expect('wp_restore_post_revision')->never();
|
Functions\expect('wp_restore_post_revision')->never();
|
||||||
|
|
||||||
$error = $this->ability(Mockery::mock(ChangeLog::class))->execute([
|
$error = $this->ability(FakeChangeLog::forbidden())->execute([
|
||||||
'revision_id' => 810,
|
'revision_id' => 810,
|
||||||
'rev' => Rev::ofPost(new WP_Post($current)),
|
'rev' => Rev::ofPost(new WP_Post($current)),
|
||||||
'confirm' => true,
|
'confirm' => true,
|
||||||
@@ -119,7 +118,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_restore_post_revision')->never();
|
Functions\expect('wp_restore_post_revision')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class))
|
$result = $this->ability(FakeChangeLog::forbidden())
|
||||||
->execute(['revision_id' => 810, 'confirm' => true, 'rev' => 'stale']);
|
->execute(['revision_id' => 810, 'confirm' => true, 'rev' => 'stale']);
|
||||||
|
|
||||||
self::assertSame('lit_rev_mismatch', $result->get_error_code());
|
self::assertSame('lit_rev_mismatch', $result->get_error_code());
|
||||||
@@ -139,8 +138,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
});
|
});
|
||||||
Functions\when('get_preview_post_link')->justReturn('https://kunde.de/team/');
|
Functions\when('get_preview_post_link')->justReturn('https://kunde.de/team/');
|
||||||
Functions\when('get_edit_post_link')->justReturn('https://kunde.de/wp-admin/post.php?post=5&action=edit');
|
Functions\when('get_edit_post_link')->justReturn('https://kunde.de/wp-admin/post.php?post=5&action=edit');
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(12);
|
||||||
$log->expects('record')->with('lit/revision-restore', 'page', '5', Mockery::type('string'), 813)->andReturn(12);
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute([
|
$result = $this->ability($log)->execute([
|
||||||
'revision_id' => 810,
|
'revision_id' => 810,
|
||||||
@@ -154,6 +152,12 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
self::assertStringContainsString('2026-09-20 10:00:00', $result['summary']);
|
self::assertStringContainsString('2026-09-20 10:00:00', $result['summary']);
|
||||||
self::assertStringContainsString('Status unverändert: publish', $result['summary']);
|
self::assertStringContainsString('Status unverändert: publish', $result['summary']);
|
||||||
self::assertSame([], $result['warnings']);
|
self::assertSame([], $result['warnings']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/revision-restore',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'revision_id' => 813,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRestoreFailureIsWriteFailed(): void
|
public function testRestoreFailureIsWriteFailed(): void
|
||||||
@@ -161,8 +165,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
Functions\when('wp_save_post_revision')->justReturn(813);
|
Functions\when('wp_save_post_revision')->justReturn(813);
|
||||||
Functions\when('wp_get_post_revisions')->justReturn([813 => new WP_Post(['ID' => 813])]);
|
Functions\when('wp_get_post_revisions')->justReturn([813 => new WP_Post(['ID' => 813])]);
|
||||||
Functions\when('wp_restore_post_revision')->justReturn(false);
|
Functions\when('wp_restore_post_revision')->justReturn(false);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->expects('record')->never();
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute([
|
$result = $this->ability($log)->execute([
|
||||||
'revision_id' => 810,
|
'revision_id' => 810,
|
||||||
@@ -188,8 +191,7 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
Actions\expectDone('lit_mcp_after_save')->once()->whenHappen(static function (): void {
|
Actions\expectDone('lit_mcp_after_save')->once()->whenHappen(static function (): void {
|
||||||
throw new RuntimeException('listener kaputt');
|
throw new RuntimeException('listener kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('db weg'));
|
||||||
$log->expects('record')->andThrow(new RuntimeException('db weg'));
|
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||||
$previous = ini_set('error_log', $file);
|
$previous = ini_set('error_log', $file);
|
||||||
|
|
||||||
@@ -208,5 +210,6 @@ final class RevisionRestoreAbilityTest extends TestCase
|
|||||||
self::assertCount(2, $result['warnings']);
|
self::assertCount(2, $result['warnings']);
|
||||||
self::assertStringContainsString('listener kaputt', (string) file_get_contents($file));
|
self::assertStringContainsString('listener kaputt', (string) file_get_contents($file));
|
||||||
unlink($file);
|
unlink($file);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\Abilities\Cursor;
|
use LIT\MCP\Abilities\Cursor;
|
||||||
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
use LIT\MCP\Integrations\WordPress\Content\PostPresenter;
|
||||||
use LIT\MCP\Integrations\WordPress\Revisions\RevisionsListAbility;
|
use LIT\MCP\Integrations\WordPress\Revisions\RevisionsListAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
final class RevisionsListAbilityTest extends TestCase
|
final class RevisionsListAbilityTest extends TestCase
|
||||||
@@ -27,8 +26,7 @@ final class RevisionsListAbilityTest extends TestCase
|
|||||||
|
|
||||||
private function ability(): RevisionsListAbility
|
private function ability(): RevisionsListAbility
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new RevisionsListAbility($schema);
|
return new RevisionsListAbility($schema);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Settings;
|
|||||||
|
|
||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Settings\SettingsWhitelist;
|
use LIT\MCP\Integrations\WordPress\Settings\SettingsWhitelist;
|
||||||
use LIT\MCP\Integrations\WordPress\Settings\WpSettingsGetAbility;
|
use LIT\MCP\Integrations\WordPress\Settings\WpSettingsGetAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Settings\WpSettingsUpdateAbility;
|
use LIT\MCP\Integrations\WordPress\Settings\WpSettingsUpdateAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -53,8 +53,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
{
|
{
|
||||||
$options = ['blogname' => 'Alt', 'blogdescription' => 'Praxis'];
|
$options = ['blogname' => 'Alt', 'blogdescription' => 'Praxis'];
|
||||||
$this->optionStore($options);
|
$this->optionStore($options);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(40);
|
||||||
$log->allows('record')->andReturn(40);
|
|
||||||
$ability = new WpSettingsUpdateAbility(new SettingsWhitelist(), $log);
|
$ability = new WpSettingsUpdateAbility(new SettingsWhitelist(), $log);
|
||||||
$input = ['settings' => ['blogname' => 'Müller & Söhne', 'blogdescription' => 'Zahnärzte']];
|
$input = ['settings' => ['blogname' => 'Müller & Söhne', 'blogdescription' => 'Zahnärzte']];
|
||||||
|
|
||||||
@@ -81,8 +80,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
{
|
{
|
||||||
$options = ['blogname' => 'Alt'];
|
$options = ['blogname' => 'Alt'];
|
||||||
$this->optionStore($options);
|
$this->optionStore($options);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1, new RuntimeException('log kaputt'));
|
||||||
$log->expects('record')->andThrow(new RuntimeException('log kaputt'));
|
|
||||||
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
$file = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||||
$previous = ini_set('error_log', $file);
|
$previous = ini_set('error_log', $file);
|
||||||
|
|
||||||
@@ -96,6 +94,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
self::assertTrue($result['ok']);
|
self::assertTrue($result['ok']);
|
||||||
self::assertNull($result['log_id']);
|
self::assertNull($result['log_id']);
|
||||||
self::assertCount(1, $result['warnings']);
|
self::assertCount(1, $result['warnings']);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
@@ -118,7 +117,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('update_option')->never();
|
Functions\expect('update_option')->never();
|
||||||
|
|
||||||
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), Mockery::mock(ChangeLog::class)))
|
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), FakeChangeLog::forbidden()))
|
||||||
->execute(['settings' => ['blogname' => 'Neu', 'admin_email' => 'x@y.de']]);
|
->execute(['settings' => ['blogname' => 'Neu', 'admin_email' => 'x@y.de']]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
@@ -129,7 +128,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('update_option')->never();
|
Functions\expect('update_option')->never();
|
||||||
|
|
||||||
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), Mockery::mock(ChangeLog::class)))
|
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), FakeChangeLog::forbidden()))
|
||||||
->execute(['settings' => ['template' => 'twentyten']]);
|
->execute(['settings' => ['template' => 'twentyten']]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
@@ -162,8 +161,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
});
|
});
|
||||||
$GLOBALS['wp_rewrite'] = $rewrite;
|
$GLOBALS['wp_rewrite'] = $rewrite;
|
||||||
Functions\expect('flush_rewrite_rules')->once();
|
Functions\expect('flush_rewrite_rules')->once();
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(30);
|
||||||
$log->expects('record')->with('lit/wp-settings-update', 'option', 'blogname,permalink_structure', Mockery::type('string'))->andReturn(30);
|
|
||||||
|
|
||||||
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), $log))->execute(['settings' => [
|
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), $log))->execute(['settings' => [
|
||||||
'blogname' => 'Neu',
|
'blogname' => 'Neu',
|
||||||
@@ -174,6 +172,11 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
self::assertSame(['blogname', 'permalink_structure'], array_column($result['changed'], 'id'));
|
self::assertSame(['blogname', 'permalink_structure'], array_column($result['changed'], 'id'));
|
||||||
self::assertSame(['blogname' => 'Alt', 'blogdescription' => 'Gleich', 'permalink_structure' => ''], $result['previous']);
|
self::assertSame(['blogname' => 'Alt', 'blogdescription' => 'Gleich', 'permalink_structure' => ''], $result['previous']);
|
||||||
self::assertSame(30, $result['log_id']);
|
self::assertSame(30, $result['log_id']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/wp-settings-update',
|
||||||
|
'object_type' => 'option',
|
||||||
|
'object_id' => 'blogname,permalink_structure',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDoesNotTreatAnAlreadyStoredValueAsAWriteFailure(): void
|
public function testDoesNotTreatAnAlreadyStoredValueAsAWriteFailure(): void
|
||||||
@@ -190,8 +193,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
'WPLANG' => '',
|
'WPLANG' => '',
|
||||||
][$key]);
|
][$key]);
|
||||||
Functions\expect('update_option')->once()->with('blogname', 'Neu')->andReturn(true);
|
Functions\expect('update_option')->once()->with('blogname', 'Neu')->andReturn(true);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(31);
|
||||||
$log->expects('record')->andReturn(31);
|
|
||||||
|
|
||||||
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), $log))->execute(['settings' => [
|
$result = (new WpSettingsUpdateAbility(new SettingsWhitelist(), $log))->execute(['settings' => [
|
||||||
'blogname' => 'Neu',
|
'blogname' => 'Neu',
|
||||||
@@ -200,6 +202,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
|
|
||||||
self::assertSame(['blogname'], array_column($result['changed'], 'id'));
|
self::assertSame(['blogname'], array_column($result['changed'], 'id'));
|
||||||
self::assertSame(31, $result['log_id']);
|
self::assertSame(31, $result['log_id']);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRestoresPreviousValuesWhenAWriteFailsPartway(): void
|
public function testRestoresPreviousValuesWhenAWriteFailsPartway(): void
|
||||||
@@ -211,9 +214,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
Functions\expect('update_option')->once()->with('blogname', 'Neu')->andReturn(true);
|
Functions\expect('update_option')->once()->with('blogname', 'Neu')->andReturn(true);
|
||||||
Functions\expect('update_option')->once()->with('blogdescription', 'Auch neu')->andReturn(false);
|
Functions\expect('update_option')->once()->with('blogdescription', 'Auch neu')->andReturn(false);
|
||||||
Functions\expect('update_option')->once()->with('blogname', 'Alt')->andReturn(true);
|
Functions\expect('update_option')->once()->with('blogname', 'Alt')->andReturn(true);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::forbidden();
|
||||||
$log->shouldNotReceive('record');
|
|
||||||
$log->shouldNotReceive('recordPartial');
|
|
||||||
|
|
||||||
$error = $this->failure(new WpSettingsUpdateAbility(new SettingsWhitelist(), $log), [
|
$error = $this->failure(new WpSettingsUpdateAbility(new SettingsWhitelist(), $log), [
|
||||||
'blogname' => 'Neu',
|
'blogname' => 'Neu',
|
||||||
@@ -221,6 +222,8 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
self::assertSame('lit_write_failed', $error->get_error_code());
|
self::assertSame('lit_write_failed', $error->get_error_code());
|
||||||
|
$log->assertRecorded();
|
||||||
|
$log->assertPartials();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testReportsPartialWriteWhenRestoringAlsoFails(): void
|
public function testReportsPartialWriteWhenRestoringAlsoFails(): void
|
||||||
@@ -242,9 +245,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
});
|
});
|
||||||
Functions\expect('update_option')->once()->with('blogdescription', 'Auch neu')->andReturn(false);
|
Functions\expect('update_option')->once()->with('blogdescription', 'Auch neu')->andReturn(false);
|
||||||
Functions\expect('update_option')->once()->with('blogname', 'Alt')->andReturn(false);
|
Functions\expect('update_option')->once()->with('blogname', 'Alt')->andReturn(false);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->shouldNotReceive('record');
|
|
||||||
$log->expects('recordPartial')->with('lit/wp-settings-update', 'option', 'blogname', Mockery::type('string'));
|
|
||||||
|
|
||||||
$error = $this->failure(new WpSettingsUpdateAbility(new SettingsWhitelist(), $log), [
|
$error = $this->failure(new WpSettingsUpdateAbility(new SettingsWhitelist(), $log), [
|
||||||
'blogname' => 'Neu',
|
'blogname' => 'Neu',
|
||||||
@@ -254,6 +255,11 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
self::assertSame('lit_partial_write', $error->get_error_code());
|
self::assertSame('lit_partial_write', $error->get_error_code());
|
||||||
self::assertStringContainsString('blogname', $error->get_error_message());
|
self::assertStringContainsString('blogname', $error->get_error_message());
|
||||||
self::assertStringContainsString('lit/wp-settings-get', $error->get_error_data()['hint']);
|
self::assertStringContainsString('lit/wp-settings-get', $error->get_error_data()['hint']);
|
||||||
|
$log->assertPartials([
|
||||||
|
'ability' => 'lit/wp-settings-update',
|
||||||
|
'object_type' => 'option',
|
||||||
|
'object_id' => 'blogname',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param array<string, mixed> $settings */
|
/** @param array<string, mixed> $settings */
|
||||||
@@ -275,7 +281,7 @@ final class WpSettingsAbilitiesTest extends TestCase
|
|||||||
self::assertSame('lit_forbidden', (new WpSettingsGetAbility(new SettingsWhitelist()))->permission([])->get_error_code());
|
self::assertSame('lit_forbidden', (new WpSettingsGetAbility(new SettingsWhitelist()))->permission([])->get_error_code());
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
'lit_forbidden',
|
'lit_forbidden',
|
||||||
(new WpSettingsUpdateAbility(new SettingsWhitelist(), Mockery::mock(ChangeLog::class)))->permission([])->get_error_code()
|
(new WpSettingsUpdateAbility(new SettingsWhitelist(), FakeChangeLog::forbidden()))->permission([])->get_error_code()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ use Brain\Monkey\Actions;
|
|||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\Rev;
|
use LIT\MCP\Abilities\Rev;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostCloner;
|
use LIT\MCP\Integrations\WordPress\Status\PostCloner;
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostDuplicateAbility;
|
use LIT\MCP\Integrations\WordPress\Status\PostDuplicateAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
@@ -34,8 +35,7 @@ final class PostDuplicateAbilityTest extends TestCase
|
|||||||
|
|
||||||
private function ability(PostCloner $cloner, ChangeLog $log): PostDuplicateAbility
|
private function ability(PostCloner $cloner, ChangeLog $log): PostDuplicateAbility
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new PostDuplicateAbility($schema, $cloner, $log);
|
return new PostDuplicateAbility($schema, $cloner, $log);
|
||||||
}
|
}
|
||||||
@@ -45,8 +45,7 @@ final class PostDuplicateAbilityTest extends TestCase
|
|||||||
$cloner = Mockery::mock(PostCloner::class);
|
$cloner = Mockery::mock(PostCloner::class);
|
||||||
$cloner->expects('duplicate')->with($this->original)->andReturn(77);
|
$cloner->expects('duplicate')->with($this->original)->andReturn(77);
|
||||||
Actions\expectDone('lit_mcp_after_save')->once()->with(77, 'lit/post-duplicate', ['post_id' => 5]);
|
Actions\expectDone('lit_mcp_after_save')->once()->with(77, 'lit/post-duplicate', ['post_id' => 5]);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(8);
|
||||||
$log->expects('record')->with('lit/post-duplicate', 'page', '77', 'Kopie von "Team" (ID 5) angelegt', null)->andReturn(8);
|
|
||||||
|
|
||||||
$result = $this->ability($cloner, $log)->execute(['post_id' => 5]);
|
$result = $this->ability($cloner, $log)->execute(['post_id' => 5]);
|
||||||
|
|
||||||
@@ -55,6 +54,13 @@ final class PostDuplicateAbilityTest extends TestCase
|
|||||||
self::assertSame(8, $result['log_id']);
|
self::assertSame(8, $result['log_id']);
|
||||||
self::assertSame(Rev::ofPost($this->copy), $result['rev']);
|
self::assertSame(Rev::ofPost($this->copy), $result['rev']);
|
||||||
self::assertSame([], $result['warnings']);
|
self::assertSame([], $result['warnings']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/post-duplicate',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '77',
|
||||||
|
'summary' => 'Kopie von "Team" (ID 5) angelegt',
|
||||||
|
'revision_id' => null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNeedsTheRightToCreateEntriesOfThatType(): void
|
public function testNeedsTheRightToCreateEntriesOfThatType(): void
|
||||||
@@ -63,7 +69,7 @@ final class PostDuplicateAbilityTest extends TestCase
|
|||||||
$cloner = Mockery::mock(PostCloner::class);
|
$cloner = Mockery::mock(PostCloner::class);
|
||||||
$cloner->expects('duplicate')->never();
|
$cloner->expects('duplicate')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class))->execute(['post_id' => 5]);
|
$result = $this->ability($cloner, FakeChangeLog::forbidden())->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $result->get_error_code());
|
self::assertSame('lit_forbidden', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -74,14 +80,14 @@ final class PostDuplicateAbilityTest extends TestCase
|
|||||||
$cloner = Mockery::mock(PostCloner::class);
|
$cloner = Mockery::mock(PostCloner::class);
|
||||||
$cloner->expects('duplicate')->never();
|
$cloner->expects('duplicate')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class))->execute(['post_id' => 5]);
|
$result = $this->ability($cloner, FakeChangeLog::forbidden())->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsIndirectAndNotIdempotent(): void
|
public function testIsIndirectAndNotIdempotent(): void
|
||||||
{
|
{
|
||||||
$ability = $this->ability(Mockery::mock(PostCloner::class), Mockery::mock(ChangeLog::class));
|
$ability = $this->ability(Mockery::mock(PostCloner::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertFalse($ability->isDirect());
|
self::assertFalse($ability->isDirect());
|
||||||
self::assertFalse($ability->annotations()['idempotent']);
|
self::assertFalse($ability->annotations()['idempotent']);
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Status;
|
|||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostPublishAbility;
|
use LIT\MCP\Integrations\WordPress\Status\PostPublishAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
@@ -18,8 +18,7 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
private function ability(ChangeLog $log): PostPublishAbility
|
private function ability(ChangeLog $log): PostPublishAbility
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new PostPublishAbility($schema, $log);
|
return new PostPublishAbility($schema, $log);
|
||||||
}
|
}
|
||||||
@@ -37,14 +36,20 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
$this->stubPost(['post_status' => 'draft', 'post_date_gmt' => '0000-00-00 00:00:00']);
|
$this->stubPost(['post_status' => 'draft', 'post_date_gmt' => '0000-00-00 00:00:00']);
|
||||||
Functions\expect('wp_update_post')->once()->with(['ID' => 5, 'post_status' => 'publish'], true)->andReturn(5);
|
Functions\expect('wp_update_post')->once()->with(['ID' => 5, 'post_status' => 'publish'], true)->andReturn(5);
|
||||||
Functions\when('get_post_status')->justReturn('publish');
|
Functions\when('get_post_status')->justReturn('publish');
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(9);
|
||||||
$log->expects('record')->with('lit/post-publish', 'page', '5', '"Team" veröffentlicht', null)->andReturn(9);
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute(['post_id' => 5]);
|
$result = $this->ability($log)->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame('published', $result['changed'][0]['action']);
|
self::assertSame('published', $result['changed'][0]['action']);
|
||||||
self::assertSame(9, $result['log_id']);
|
self::assertSame(9, $result['log_id']);
|
||||||
self::assertSame('https://kunde.de/team/', $result['preview_url']);
|
self::assertSame('https://kunde.de/team/', $result['preview_url']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/post-publish',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'summary' => '"Team" veröffentlicht',
|
||||||
|
'revision_id' => null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testScheduledPostIsPublishedNowInsteadOfStayingFuture(): void
|
public function testScheduledPostIsPublishedNowInsteadOfStayingFuture(): void
|
||||||
@@ -59,8 +64,7 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
'edit_date' => true,
|
'edit_date' => true,
|
||||||
], true)->andReturn(5);
|
], true)->andReturn(5);
|
||||||
Functions\when('get_post_status')->justReturn('publish');
|
Functions\when('get_post_status')->justReturn('publish');
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(1);
|
||||||
$log->allows('record')->andReturn(1);
|
|
||||||
|
|
||||||
$this->ability($log)->execute(['post_id' => 5]);
|
$this->ability($log)->execute(['post_id' => 5]);
|
||||||
}
|
}
|
||||||
@@ -70,7 +74,7 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
$this->stubPost(['post_status' => 'publish']);
|
$this->stubPost(['post_status' => 'publish']);
|
||||||
Functions\expect('wp_update_post')->never();
|
Functions\expect('wp_update_post')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class))->execute(['post_id' => 5]);
|
$result = $this->ability(FakeChangeLog::forbidden())->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame([], $result['changed']);
|
self::assertSame([], $result['changed']);
|
||||||
self::assertStringContainsString('bereits veröffentlicht', $result['summary']);
|
self::assertStringContainsString('bereits veröffentlicht', $result['summary']);
|
||||||
@@ -82,8 +86,7 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
Functions\when('wp_update_post')->alias(static function (): void {
|
Functions\when('wp_update_post')->alias(static function (): void {
|
||||||
throw new RuntimeException('save_post-Hook kaputt');
|
throw new RuntimeException('save_post-Hook kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->with('lit/post-publish', 'page', '5', Mockery::type('string'));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ability($log)->execute(['post_id' => 5]);
|
$this->ability($log)->execute(['post_id' => 5]);
|
||||||
@@ -94,6 +97,7 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
||||||
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
||||||
}
|
}
|
||||||
|
$log->assertPartials(['ability' => 'lit/post-publish', 'object_type' => 'page', 'object_id' => '5']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testATrashedPostIsRefused(): void
|
public function testATrashedPostIsRefused(): void
|
||||||
@@ -101,7 +105,7 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
$this->stubPost(['post_status' => 'trash']);
|
$this->stubPost(['post_status' => 'trash']);
|
||||||
Functions\expect('wp_update_post')->never();
|
Functions\expect('wp_update_post')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class))->execute(['post_id' => 5]);
|
$result = $this->ability(FakeChangeLog::forbidden())->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -111,6 +115,6 @@ final class PostPublishAbilityTest extends TestCase
|
|||||||
$this->stubPost(['post_status' => 'draft']);
|
$this->stubPost(['post_status' => 'draft']);
|
||||||
Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap !== 'publish_post');
|
Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap !== 'publish_post');
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $this->ability(Mockery::mock(ChangeLog::class))->permission(['post_id' => 5])->get_error_code());
|
self::assertSame('lit_forbidden', $this->ability(FakeChangeLog::forbidden())->permission(['post_id' => 5])->get_error_code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ use Brain\Monkey\Functions;
|
|||||||
use LIT\MCP\Abilities\Rev;
|
use LIT\MCP\Abilities\Rev;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostCloner;
|
use LIT\MCP\Integrations\WordPress\Status\PostCloner;
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostReplaceWithCopyAbility;
|
use LIT\MCP\Integrations\WordPress\Status\PostReplaceWithCopyAbility;
|
||||||
use LIT\MCP\Integrations\WordPress\Status\ReplaceFailed;
|
use LIT\MCP\Integrations\WordPress\Status\ReplaceFailed;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -39,8 +40,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
|
|
||||||
private function ability(PostCloner $cloner, ChangeLog $log, bool $trash = true): PostReplaceWithCopyAbility
|
private function ability(PostCloner $cloner, ChangeLog $log, bool $trash = true): PostReplaceWithCopyAbility
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new PostReplaceWithCopyAbility($schema, $cloner, $log, static fn (): bool => $trash);
|
return new PostReplaceWithCopyAbility($schema, $cloner, $log, static fn (): bool => $trash);
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner = Mockery::mock(PostCloner::class);
|
$cloner = Mockery::mock(PostCloner::class);
|
||||||
$cloner->expects('replace')->never();
|
$cloner->expects('replace')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class))
|
$result = $this->ability($cloner, FakeChangeLog::forbidden())
|
||||||
->execute(['original_id' => 5, 'copy_id' => 77, 'rev' => Rev::ofPost($this->original)]);
|
->execute(['original_id' => 5, 'copy_id' => 77, 'rev' => Rev::ofPost($this->original)]);
|
||||||
|
|
||||||
self::assertSame('lit_confirm_required', $result->get_error_code());
|
self::assertSame('lit_confirm_required', $result->get_error_code());
|
||||||
@@ -77,7 +77,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner = Mockery::mock(PostCloner::class);
|
$cloner = Mockery::mock(PostCloner::class);
|
||||||
$cloner->expects('replace')->never();
|
$cloner->expects('replace')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class))->execute($this->input());
|
$result = $this->ability($cloner, FakeChangeLog::forbidden())->execute($this->input());
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner->expects('backup')->never();
|
$cloner->expects('backup')->never();
|
||||||
$cloner->expects('replace')->never();
|
$cloner->expects('replace')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class))->execute($this->input());
|
$result = $this->ability($cloner, FakeChangeLog::forbidden())->execute($this->input());
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner->expects('backup')->never();
|
$cloner->expects('backup')->never();
|
||||||
$cloner->expects('replace')->never();
|
$cloner->expects('replace')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class))->execute($this->input());
|
$result = $this->ability($cloner, FakeChangeLog::forbidden())->execute($this->input());
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
self::assertArrayHasKey('original_id', $result->get_error_data()['fields']);
|
self::assertArrayHasKey('original_id', $result->get_error_data()['fields']);
|
||||||
@@ -129,8 +129,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner->allows('backup')->andReturn(88);
|
$cloner->allows('backup')->andReturn(88);
|
||||||
$cloner->expects('replace');
|
$cloner->expects('replace');
|
||||||
Functions\when('wp_trash_post')->justReturn(new WP_Post(['ID' => 77]));
|
Functions\when('wp_trash_post')->justReturn(new WP_Post(['ID' => 77]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(11);
|
||||||
$log->allows('record')->andReturn(11);
|
|
||||||
|
|
||||||
self::assertTrue($this->ability($cloner, $log)->execute($this->input())['ok']);
|
self::assertTrue($this->ability($cloner, $log)->execute($this->input())['ok']);
|
||||||
}
|
}
|
||||||
@@ -141,7 +140,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner->expects('replace')->never();
|
$cloner->expects('replace')->never();
|
||||||
Functions\expect('wp_save_post_revision')->never();
|
Functions\expect('wp_save_post_revision')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class))->execute($this->input(['rev' => 'veraltet']));
|
$result = $this->ability($cloner, FakeChangeLog::forbidden())->execute($this->input(['rev' => 'veraltet']));
|
||||||
|
|
||||||
self::assertSame('lit_rev_mismatch', $result->get_error_code());
|
self::assertSame('lit_rev_mismatch', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -151,7 +150,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner = Mockery::mock(PostCloner::class);
|
$cloner = Mockery::mock(PostCloner::class);
|
||||||
$cloner->expects('replace')->never();
|
$cloner->expects('replace')->never();
|
||||||
|
|
||||||
$result = $this->ability($cloner, Mockery::mock(ChangeLog::class), false)->execute($this->input());
|
$result = $this->ability($cloner, FakeChangeLog::forbidden(), false)->execute($this->input());
|
||||||
|
|
||||||
self::assertSame('lit_unsupported', $result->get_error_code());
|
self::assertSame('lit_unsupported', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -181,8 +180,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$order[] = 'trash';
|
$order[] = 'trash';
|
||||||
return new WP_Post(['ID' => 77]);
|
return new WP_Post(['ID' => 77]);
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(11);
|
||||||
$log->expects('record')->with('lit/post-replace-with-copy', 'page', '5', Mockery::type('string'), 900)->andReturn(11);
|
|
||||||
|
|
||||||
$result = $this->ability($cloner, $log)->execute($this->input());
|
$result = $this->ability($cloner, $log)->execute($this->input());
|
||||||
|
|
||||||
@@ -194,6 +192,12 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
self::assertSame(Rev::ofPost($this->original), $result['rev']);
|
self::assertSame(Rev::ofPost($this->original), $result['rev']);
|
||||||
self::assertSame(['replaced', 'trashed'], array_column($result['changed'], 'action'));
|
self::assertSame(['replaced', 'trashed'], array_column($result['changed'], 'action'));
|
||||||
self::assertSame([], $result['warnings']);
|
self::assertSame([], $result['warnings']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/post-replace-with-copy',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'revision_id' => 900,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAFailedTrashOfTheCopyIsAWarningOnly(): void
|
public function testAFailedTrashOfTheCopyIsAWarningOnly(): void
|
||||||
@@ -204,8 +208,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner->allows('backup')->andReturn(88);
|
$cloner->allows('backup')->andReturn(88);
|
||||||
$cloner->expects('replace');
|
$cloner->expects('replace');
|
||||||
Functions\when('wp_trash_post')->justReturn(false);
|
Functions\when('wp_trash_post')->justReturn(false);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(11);
|
||||||
$log->allows('record')->andReturn(11);
|
|
||||||
|
|
||||||
$result = $this->ability($cloner, $log)->execute($this->input());
|
$result = $this->ability($cloner, $log)->execute($this->input());
|
||||||
|
|
||||||
@@ -225,7 +228,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
Functions\expect('wp_trash_post')->never();
|
Functions\expect('wp_trash_post')->never();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ability($cloner, Mockery::mock(ChangeLog::class))->execute($this->input());
|
$this->ability($cloner, FakeChangeLog::forbidden())->execute($this->input());
|
||||||
self::fail('WriteFailed expected');
|
self::fail('WriteFailed expected');
|
||||||
} catch (WriteFailed $e) {
|
} catch (WriteFailed $e) {
|
||||||
self::assertTrue($e->rolledBack);
|
self::assertTrue($e->rolledBack);
|
||||||
@@ -246,10 +249,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner->expects('replace')->andThrow(new RuntimeException('hook kaputt'));
|
$cloner->expects('replace')->andThrow(new RuntimeException('hook kaputt'));
|
||||||
$cloner->expects('restore')->with($this->original, self::SNAPSHOT, PostCloner::PARTS)->andThrow(new RuntimeException('Restore incomplete: terms'));
|
$cloner->expects('restore')->with($this->original, self::SNAPSHOT, PostCloner::PARTS)->andThrow(new RuntimeException('Restore incomplete: terms'));
|
||||||
Functions\expect('wp_trash_post')->never();
|
Functions\expect('wp_trash_post')->never();
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')
|
|
||||||
->with('lit/post-replace-with-copy', 'page', '5', Mockery::type('string'), 900)
|
|
||||||
->andReturn(12);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ability($cloner, $log)->execute($this->input());
|
$this->ability($cloner, $log)->execute($this->input());
|
||||||
@@ -264,6 +264,12 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
self::assertStringContainsString('Sicherung', $error->get_error_message());
|
self::assertStringContainsString('Sicherung', $error->get_error_message());
|
||||||
self::assertStringContainsString('88', $error->get_error_message());
|
self::assertStringContainsString('88', $error->get_error_message());
|
||||||
}
|
}
|
||||||
|
$log->assertPartials([
|
||||||
|
'ability' => 'lit/post-replace-with-copy',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'revision_id' => 900,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNeedsTheRightToCreateEntriesForTheBackup(): void
|
public function testNeedsTheRightToCreateEntriesForTheBackup(): void
|
||||||
@@ -272,7 +278,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
$cloner = Mockery::mock(PostCloner::class);
|
$cloner = Mockery::mock(PostCloner::class);
|
||||||
$cloner->expects('backup')->never();
|
$cloner->expects('backup')->never();
|
||||||
$cloner->expects('replace')->never();
|
$cloner->expects('replace')->never();
|
||||||
$ability = $this->ability($cloner, Mockery::mock(ChangeLog::class));
|
$ability = $this->ability($cloner, FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('lit_forbidden', $ability->permission($this->input())->get_error_code());
|
self::assertSame('lit_forbidden', $ability->permission($this->input())->get_error_code());
|
||||||
self::assertSame('lit_forbidden', $ability->execute($this->input())->get_error_code());
|
self::assertSame('lit_forbidden', $ability->execute($this->input())->get_error_code());
|
||||||
@@ -289,7 +295,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
Functions\expect('wp_trash_post')->never();
|
Functions\expect('wp_trash_post')->never();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ability($cloner, Mockery::mock(ChangeLog::class))->execute($this->input());
|
$this->ability($cloner, FakeChangeLog::forbidden())->execute($this->input());
|
||||||
self::fail('WriteFailed expected');
|
self::fail('WriteFailed expected');
|
||||||
} catch (WriteFailed $e) {
|
} catch (WriteFailed $e) {
|
||||||
self::assertSame('lit_write_failed', $e->toError()->get_error_code());
|
self::assertSame('lit_write_failed', $e->toError()->get_error_code());
|
||||||
@@ -298,7 +304,7 @@ final class PostReplaceWithCopyAbilityTest extends TestCase
|
|||||||
|
|
||||||
public function testRequiresRevAndIsDestructive(): void
|
public function testRequiresRevAndIsDestructive(): void
|
||||||
{
|
{
|
||||||
$ability = $this->ability(Mockery::mock(PostCloner::class), Mockery::mock(ChangeLog::class));
|
$ability = $this->ability(Mockery::mock(PostCloner::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertContains('rev', $ability->inputSchema()['required']);
|
self::assertContains('rev', $ability->inputSchema()['required']);
|
||||||
self::assertContains('backup_id', $ability->outputSchema()['required']);
|
self::assertContains('backup_id', $ability->outputSchema()['required']);
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ use Brain\Monkey\Functions;
|
|||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostScheduleAbility;
|
use LIT\MCP\Integrations\WordPress\Status\PostScheduleAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
@@ -29,8 +29,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
|
|
||||||
private function ability(ChangeLog $log): PostScheduleAbility
|
private function ability(ChangeLog $log): PostScheduleAbility
|
||||||
{
|
{
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new PostScheduleAbility($schema, $log);
|
return new PostScheduleAbility($schema, $log);
|
||||||
}
|
}
|
||||||
@@ -45,13 +44,13 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
'edit_date' => true,
|
'edit_date' => true,
|
||||||
], true)->andReturn(5);
|
], true)->andReturn(5);
|
||||||
Functions\when('get_post_status')->justReturn('future');
|
Functions\when('get_post_status')->justReturn('future');
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(4);
|
||||||
$log->expects('record')->andReturn(4);
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
$result = $this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
||||||
|
|
||||||
self::assertSame('scheduled', $result['changed'][0]['action']);
|
self::assertSame('scheduled', $result['changed'][0]['action']);
|
||||||
self::assertStringContainsString('2099-06-01 09:30', $result['summary']);
|
self::assertStringContainsString('2099-06-01 09:30', $result['summary']);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWarnsThatAPrivatePostBecomesPublic(): void
|
public function testWarnsThatAPrivatePostBecomesPublic(): void
|
||||||
@@ -59,8 +58,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'post', 'post_title' => 'Intern', 'post_status' => 'private']));
|
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'post', 'post_title' => 'Intern', 'post_status' => 'private']));
|
||||||
Functions\when('wp_update_post')->justReturn(5);
|
Functions\when('wp_update_post')->justReturn(5);
|
||||||
Functions\when('get_post_status')->justReturn('future');
|
Functions\when('get_post_status')->justReturn('future');
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(4);
|
||||||
$log->allows('record')->andReturn(4);
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
$result = $this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
||||||
|
|
||||||
@@ -73,8 +71,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('wp_update_post')->justReturn(5);
|
Functions\when('wp_update_post')->justReturn(5);
|
||||||
Functions\when('get_post_status')->justReturn('future');
|
Functions\when('get_post_status')->justReturn('future');
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(4);
|
||||||
$log->allows('record')->andReturn(4);
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
$result = $this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
||||||
|
|
||||||
@@ -86,8 +83,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
Functions\when('wp_update_post')->alias(static function (): void {
|
Functions\when('wp_update_post')->alias(static function (): void {
|
||||||
throw new RuntimeException('save_post-Hook kaputt');
|
throw new RuntimeException('save_post-Hook kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->with('lit/post-schedule', 'post', '5', Mockery::type('string'));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
$this->ability($log)->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
||||||
@@ -98,6 +94,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
||||||
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
||||||
}
|
}
|
||||||
|
$log->assertPartials(['ability' => 'lit/post-schedule', 'object_type' => 'post', 'object_id' => '5']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testATrashedPostIsRefused(): void
|
public function testATrashedPostIsRefused(): void
|
||||||
@@ -105,7 +102,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'post', 'post_status' => 'trash']));
|
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'post', 'post_status' => 'trash']));
|
||||||
Functions\expect('wp_update_post')->never();
|
Functions\expect('wp_update_post')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class))->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
$result = $this->ability(FakeChangeLog::forbidden())->execute(['post_id' => 5, 'date' => '2099-06-01 09:30']);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -113,7 +110,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
public function testPastAndImpossibleDatesAreRejected(): void
|
public function testPastAndImpossibleDatesAreRejected(): void
|
||||||
{
|
{
|
||||||
Functions\expect('wp_update_post')->never();
|
Functions\expect('wp_update_post')->never();
|
||||||
$ability = $this->ability(Mockery::mock(ChangeLog::class));
|
$ability = $this->ability(FakeChangeLog::forbidden());
|
||||||
|
|
||||||
$past = $ability->execute(['post_id' => 5, 'date' => '2020-01-01 10:00']);
|
$past = $ability->execute(['post_id' => 5, 'date' => '2020-01-01 10:00']);
|
||||||
$impossible = $ability->execute(['post_id' => 5, 'date' => '2099-13-45 10:00']);
|
$impossible = $ability->execute(['post_id' => 5, 'date' => '2099-13-45 10:00']);
|
||||||
@@ -131,7 +128,7 @@ final class PostScheduleAbilityTest extends TestCase
|
|||||||
->setTimezone(new DateTimeZone('Europe/Berlin'));
|
->setTimezone(new DateTimeZone('Europe/Berlin'));
|
||||||
Functions\when('get_post_status')->justReturn('future');
|
Functions\when('get_post_status')->justReturn('future');
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class))
|
$result = $this->ability(FakeChangeLog::forbidden())
|
||||||
->execute(['post_id' => 5, 'date' => $soon->format('Y-m-d H:i')]);
|
->execute(['post_id' => 5, 'date' => $soon->format('Y-m-d H:i')]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Status;
|
|||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostTrashAbility;
|
use LIT\MCP\Integrations\WordPress\Status\PostTrashAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
@@ -33,8 +33,7 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_title' => 'Alt', 'post_status' => 'publish']));
|
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_title' => 'Alt', 'post_status' => 'publish']));
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new PostTrashAbility($schema, $log);
|
return new PostTrashAbility($schema, $log);
|
||||||
}
|
}
|
||||||
@@ -43,7 +42,7 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_trash_post')->never();
|
Functions\expect('wp_trash_post')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class))->execute(['post_id' => 5]);
|
$result = $this->ability(FakeChangeLog::forbidden())->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame('lit_confirm_required', $result->get_error_code());
|
self::assertSame('lit_confirm_required', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -52,13 +51,19 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_trash_post')->once()->with(5)->andReturn(new WP_Post(['ID' => 5]));
|
Functions\expect('wp_trash_post')->once()->with(5)->andReturn(new WP_Post(['ID' => 5]));
|
||||||
Functions\expect('wp_delete_post')->never();
|
Functions\expect('wp_delete_post')->never();
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(2);
|
||||||
$log->expects('record')->with('lit/post-trash', 'page', '5', '"Alt" in den Papierkorb gelegt', null)->andReturn(2);
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute(['post_id' => 5, 'confirm' => true]);
|
$result = $this->ability($log)->execute(['post_id' => 5, 'confirm' => true]);
|
||||||
|
|
||||||
self::assertSame('trashed', $result['changed'][0]['action']);
|
self::assertSame('trashed', $result['changed'][0]['action']);
|
||||||
self::assertStringContainsString('Papierkorb', $result['summary']);
|
self::assertStringContainsString('Papierkorb', $result['summary']);
|
||||||
|
$log->assertRecorded([
|
||||||
|
'ability' => 'lit/post-trash',
|
||||||
|
'object_type' => 'page',
|
||||||
|
'object_id' => '5',
|
||||||
|
'summary' => '"Alt" in den Papierkorb gelegt',
|
||||||
|
'revision_id' => null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRefusesWhenTheTrashIsDisabledInsteadOfDeleting(): void
|
public function testRefusesWhenTheTrashIsDisabledInsteadOfDeleting(): void
|
||||||
@@ -66,9 +71,8 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_status' => 'publish']));
|
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_status' => 'publish']));
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
Functions\expect('wp_trash_post')->never();
|
Functions\expect('wp_trash_post')->never();
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
$ability = new PostTrashAbility($schema, FakeChangeLog::forbidden(), static fn (): bool => false);
|
||||||
$ability = new PostTrashAbility($schema, Mockery::mock(ChangeLog::class), static fn (): bool => false);
|
|
||||||
|
|
||||||
$result = $ability->execute(['post_id' => 5, 'confirm' => true]);
|
$result = $ability->execute(['post_id' => 5, 'confirm' => true]);
|
||||||
|
|
||||||
@@ -80,8 +84,7 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
Functions\when('wp_trash_post')->alias(static function (): void {
|
Functions\when('wp_trash_post')->alias(static function (): void {
|
||||||
throw new RuntimeException('save_post-Hook kaputt');
|
throw new RuntimeException('save_post-Hook kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->with('lit/post-trash', 'page', '5', Mockery::type('string'));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ability($log)->execute(['post_id' => 5, 'confirm' => true]);
|
$this->ability($log)->execute(['post_id' => 5, 'confirm' => true]);
|
||||||
@@ -92,11 +95,12 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
||||||
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
||||||
}
|
}
|
||||||
|
$log->assertPartials(['ability' => 'lit/post-trash', 'object_type' => 'page', 'object_id' => '5']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAnAlreadyTrashedPostIsANoOp(): void
|
public function testAnAlreadyTrashedPostIsANoOp(): void
|
||||||
{
|
{
|
||||||
$ability = $this->ability(Mockery::mock(ChangeLog::class));
|
$ability = $this->ability(FakeChangeLog::forbidden());
|
||||||
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_title' => 'Alt', 'post_status' => 'trash']));
|
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_title' => 'Alt', 'post_status' => 'trash']));
|
||||||
Functions\expect('wp_trash_post')->never();
|
Functions\expect('wp_trash_post')->never();
|
||||||
|
|
||||||
@@ -110,8 +114,7 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->options(['show_on_front' => 'page', 'page_on_front' => '5', 'page_for_posts' => '9']);
|
$this->options(['show_on_front' => 'page', 'page_on_front' => '5', 'page_for_posts' => '9']);
|
||||||
Functions\when('wp_trash_post')->justReturn(new WP_Post(['ID' => 5]));
|
Functions\when('wp_trash_post')->justReturn(new WP_Post(['ID' => 5]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(3);
|
||||||
$log->allows('record')->andReturn(3);
|
|
||||||
$ability = $this->ability($log);
|
$ability = $this->ability($log);
|
||||||
|
|
||||||
$confirm = $ability->execute(['post_id' => 5]);
|
$confirm = $ability->execute(['post_id' => 5]);
|
||||||
@@ -141,8 +144,7 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
Functions\when('wp_trash_post')->justReturn(new WP_Post(['ID' => 5]));
|
Functions\when('wp_trash_post')->justReturn(new WP_Post(['ID' => 5]));
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(3);
|
||||||
$log->allows('record')->andReturn(3);
|
|
||||||
|
|
||||||
$result = $this->ability($log)->execute(['post_id' => 5, 'confirm' => true]);
|
$result = $this->ability($log)->execute(['post_id' => 5, 'confirm' => true]);
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ final class PostTrashAbilityTest extends TestCase
|
|||||||
|
|
||||||
public function testIsDestructiveAndIndirect(): void
|
public function testIsDestructiveAndIndirect(): void
|
||||||
{
|
{
|
||||||
$ability = $this->ability(Mockery::mock(ChangeLog::class));
|
$ability = $this->ability(FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertTrue($ability->annotations()['destructive']);
|
self::assertTrue($ability->annotations()['destructive']);
|
||||||
self::assertFalse($ability->isDirect());
|
self::assertFalse($ability->isDirect());
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Status;
|
|||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\WriteFailed;
|
use LIT\MCP\Abilities\WriteFailed;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
use LIT\MCP\ChangeLog\ChangeLog;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Status\PostUnpublishAbility;
|
use LIT\MCP\Integrations\WordPress\Status\PostUnpublishAbility;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
|
||||||
@@ -22,8 +22,7 @@ final class PostUnpublishAbilityTest extends TestCase
|
|||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
Functions\when('get_preview_post_link')->justReturn('https://kunde.de/?p=5&preview=true');
|
Functions\when('get_preview_post_link')->justReturn('https://kunde.de/?p=5&preview=true');
|
||||||
Functions\when('get_edit_post_link')->justReturn('https://kunde.de/wp-admin/post.php?post=5&action=edit');
|
Functions\when('get_edit_post_link')->justReturn('https://kunde.de/wp-admin/post.php?post=5&action=edit');
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
|
|
||||||
return new PostUnpublishAbility($schema, $log);
|
return new PostUnpublishAbility($schema, $log);
|
||||||
}
|
}
|
||||||
@@ -31,12 +30,12 @@ final class PostUnpublishAbilityTest extends TestCase
|
|||||||
public function testPublishedPostBecomesDraft(): void
|
public function testPublishedPostBecomesDraft(): void
|
||||||
{
|
{
|
||||||
Functions\expect('wp_update_post')->once()->with(['ID' => 5, 'post_status' => 'draft'], true)->andReturn(5);
|
Functions\expect('wp_update_post')->once()->with(['ID' => 5, 'post_status' => 'draft'], true)->andReturn(5);
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = new FakeChangeLog(3);
|
||||||
$log->expects('record')->andReturn(3);
|
|
||||||
|
|
||||||
$result = $this->ability($log, 'publish')->execute(['post_id' => 5]);
|
$result = $this->ability($log, 'publish')->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame('unpublished', $result['changed'][0]['action']);
|
self::assertSame('unpublished', $result['changed'][0]['action']);
|
||||||
|
$log->assertRecorded([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAHookThrowingDuringTheWriteIsAPartialWrite(): void
|
public function testAHookThrowingDuringTheWriteIsAPartialWrite(): void
|
||||||
@@ -44,8 +43,7 @@ final class PostUnpublishAbilityTest extends TestCase
|
|||||||
Functions\when('wp_update_post')->alias(static function (): void {
|
Functions\when('wp_update_post')->alias(static function (): void {
|
||||||
throw new RuntimeException('save_post-Hook kaputt');
|
throw new RuntimeException('save_post-Hook kaputt');
|
||||||
});
|
});
|
||||||
$log = Mockery::mock(ChangeLog::class);
|
$log = FakeChangeLog::partialsOnly();
|
||||||
$log->expects('recordPartial')->with('lit/post-unpublish', 'page', '5', Mockery::type('string'));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ability($log, 'publish')->execute(['post_id' => 5]);
|
$this->ability($log, 'publish')->execute(['post_id' => 5]);
|
||||||
@@ -56,13 +54,14 @@ final class PostUnpublishAbilityTest extends TestCase
|
|||||||
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
self::assertStringContainsString('save_post-Hook kaputt', $e->getMessage());
|
||||||
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
self::assertSame('lit_partial_write', $e->toError()->get_error_code());
|
||||||
}
|
}
|
||||||
|
$log->assertPartials(['ability' => 'lit/post-unpublish', 'object_type' => 'page', 'object_id' => '5']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testATrashedPostIsRefusedInsteadOfBecomingADraft(): void
|
public function testATrashedPostIsRefusedInsteadOfBecomingADraft(): void
|
||||||
{
|
{
|
||||||
Functions\expect('wp_update_post')->never();
|
Functions\expect('wp_update_post')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class), 'trash')->execute(['post_id' => 5]);
|
$result = $this->ability(FakeChangeLog::forbidden(), 'trash')->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame('lit_validation_failed', $result->get_error_code());
|
self::assertSame('lit_validation_failed', $result->get_error_code());
|
||||||
}
|
}
|
||||||
@@ -71,7 +70,7 @@ final class PostUnpublishAbilityTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\expect('wp_update_post')->never();
|
Functions\expect('wp_update_post')->never();
|
||||||
|
|
||||||
$result = $this->ability(Mockery::mock(ChangeLog::class), 'draft')->execute(['post_id' => 5]);
|
$result = $this->ability(FakeChangeLog::forbidden(), 'draft')->execute(['post_id' => 5]);
|
||||||
|
|
||||||
self::assertSame([], $result['changed']);
|
self::assertSame([], $result['changed']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ namespace LIT\MCP\Tests\Unit\Integrations\WordPress;
|
|||||||
|
|
||||||
use LIT\MCP\Abilities\Ability;
|
use LIT\MCP\Abilities\Ability;
|
||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\ChangeLog\ChangeLog;
|
|
||||||
use LIT\MCP\Integrations\WordPress\Content\ContentComposer;
|
use LIT\MCP\Integrations\WordPress\Content\ContentComposer;
|
||||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
use LIT\MCP\Integrations\WordPress\WordPressIntegration;
|
use LIT\MCP\Integrations\WordPress\WordPressIntegration;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Support\SchemaFixture;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ final class WordPressIntegrationTest extends TestCase
|
|||||||
|
|
||||||
public function testProvidesTheWordPressAbilitiesInOrder(): void
|
public function testProvidesTheWordPressAbilitiesInOrder(): void
|
||||||
{
|
{
|
||||||
$integration = new WordPressIntegration(Mockery::mock(ContentSchema::class), Mockery::mock(ChangeLog::class));
|
$integration = new WordPressIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
self::assertSame('wordpress', $integration->id());
|
self::assertSame('wordpress', $integration->id());
|
||||||
self::assertTrue($integration->isAvailable());
|
self::assertTrue($integration->isAvailable());
|
||||||
@@ -54,7 +55,7 @@ final class WordPressIntegrationTest extends TestCase
|
|||||||
|
|
||||||
public function testEveryAbilityHasAnObjectInputSchemaThatRejectsUnknownKeys(): void
|
public function testEveryAbilityHasAnObjectInputSchemaThatRejectsUnknownKeys(): void
|
||||||
{
|
{
|
||||||
$integration = new WordPressIntegration(Mockery::mock(ContentSchema::class), Mockery::mock(ChangeLog::class));
|
$integration = new WordPressIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
|
||||||
|
|
||||||
foreach ($integration->abilities() as $ability) {
|
foreach ($integration->abilities() as $ability) {
|
||||||
$schema = $ability->inputSchema();
|
$schema = $ability->inputSchema();
|
||||||
@@ -68,11 +69,10 @@ final class WordPressIntegrationTest extends TestCase
|
|||||||
{
|
{
|
||||||
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['create_posts' => 'edit_pages']]);
|
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) ['create_posts' => 'edit_pages']]);
|
||||||
Functions\when('current_user_can')->justReturn(true);
|
Functions\when('current_user_can')->justReturn(true);
|
||||||
$schema = Mockery::mock(ContentSchema::class);
|
$schema = SchemaFixture::managed();
|
||||||
$schema->allows('isManaged')->andReturn(true);
|
|
||||||
$composer = Mockery::mock(ContentComposer::class);
|
$composer = Mockery::mock(ContentComposer::class);
|
||||||
$composer->expects('available')->andReturn(false);
|
$composer->expects('available')->andReturn(false);
|
||||||
$integration = new WordPressIntegration($schema, Mockery::mock(ChangeLog::class), $composer);
|
$integration = new WordPressIntegration($schema, FakeChangeLog::forbidden(), $composer);
|
||||||
|
|
||||||
$create = array_values(array_filter(
|
$create = array_values(array_filter(
|
||||||
$integration->abilities(),
|
$integration->abilities(),
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Tests\Unit\Support;
|
||||||
|
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
|
use LogicException;
|
||||||
|
use PHPUnit\Framework\AssertionFailedError;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
final class FakeChangeLogTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testRecordsCallsWithIncreasingIds(): void
|
||||||
|
{
|
||||||
|
$log = new FakeChangeLog(41);
|
||||||
|
|
||||||
|
self::assertSame(41, $log->record('lit/a', 'page', '5', 'eins', 811));
|
||||||
|
self::assertSame(42, $log->record('lit/a', 'page', '6', 'zwei'));
|
||||||
|
$log->assertRecorded(
|
||||||
|
['object_id' => '5', 'summary' => 'eins', 'revision_id' => 811, 'result' => 'ok'],
|
||||||
|
['object_id' => '6', 'revision_id' => null]
|
||||||
|
);
|
||||||
|
self::assertSame(['eins', 'zwei'], $log->summaries());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAFailureIsThrownAfterTheCallIsNoted(): void
|
||||||
|
{
|
||||||
|
$log = new FakeChangeLog(1, new RuntimeException('db weg'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$log->record('lit/a', 'page', '5', 'x');
|
||||||
|
self::fail('RuntimeException expected');
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
self::assertSame('db weg', $e->getMessage());
|
||||||
|
}
|
||||||
|
$log->assertRecorded(['ability' => 'lit/a']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAssertRecordedChecksTheNumberOfCalls(): void
|
||||||
|
{
|
||||||
|
$log = new FakeChangeLog();
|
||||||
|
$log->record('lit/a', 'page', '5', 'x');
|
||||||
|
|
||||||
|
$this->expectException(AssertionFailedError::class);
|
||||||
|
$log->assertRecorded([], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAssertPartialsChecksTheNamedArguments(): void
|
||||||
|
{
|
||||||
|
$log = FakeChangeLog::partialsOnly();
|
||||||
|
$log->recordPartial('lit/a', 'page', '5', 'teilweise', 811);
|
||||||
|
$log->assertPartials(['ability' => 'lit/a', 'revision_id' => 811]);
|
||||||
|
|
||||||
|
$this->expectException(AssertionFailedError::class);
|
||||||
|
$log->assertPartials(['object_id' => '6']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testACallTheFakeDoesNotAllowThrowsAndFailsTheTestEvenWhenSwallowed(): void
|
||||||
|
{
|
||||||
|
$log = FakeChangeLog::forbidden();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$log->record('lit/a', 'page', '5', 'x');
|
||||||
|
self::fail('LogicException expected');
|
||||||
|
} catch (LogicException $e) {
|
||||||
|
self::assertStringContainsString('record(lit/a, page, 5)', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
FakeChangeLog::verify();
|
||||||
|
self::fail('verify() must fail');
|
||||||
|
} catch (AssertionFailedError $e) {
|
||||||
|
self::assertStringContainsString('Unexpected change log calls', $e->getMessage());
|
||||||
|
}
|
||||||
|
FakeChangeLog::verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecordOnlyFakesRefusePartialsAndPartialsOnlyFakesRefuseRecords(): void
|
||||||
|
{
|
||||||
|
foreach ([[new FakeChangeLog(), 'recordPartial'], [FakeChangeLog::partialsOnly(), 'record']] as [$log, $method]) {
|
||||||
|
try {
|
||||||
|
$log->$method('lit/a', 'page', '5', 'x');
|
||||||
|
self::fail($method . ' must be refused');
|
||||||
|
} catch (LogicException $e) {
|
||||||
|
self::assertStringContainsString($method, $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->expectException(AssertionFailedError::class);
|
||||||
|
$this->expectExceptionMessage('Unexpected change log calls: recordPartial(lit/a, page, 5); record(lit/a, page, 5)');
|
||||||
|
FakeChangeLog::verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAnyAllowsBoth(): void
|
||||||
|
{
|
||||||
|
$log = FakeChangeLog::any(9);
|
||||||
|
|
||||||
|
self::assertSame(9, $log->record('lit/a', 'page', '5', 'x'));
|
||||||
|
$log->recordPartial('lit/a', 'page', '5', 'y');
|
||||||
|
$log->assertPartials(['summary' => 'y']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ namespace LIT\MCP\Tests\Unit;
|
|||||||
|
|
||||||
use Brain\Monkey;
|
use Brain\Monkey;
|
||||||
use Brain\Monkey\Functions;
|
use Brain\Monkey\Functions;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
|
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
|
||||||
use PHPUnit\Framework\TestCase as BaseTestCase;
|
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||||
|
|
||||||
@@ -35,5 +36,6 @@ abstract class TestCase extends BaseTestCase
|
|||||||
{
|
{
|
||||||
Monkey\tearDown();
|
Monkey\tearDown();
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
|
FakeChangeLog::verify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user