fix: note the library-list scan cap; preset-cap note only with presets (M-11, M-12)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-27 04:19:05 +02:00
co-authored by Claude Opus 5.5
parent d8d7bb67be
commit 031f01451b
4 changed files with 54 additions and 3 deletions
@@ -152,7 +152,7 @@ final class DiviDesignSystemAbility implements Ability
static fn (array $preset): bool => $preset['module'] === $name
));
}
if (count($presets) > self::PRESET_LIMIT) {
if (in_array('presets', $only, true) && count($presets) > self::PRESET_LIMIT) {
$notes[] = sprintf(
'%d von %d Presets gezeigt; mit module auf ein Modul eingrenzen.',
self::PRESET_LIMIT,
@@ -105,6 +105,7 @@ final class LibraryListAbility implements Ability
'affected' => ['type' => 'array', 'items' => ['type' => 'object']],
'affected_count' => ['type' => 'integer'],
'hidden_count' => ['type' => 'integer'],
'notes' => ['type' => 'array', 'items' => ['type' => 'string']],
],
'required' => ['items', 'next_cursor'],
];
@@ -174,7 +175,7 @@ final class LibraryListAbility implements Ability
* null when no readable entry follows (or the scan cap is reached with nothing found).
*
* @param array<string, string|int> $filters
* @return array{items: list<array<string, mixed>>, next_cursor: ?string}
* @return array{items: list<array<string, mixed>>, next_cursor: ?string, notes?: list<string>}
*/
private function page(array $filters, int $limit, int $skip): array
{
@@ -199,7 +200,19 @@ final class LibraryListAbility implements Ability
}
}
return ['items' => $items, 'next_cursor' => $items === [] ? null : Cursor::encode($skip + count($items))];
// Review M-12: say why the page is short, so the client neither thinks the list ended nor loops.
return [
'items' => $items,
'next_cursor' => $items === [] ? null : Cursor::encode($skip + count($items)),
'notes' => [sprintf(
'Nach %d gelesenen Bibliothekseinträgen abgebrochen (Einträge ohne Bearbeitungsrecht werden '
. 'übersprungen); %s',
self::MAX_SCAN,
$items === []
? 'weitere Einträge mit Filtern (layout_type, scope, search, category) eingrenzen.'
: 'mit next_cursor weiterblättern oder mit Filtern eingrenzen.'
)],
];
}
/**
@@ -75,5 +75,9 @@ final class DiviDesignSystemAbilityTest extends TestCase
self::assertCount(DiviDesignSystemAbility::PRESET_LIMIT, $result['presets']);
self::assertContains('200 von 250 Presets gezeigt; mit module auf ein Modul eingrenzen.', $result['notes']);
$colors = (new DiviDesignSystemAbility($reader))->execute(['only' => ['colors']]);
self::assertSame([], $colors['presets']);
self::assertStringNotContainsString('Presets gezeigt', implode(' ', $colors['notes']), 'not requested');
}
}
@@ -14,6 +14,7 @@ use LIT\MCP\Tests\Support\LibraryFixtures;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
use WP_Error;
use WP_Post;
final class LibraryListAbilityTest extends TestCase
{
@@ -171,6 +172,39 @@ final class LibraryListAbilityTest extends TestCase
self::assertSame(['items' => [], 'next_cursor' => null], $ability->execute(['search' => 'Geheim']));
}
/** Review M-12: when the scan cap stops a page, the answer says so. */
public function testSaysWhenTheScanCapIsReached(): void
{
LibraryFixtures::given([]);
Functions\when('current_user_can')->alias(
static fn (string $cap, mixed ...$args): bool => ! ($cap === 'edit_post' && ($args[0] ?? 0) >= 5000)
);
Functions\when('get_post_meta')->justReturn('');
Functions\when('wp_get_object_terms')->justReturn([]);
$hidden = array_map(
static fn (int $id): WP_Post => new WP_Post(['ID' => $id, 'post_type' => 'et_pb_layout', 'post_title' => 'x']),
range(5000, 5000 + LibraryListAbility::MAX_SCAN + 10)
);
$posts = array_merge([LibraryFixtures::item(1055)], $hidden, [LibraryFixtures::item(1056)]);
$store = LibraryFixtures::store();
$store->allows('find')->andReturnUsing(
static fn (array $filters, int $limit, int $offset): array => array_slice($posts, $offset, $limit + 1)
);
$access = LibraryFixtures::access([], true, $store);
$ability = new LibraryListAbility(
$access,
LibraryFixtures::loader($access),
DiviWriterFixture::designReader(),
LibraryFixtures::usages([])
);
$result = $ability->execute(['limit' => 5]);
self::assertSame([1055], array_column($result['items'], 'id'));
self::assertSame(Cursor::encode(1), $result['next_cursor']);
self::assertStringContainsString('500', $result['notes'][0]);
}
public function testNoNextCursorWhenOnlyHiddenEntriesFollow(): void
{
LibraryFixtures::given([]);