Files
WP-LIT-MCP/tests/Unit/Integrations/WordPress/Schema/ContentSchemaTest.php
T
2026-09-30 00:54:48 +02:00

135 lines
5.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Schema;
use Brain\Monkey\Functions;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
use LIT\MCP\Integrations\WordPress\Schema\FieldDefinition;
use LIT\MCP\Integrations\WordPress\Schema\FieldSource;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
final class ContentSchemaTest extends TestCase
{
private function source(string $id, array $fields): FieldSource
{
$source = Mockery::mock(FieldSource::class);
$source->allows('id')->andReturn($id);
$source->allows('fieldsFor')->andReturn($fields);
return $source;
}
private function stubPostTypes(): void
{
Functions\when('get_post_types')->justReturn([
'post' => 'post',
'page' => 'page',
'attachment' => 'attachment',
'et_pb_layout' => 'et_pb_layout',
'lit_person' => 'lit_person',
]);
}
public function testPostTypeNamesExcludeInternalTypesAndAreSorted(): void
{
$this->stubPostTypes();
$schema = new ContentSchema([]);
self::assertSame(['lit_person', 'page', 'post'], $schema->postTypeNames());
self::assertTrue($schema->isManaged('page'));
self::assertFalse($schema->isManaged('et_pb_layout'));
}
public function testFieldsAreMergedByPriorityAndSorted(): void
{
$pods = $this->source('pods', [new FieldDefinition('role', 'Funktion', 'string', 'pods', 'writable', true)]);
$meta = $this->source('sample', [
new FieldDefinition('role', 'role', 'unknown', 'sample', 'unknown'),
new FieldDefinition('city', 'city', 'unknown', 'sample', 'unknown'),
]);
$schema = new ContentSchema([$pods, $meta]);
$fields = $schema->fields('lit_person');
self::assertSame(['city', 'role'], array_keys($fields));
self::assertSame('pods', $fields['role']->source);
self::assertTrue($fields['role']->required);
self::assertSame($pods, $schema->sourceOf($fields['role']));
self::assertSame($meta, $schema->sourceOf($fields['city']));
}
public function testDescribeIncludesTaxonomiesCountsDiviAndOptionalFields(): void
{
$this->stubPostTypes();
Functions\when('get_post_type_object')->justReturn((object) [
'hierarchical' => true,
'labels' => (object) ['name' => 'Seiten', 'singular_name' => 'Seite'],
]);
Functions\when('get_all_post_type_supports')->justReturn(['title' => true, 'editor' => true]);
Functions\when('wp_count_posts')->justReturn((object) ['publish' => 3, 'draft' => 2, 'trash' => 9]);
Functions\when('get_object_taxonomies')->justReturn([
(object) ['name' => 'category', 'show_ui' => true, 'hierarchical' => true, 'labels' => (object) ['name' => 'Kategorien']],
(object) ['name' => 'post_format', 'show_ui' => false, 'hierarchical' => false, 'labels' => (object) ['name' => 'Formate']],
]);
$schema = new ContentSchema(
[$this->source('meta', [
new FieldDefinition('footnotes', 'footnotes', 'string', 'meta', 'writable'),
new FieldDefinition('zz_hidden', 'Verborgen', 'string', 'pods', FieldDefinition::HIDDEN),
])],
static fn (string $type): bool => $type === 'page'
);
$withoutFields = $schema->describe('page', false);
$withFields = $schema->describe('page', true);
self::assertSame('Seiten', $withoutFields['label']);
self::assertSame('Seite', $withoutFields['singular_label']);
self::assertTrue($withoutFields['hierarchical']);
self::assertSame(['title', 'editor'], $withoutFields['supports']);
self::assertTrue($withoutFields['divi']);
self::assertSame(5, $withoutFields['count']);
self::assertSame([['name' => 'category', 'label' => 'Kategorien', 'hierarchical' => true]], $withoutFields['taxonomies']);
self::assertArrayNotHasKey('fields', $withoutFields);
self::assertSame('footnotes', $withFields['fields'][0]['key']);
self::assertCount(1, $withFields['fields'], 'hidden (restricted) fields are not described (M4)');
self::assertNull($schema->describe('et_pb_layout', false));
}
public function testMediaFieldKeysCoverFileFieldsAndMediaRelations(): void
{
Functions\when('get_post_types')->justReturn(['page' => 'page', 'lit_person' => 'lit_person']);
$source = Mockery::mock(FieldSource::class);
$source->allows('id')->andReturn('pods');
$source->allows('fieldsFor')->andReturnUsing(static fn (string $type): array => $type === 'lit_person'
? [
new FieldDefinition('photo', 'Foto', 'file', 'pods', 'writable'),
new FieldDefinition('logo', 'Logo', 'relation', 'pods', 'writable', false, false, null, [
'object_type' => 'media',
'name' => null,
]),
new FieldDefinition('role', 'Funktion', 'string', 'pods', 'writable'),
]
: [new FieldDefinition('photo', 'Foto', 'file', 'pods', 'writable')]);
self::assertSame(['logo', 'photo'], (new ContentSchema([$source]))->mediaFieldKeys());
}
/** Spec §3: Divi's post types come from the Katalog adapter – the list itself stays the same. */
public function testExcludedPostTypesStayTheSame(): void
{
self::assertSame(
[
'attachment', 'nav_menu_item', 'revision', 'wp_block', 'wp_template', 'wp_template_part',
'wp_navigation', 'wp_global_styles', 'wp_font_family', 'wp_font_face', 'et_pb_layout',
'et_theme_builder', 'et_template', 'et_header_layout', 'et_body_layout', 'et_footer_layout',
'et_pb_canvas', '_et_pb_speculation',
],
ContentSchema::EXCLUDED
);
}
}