From 031f01451b777663725e0ce8e9cfc3077a3cfeb2 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Sun, 27 Sep 2026 04:19:05 +0200 Subject: [PATCH] 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) --- .../Divi/Design/DiviDesignSystemAbility.php | 2 +- .../Divi/Library/LibraryListAbility.php | 17 ++++++++-- .../Design/DiviDesignSystemAbilityTest.php | 4 +++ .../Divi/Library/LibraryListAbilityTest.php | 34 +++++++++++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/Integrations/Divi/Design/DiviDesignSystemAbility.php b/src/Integrations/Divi/Design/DiviDesignSystemAbility.php index 1ecf8e8..065d50c 100644 --- a/src/Integrations/Divi/Design/DiviDesignSystemAbility.php +++ b/src/Integrations/Divi/Design/DiviDesignSystemAbility.php @@ -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, diff --git a/src/Integrations/Divi/Library/LibraryListAbility.php b/src/Integrations/Divi/Library/LibraryListAbility.php index e5b3e80..48f8862 100644 --- a/src/Integrations/Divi/Library/LibraryListAbility.php +++ b/src/Integrations/Divi/Library/LibraryListAbility.php @@ -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 $filters - * @return array{items: list>, next_cursor: ?string} + * @return array{items: list>, next_cursor: ?string, notes?: list} */ 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.' + )], + ]; } /** diff --git a/tests/Unit/Integrations/Divi/Design/DiviDesignSystemAbilityTest.php b/tests/Unit/Integrations/Divi/Design/DiviDesignSystemAbilityTest.php index 26b7cc7..c91a888 100644 --- a/tests/Unit/Integrations/Divi/Design/DiviDesignSystemAbilityTest.php +++ b/tests/Unit/Integrations/Divi/Design/DiviDesignSystemAbilityTest.php @@ -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'); } } diff --git a/tests/Unit/Integrations/Divi/Library/LibraryListAbilityTest.php b/tests/Unit/Integrations/Divi/Library/LibraryListAbilityTest.php index 6b6a24e..7cddffb 100644 --- a/tests/Unit/Integrations/Divi/Library/LibraryListAbilityTest.php +++ b/tests/Unit/Integrations/Divi/Library/LibraryListAbilityTest.php @@ -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([]);