fix: address code review findings for bookmark feature (H1–L5)
Fix broken tests (H1), align interface signatures with implementations (H3), remove dead code (H4), add missing input guard (M1), replace raw $_SESSION access with SessionStoreInterface (M2), sync update script schema (M4), use shared getAppBase utility (L2), document fragment stripping (L3), and apply app- CSS class prefix convention (L5). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -94,27 +94,17 @@ class BookmarkServiceTest extends TestCase
|
||||
$this->assertSame('updated', $result['mode']);
|
||||
}
|
||||
|
||||
public function testSaveBookmarkUpsertsLegacyBookmarkByCanonicalUrl(): void
|
||||
public function testSaveBookmarkCreatesWhenNoExactUrlMatch(): void
|
||||
{
|
||||
$this->bookmarkRepo->expects($this->once())->method('findByUserAndUrl')->with(1, 'admin/users?a=1&b=2')->willReturn(false);
|
||||
$this->bookmarkRepo->expects($this->once())->method('listByUser')->with(1)->willReturn([
|
||||
['id' => 7, 'url' => 'admin/users?b=2&a=1'],
|
||||
]);
|
||||
$this->bookmarkRepo->expects($this->once())->method('update')->with(
|
||||
7,
|
||||
1,
|
||||
[
|
||||
'name' => 'Users',
|
||||
'group_id' => null,
|
||||
'url' => 'admin/users?a=1&b=2',
|
||||
]
|
||||
)->willReturn(true);
|
||||
$this->bookmarkRepo->expects($this->never())->method('create');
|
||||
$this->bookmarkRepo->method('countByUser')->willReturn(0);
|
||||
$this->navigationRepo->method('nextRootSortOrder')->willReturn(1);
|
||||
$this->bookmarkRepo->expects($this->once())->method('create')->willReturn(7);
|
||||
|
||||
$result = $this->service->saveBookmark(1, 'Users', 'admin/users?a=1&b=2', null);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertSame('updated', $result['mode']);
|
||||
$this->assertSame('created', $result['mode']);
|
||||
$this->assertSame(7, $result['bookmark']['id']);
|
||||
}
|
||||
|
||||
@@ -338,22 +328,46 @@ class BookmarkServiceTest extends TestCase
|
||||
$this->assertSame('bi-heart', $result['group']['icon']);
|
||||
}
|
||||
|
||||
public function testDeleteGroupClearsBookmarks(): void
|
||||
public function testDeleteGroupUnsetsGroupAndDeletes(): void
|
||||
{
|
||||
$this->bookmarkRepo->expects($this->once())->method('clearGroupId')->with(5, 1)->willReturn(true);
|
||||
$this->groupRepo->expects($this->any())->method('delete')->with(5, 1)->willReturn(true);
|
||||
$this->bookmarkRepo->expects($this->once())->method('listByGroup')->with(1, 5)->willReturn([
|
||||
['id' => 10, 'name' => 'A', 'url' => 'admin', 'group_id' => 5, 'sort_order' => 1],
|
||||
['id' => 11, 'name' => 'B', 'url' => 'admin/users', 'group_id' => 5, 'sort_order' => 2],
|
||||
]);
|
||||
$this->navigationRepo->expects($this->once())->method('nextRootSortOrder')->with(1)->willReturn(20);
|
||||
$this->bookmarkRepo->expects($this->exactly(2))->method('unsetGroupId')
|
||||
->willReturnCallback(fn (int $bmId, int $userId, int $sort) => match ([$bmId, $userId]) {
|
||||
[10, 1] => $sort === 20,
|
||||
[11, 1] => $sort === 21,
|
||||
default => false,
|
||||
});
|
||||
$this->groupRepo->expects($this->once())->method('delete')->with(5, 1)->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->service->deleteGroup(1, 5));
|
||||
}
|
||||
|
||||
public function testDeleteGroupReturnsFalseWhenClearFails(): void
|
||||
public function testDeleteGroupReturnsFalseWhenUnsetFails(): void
|
||||
{
|
||||
$this->bookmarkRepo->expects($this->once())->method('clearGroupId')->with(5, 1)->willReturn(false);
|
||||
$this->bookmarkRepo->expects($this->once())->method('listByGroup')->with(1, 5)->willReturn([
|
||||
['id' => 10, 'name' => 'A', 'url' => 'admin', 'group_id' => 5, 'sort_order' => 1],
|
||||
]);
|
||||
$this->navigationRepo->expects($this->once())->method('nextRootSortOrder')->with(1)->willReturn(20);
|
||||
$this->bookmarkRepo->expects($this->once())->method('unsetGroupId')->with(10, 1, 20)->willReturn(false);
|
||||
$this->groupRepo->expects($this->never())->method('delete');
|
||||
|
||||
$this->assertFalse($this->service->deleteGroup(1, 5));
|
||||
}
|
||||
|
||||
public function testDeleteGroupWithNoBookmarksDeletesDirectly(): void
|
||||
{
|
||||
$this->bookmarkRepo->expects($this->once())->method('listByGroup')->with(1, 5)->willReturn([]);
|
||||
$this->navigationRepo->expects($this->once())->method('nextRootSortOrder')->with(1)->willReturn(1);
|
||||
$this->bookmarkRepo->expects($this->never())->method('unsetGroupId');
|
||||
$this->groupRepo->expects($this->once())->method('delete')->with(5, 1)->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->service->deleteGroup(1, 5));
|
||||
}
|
||||
|
||||
public function testReorderGroupsDelegatesToRepository(): void
|
||||
{
|
||||
$pairs = [
|
||||
@@ -413,6 +427,60 @@ class BookmarkServiceTest extends TestCase
|
||||
$this->assertArrayNotHasKey('icon', $result['ungrouped'][0]);
|
||||
}
|
||||
|
||||
public function testReorderFromItemsRejectsInvalidType(): void
|
||||
{
|
||||
$result = $this->service->reorderFromItems(1, 'invalid', []);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('invalid_type', $result['error']);
|
||||
}
|
||||
|
||||
public function testReorderFromItemsRejectsEmptyItems(): void
|
||||
{
|
||||
$result = $this->service->reorderFromItems(1, 'bookmarks', []);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('invalid_items', $result['error']);
|
||||
}
|
||||
|
||||
public function testReorderFromItemsBookmarksHappyPath(): void
|
||||
{
|
||||
$this->bookmarkRepo->expects($this->once())->method('updateSortOrders')->with(1, [
|
||||
['id' => 3, 'sort_order' => 1],
|
||||
['id' => 7, 'sort_order' => 2],
|
||||
])->willReturn(true);
|
||||
|
||||
$result = $this->service->reorderFromItems(1, 'bookmarks', [
|
||||
['id' => 3, 'sort_order' => 1],
|
||||
['id' => 7, 'sort_order' => 2],
|
||||
]);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
}
|
||||
|
||||
public function testReorderRootFromItemsHappyPath(): void
|
||||
{
|
||||
$this->navigationRepo->expects($this->once())->method('updateRootSortOrders')->with(1, [
|
||||
['kind' => 'group', 'id' => 2, 'sort_order' => 1],
|
||||
['kind' => 'bookmark', 'id' => 5, 'sort_order' => 2],
|
||||
])->willReturn(true);
|
||||
|
||||
$result = $this->service->reorderRootFromItems(1, [
|
||||
['kind' => 'group', 'id' => 2, 'sort_order' => 1],
|
||||
['kind' => 'bookmark', 'id' => 5, 'sort_order' => 2],
|
||||
]);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
}
|
||||
|
||||
public function testReorderRootFromItemsRejectsEmptyItems(): void
|
||||
{
|
||||
$result = $this->service->reorderRootFromItems(1, []);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('invalid_items', $result['error']);
|
||||
}
|
||||
|
||||
public function testAllowedGroupIconsReturnsNonEmptyList(): void
|
||||
{
|
||||
$icons = BookmarkService::allowedGroupIcons();
|
||||
|
||||
Reference in New Issue
Block a user