More tests

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
This commit is contained in:
Marcel Klehr 2019-10-19 14:35:19 +02:00
parent e6534532b7
commit 915aacd32c
6 changed files with 340 additions and 55 deletions

View File

@ -7,6 +7,7 @@
"psr/http-message": "^1.0",
"psr/http-factory": "^1.0",
"psr/http-client": "^0.2.0",
"rowbot/url": "^2.0"
}
"rowbot/url": "^2.0",
"ext-pdo": "*"
}
}

View File

@ -39,19 +39,17 @@ class BookmarkMapper extends QBMapper {
/**
* Find a specific bookmark by Id
*
* @param int $userId
* @param int $id
* @return Entity
* @throws DoesNotExistException if not found
* @throws MultipleObjectsReturnedException if more than one result
*/
public function find(int $userId, int $id) : Entity {
public function find(int $id) : Entity {
$qb = $this->db->getQueryBuilder();
$qb
->select('*')
->from('bookmarks')
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
->andWhere($qb->expr()->eq('id', $qb->createNamedParameter($id)));
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
return $this->findEntity($qb);
}
@ -93,6 +91,8 @@ class BookmarkMapper extends QBMapper {
if (!in_array($sortBy, $tableAttributes)) {
$sqlSortColumn = 'lastmodified';
}else{
$sqlSortColumn = $sortBy;
}
$qb = $this->db->getQueryBuilder();
@ -155,13 +155,11 @@ class BookmarkMapper extends QBMapper {
} else {
$expr[] = $qb->expr()->iLike('tags', $qb->createPositionalParameter('%'.$this->db->escapeLikeParameter($filter).'%'));
}
if (!$filterTagOnly) {
foreach ($otherColumns as $col) {
$expr[] = $qb->expr()->iLike(
$qb->createFunction($qb->getColumnName($col)),
$qb->createPositionalParameter('%' . $this->db->escapeLikeParameter(strtolower($filter)) . '%')
);
}
foreach ($otherColumns as $col) {
$expr[] = $qb->expr()->iLike(
$qb->createFunction($qb->getColumnName($col)),
$qb->createPositionalParameter('%' . $this->db->escapeLikeParameter(strtolower($filter)) . '%')
);
}
$filterExpressions[] = call_user_func_array([$qb->expr(), 'orX'], $expr);
$i++;
@ -175,10 +173,11 @@ class BookmarkMapper extends QBMapper {
}
/**
* @param int $userId
* @param string $tag
* @return array|Entity[]
*/
public function findByTag(string $tag) {
public function findByTag(int $userId, string $tag) {
$qb = $this->db->getQueryBuilder();
$qb->select('*');
@ -215,7 +214,7 @@ class BookmarkMapper extends QBMapper {
$qb->select('*')
->from('bookmarks', 'b')
->leftJoin('b', 'bookmarks_folders_bookmarks', 'f', $qb->expr()->eq('f.bookmark_id', 'b.id'))
->where($qb->expr()->eq('f.folder_id', $qb->createPositionalParameter(-1)));
->where($qb->expr()->eq('f.folder_id', $qb->createPositionalParameter(-1)))
->where($qb->expr()->eq('b.user_id', $qb->createPositionalParameter($userId)));
return $this->findEntities($qb);
}

View File

@ -1,9 +1,12 @@
<?php
namespace OCA\Bookmarks\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\AppFramework\Db\QBMapper;
use OCP\AppFramework\Db\Entity;
class FolderMapper extends QBMapper {
@ -16,33 +19,35 @@ class FolderMapper extends QBMapper {
}
/**
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result
* @param int $id
* @return Folder
* @throws DoesNotExistException if not found
* @throws MultipleObjectsReturnedException if more than one result
*/
public function find(int $id) : Folder {
public function find(int $id) : Entity {
$qb = $this->db->getQueryBuilder();
$qb
->select('id', 'parent_folder', 'title', 'user_id')
->select('*')
->from('bookmarks_folders')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
return $this->findEntity($qb);
}
public function findByParentFolder(int $folderId) {
public function findByParentFolder(int $folderId) {
$qb = $this->db->getQueryBuilder();
$qb
->select('id', 'title', 'parent_folder')
->select('*')
->from('bookmarks_folders')
->where($qb->expr()->eq('parent_folder', $qb->createPositionalParameter($folderId)))
->orderBy('title', 'DESC');
return $this->findEntities($qb);
}
public function findByRootFolder(int $userId) {
public function findByRootFolder(int $userId) {
$qb = $this->db->getQueryBuilder();
$qb
->select('id', 'title', 'parent_folder')
->select('*')
->from('bookmarks_folders')
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
->andWhere($qb->expr()->eq('parent_folder', $qb->createPositionalParameter(-1)))
@ -50,16 +55,16 @@ class FolderMapper extends QBMapper {
return $this->findEntities($qb);
}
public function delete(Folder $entity) {
public function delete(Entity $entity) : Entity {
$childFolders = $this->findByParentFolder($entity->id);
foreach ($childFolders as $folder) {
$this->delete($folder);
}
$childBookmarks = $this->bookmarkMapper->findByFolder($entity->id)
$childBookmarks = $this->bookmarkMapper->findByFolder($entity->id);
foreach ($childBookmarks as $bookmark) {
$this->bookmarkMapper->delete($bookmark);
}
parent::delete($entity);
return parent::delete($entity);
}
public function deleteAll(int $userId) {
@ -67,30 +72,29 @@ class FolderMapper extends QBMapper {
foreach ($childFolders as $folder) {
$this->delete($folder);
}
$childBookmarks = $this->bookmarkMapper->findByRootFolder($userId)
$childBookmarks = $this->bookmarkMapper->findByRootFolder($userId);
foreach ($childBookmarks as $bookmark) {
$this->bookmarkMapper->delete($bookmark);
}
parent::delete($entity);
}
public function update(Folder $entity) {
if ($entity->parentFolder !== -1) {
$this->find($entity->parentFolder)
public function update(Entity $entity) : Entity {
if ($entity->getParentFolder() !== -1) {
$this->find($entity->getParentFolder());
}
parent::insertOrUpdate($entity);
return parent::update($entity);
}
public function insertOrUpdate(Folder $entity) {
if ($entity->parentFolder !== -1) {
$this->find($entity->parentFolder)
public function insertOrUpdate(Entity $entity) : Entity {
if ($entity->getParentFolder() !== -1) {
$this->find($entity->getParentFolder());
}
$newEntity = parent::insertOrUpdate($entity);
return parent::insertOrUpdate($entity);
}
public function insert(Folder $entity) {
if ($entity->parentFolder !== -1) {
$this->find($entity->parentFolder)
public function insert(Entity $entity) : Entity {
if ($entity->getParentFolder() !== -1) {
$this->find($entity->getParentFolder());
}
return parent::insert($entity);
}
@ -102,15 +106,14 @@ class FolderMapper extends QBMapper {
$qb
->from('bookmarks_folders', 'f')
->leftJoin('b', 'bookmarks_folders_bookmarks', 'f', $qb->expr()->eq('b.bookmark_id', 'f.id'))
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
->andWhere($qb->expr()->eq('b.bookmark_id', $qb->createPositionalParamter($bookmarkId)));
->where($qb->expr()->eq('b.bookmark_id', $qb->createPositionalParamter($bookmarkId)));
return $this->findEntities($qb);
}
/**
* @brief Lists bookmark folders' child folders (helper)
* @param int $root Root folder from which to return hierarchy, -1 for absolute root
* @param $folderId
* @param int $layers The amount of levels to return
* @return array the children each in the format ["id" => int, "type" => 'bookmark' | 'folder' ]
*/
@ -150,9 +153,53 @@ class FolderMapper extends QBMapper {
return $children;
}
/**
* @brief Lists bookmark folders' child folders (helper)
* @param int $userId
* @param int $layers The amount of levels to return
* @return array the children each in the format ["id" => int, "type" => 'bookmark' | 'folder' ]
*/
public function getRootChildren(int $userId, $layers = 1) {
$qb = $this->db->getQueryBuilder();
$qb
->select('id', 'title', 'parent_folder', 'index')
->from('bookmarks_folders')
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
->andWhere($qb->expr()->eq('parent_folder', $qb->createPositionalParameter(-1)))
->orderBy('index', 'ASC');
$childFolders = $qb->execute()->fetchAll();
$qb = $this->db->getQueryBuilder();
$qb
->select('bookmark_id', 'index')
->from('bookmarks_folders_bookmarks', 'f')
->innerJoin('f', 'bookmarks', 'b', $qb->expr()->eq('b.id', 'f.bookmark_id'))
->where($qb->expr()->eq('folder_id', $qb->createPositionalParameter(-1)))
->andWhere($qb->expr()->eq('b.user_id', $qb->createPositionalParameter($userId)))
->orderBy('index', 'ASC');
$childBookmarks = $qb->execute()->fetchAll();
$children = array_merge($childFolders, $childBookmarks);
array_multisort(array_column($children, 'index'), \SORT_ASC, $children);
$children = array_map(function ($child) use ($layers) {
return isset($child['bookmark_id'])
? ['type' => self::TYPE_BOOKMARK, 'id' => $child['bookmark_id']]
: ($layers === 1
? ['type' => self::TYPE_FOLDER, 'id' => $child['id']]
: [
'type' => self::TYPE_FOLDER,
'id' => $child['id'],
'children' => $this->getChildren($child['id'], $layers-1)
]);
}, $children);
return $children;
}
public function hashFolder(Folder $entity, $fields = ['title', 'url']) {
$children = $this->getChildren($entity->id);
$childHashes = array_map(function ($item) use ($userId, $fields) {
$childHashes = array_map(function ($item) use ($fields) {
switch ($item['type']) {
case self::TYPE_BOOKMARK:
return $this->bookmarkMapper->hashBookmark($item['id'], $fields);
@ -163,10 +210,93 @@ class FolderMapper extends QBMapper {
}
}, $children);
$folder = [];
if (isset($entity->title)) {
$folder['title'] = $entity->title;
if ($entity->getTitle() !== null) {
$folder['title'] = $entity->getTitle();
}
$folder['children'] = $childHashes;
return hash('sha256', json_encode($folder, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
/**
* @brief Add a bookmark to a set of folders
* @param int $bookmarkId The bookmark reference
* @param array $folders Set of folders ids to add the bookmark to
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function addToFolders(int $bookmarkId, array $folders) {
$bookmark = $this->bookmarkMapper->find($bookmarkId);
foreach ($folders as $folderId) {
// check if folder exists
if ($folderId !== -1 && $folderId !== '-1') {
$this->find($folderId);
}
// check if this folder<->bookmark mapping already exists
$qb = $this->db->getQueryBuilder();
$qb
->select('*')
->from('bookmarks_folders_bookmarks')
->where($qb->expr()->eq('bookmark_id', $qb->createNamedParameter($bookmarkId)))
->andWhere($qb->expr()->eq('folder_id', $qb->createNamedParameter($folderId)));
if ($qb->execute()->fetch()) {
continue;
}
$qb = $this->db->getQueryBuilder();
$qb
->insert('bookmarks_folders_bookmarks')
->values([
'folder_id' => $qb->createNamedParameter($folderId),
'bookmark_id' => $qb->createNamedParameter($bookmarkId),
'index' => $folderId !== -1 ? count($this->getChildren($folderId)) : count($this->getRootChildren($bookmark->getUserId()))
]);
$qb->execute();
}
}
/**
* @brief Remove a bookmark from a set of folders
* @param int $bookmarkId The bookmark reference
* @param array $folders Set of folders ids to add the bookmark to
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function removeFromFolders(int $bookmarkId, array $folders) {
$bm = $this->bookmarkMapper->find($bookmarkId);
$foldersLeft = count($this->findByBookmark($bookmarkId));
foreach ($folders as $folderId) {
// check if folder exists
if ($folderId !== -1 && $folderId !== '-1') {
$this->find($folderId);
}
// check if this folder<->bookmark mapping exists
$qb = $this->db->getQueryBuilder();
$qb
->select('*')
->from('bookmarks_folders_bookmarks')
->where($qb->expr()->eq('bookmark_id', $qb->createNamedParameter($bookmarkId)))
->andWhere($qb->expr()->eq('folder_id', $qb->createNamedParameter($folderId)));
if (!$qb->execute()->fetch()) {
continue;
}
$qb = $this->db->getQueryBuilder();
$qb
->delete('bookmarks_folders_bookmarks')
->where($qb->expr()->eq('folder_id', $qb->createNamedParameter($folderId)))
->andwhere($qb->expr()->eq('bookmark_id', $qb->createNamedParameter($bookmarkId)));
$qb->execute();
$foldersLeft--;
}
if ($foldersLeft <= 0) {
$this->bookmarkMapper->delete($bm);
}
}
}

View File

@ -5,10 +5,12 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\AppFramework\Db\QBMapper;
class TagMapper extends QBMapper {
class TagMapper {
protected $db;
public function __construct(IDBConnection $db) {
parent::__construct($db, 'bookmarks_tags');
$this->db = $db;
}
public function findAllWithCount(int $userId) {
@ -57,7 +59,7 @@ class TagMapper extends QBMapper {
->innerJoin('tgs', 'bookmarks', 'bm', $qb->expr()->eq('tgs.bookmark_id', 'bm.id'))
->where($qb->expr()->eq('tgs.tag', $qb->createNamedParameter($tag)))
->andWhere($qb->expr()->eq('bm.user_id', $qb->createNamedParameter($userId)));
return $qb->execute();
return $qb->execute();
}
public function deleteAll(int $userId) {
@ -66,7 +68,7 @@ class TagMapper extends QBMapper {
->delete('bookmarks_tags', 'tgs')
->innerJoin('tgs', 'bookmarks', 'bm', $qb->expr()->eq('tgs.bookmark_id', 'bm.id'))
->where($qb->expr()->eq('bm.user_id', $qb->createNamedParameter($userId)));
return $qb->execute();
return $qb->execute();
}
public function addTo($tags, int $bookmarkId) {
@ -120,7 +122,7 @@ class TagMapper extends QBMapper {
/**
* @brief Rename a tag
* @param string $userId UserId
* @param int $userId UserId
* @param string $old Old Tag Name
* @param string $new New Tag Name
* @return boolean Success of operation

View File

@ -8,6 +8,7 @@ use OCA\Bookmarks\Db\Bookmark;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\User;
class BookmarkMapperTest extends TestCase {
@ -22,9 +23,10 @@ class BookmarkMapperTest extends TestCase {
*/
private $userId;
protected function setUp() {
protected function setUp() : void {
parent::setUp();
$this->bookmarkMapper = \OC::$server->query(Db\BookmarkMapper::class);
$this->userId = User::getUser();
}
/**
@ -35,8 +37,9 @@ class BookmarkMapperTest extends TestCase {
* @throws MultipleObjectsReturnedException
*/
public function testInsertAndFind(Entity $bookmark) {
$bookmark->setUserId($this->userId);
$bookmark = $this->bookmarkMapper->insert($bookmark);
$foundEntity = $this->bookmarkMapper->find($bookmark->getUserId(), $bookmark->getId());
$foundEntity = $this->bookmarkMapper->find($bookmark->getId());
$this->assertSame($bookmark->getUrl(), $foundEntity->getUrl());
$this->assertSame($bookmark->getTitle(), $foundEntity->getTitle());
$this->assertSame($bookmark->getDescription(), $foundEntity->getDescription());
@ -55,6 +58,23 @@ class BookmarkMapperTest extends TestCase {
$this->assertSame($bookmark->getUrl(), $foundEntity->getUrl());
}
/**
* @depends testInsertAndFind
* @depends testFindByUrl
* @dataProvider singleBookmarks
* @param Entity $bookmark
* @return void
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function testUpdate(Entity $bookmark) {
$entity = $this->bookmarkMapper->findByUrl($bookmark->getUserId(), $bookmark->getUrl());
$entity->setTitle('foobar');
$this->bookmarkMapper->update($entity);
$foundEntity = $this->bookmarkMapper->find($entity->getId());
$this->assertSame($entity->title, $foundEntity->title);
}
/**
* @depends testInsertAndFind
* @depends testFindByUrl
@ -78,9 +98,10 @@ class BookmarkMapperTest extends TestCase {
return array_map(function($props) {
return Db\Bookmark::fromArray($props);
}, [
'Simple URL with title and description' => ['url' => 'https://google.com/', 'title' => 'Google', 'description' => 'Search engine', 'userId' => $this->userId],
'Simple URL with title' => ['url' => 'https://nextcloud.com/', 'title' => 'Nextcloud', 'userId' => $this->userId],
'Simple URL' => ['url' => 'https://php.net/', 'userId' => $this->userId],
'Simple URL with title and description' => ['url' => 'https://google.com/', 'title' => 'Google', 'description' => 'Search engine'],
'Simple URL with title' => ['url' => 'https://nextcloud.com/', 'title' => 'Nextcloud'],
'Simple URL' => ['url' => 'https://php.net/'],
'URL with unicode' => ['url' => 'https://de.wikipedia.org/wiki/Ü'],
]);
}
}

132
tests/FolderMapperTest.php Normal file
View File

@ -0,0 +1,132 @@
<?php
namespace OCA\Bookmarks\Tests;
use OCA\Bookmarks\Db;
use OCA\Bookmarks\Db\Bookmark;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\User;
class FolderMapperTest extends TestCase {
/**
* @var Db\BookmarkMapper
*/
private $bookmarkMapper;
/**
* @var Db\FolderMapper
*/
private $folderMapper;
/**
* @var string
*/
private $userId;
protected function setUp() : void {
parent::setUp();
$this->bookmarkMapper = \OC::$server->query(Db\BookmarkMapper::class);
$this->folderMapper = \OC::$server->query(Db\FolderMapper::class);
$this->userId = User::getUser();
}
/**
* @return void
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function testInsertAndFind() {
$folder = new Db\Folder();
$folder->setTitle('foobar');
$folder->setParentFolder(-1);
$folder->setUserId($this->userId);
$insertedFolder = $this->folderMapper->insert($folder);
$foundEntity = $this->folderMapper->find($insertedFolder->getId());
$this->assertSame($foundEntity->getTitle(), $foundEntity->getTitle());
$this->assertSame($foundEntity->getParentFolder(), $foundEntity->gettParentFolder());
return $insertedFolder;
}
/**
* @depends testInsertAndFind
* @param Entity $folder
* @return void
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function testUpdate(Entity $folder) {
$folder->setTitle('barbla');
$this->folderMapper->update($folder);
$foundEntity = $this->folderMapper->find($folder->getId());
$this->assertSame($folder->title, $foundEntity->getTitle());
}
/**
* @depends testInsertAndFind
* @param Entity $folder
* @return void
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function testDelete(Entity $folder) {
$this->folderMapper->delete($folder);
$this->expectException(DoesNotExistException::class);
$this->folderMapper->find($folder->getId());
}
/**
* @depends testInsertAndFind
* @dataProvider singleBookmarks
* @param Entity $folder
* @param Bookmark $bookmark
* @return void
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function testAddBookmarks(Entity $folder, Bookmark $bookmark) {
$insertedBookmark = $this->bookmarkMapper->insertOrUpdate($bookmark);
$this->folderMapper->addToFolders($insertedBookmark->getId(), [$folder->getId()]);
$bookmarks = $this->bookmarkMapper->findByFolder($folder->getId());
$this->assertContains($insertedBookmark->getId(), array_map(function($bookmark) {
return $bookmark->getId();
}, $bookmarks));
}
/**
* @depends testInsertAndFind
* @dataProvider singleBookmarks
* @param Entity $folder
* @param Bookmark $bookmark
* @return void
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function testRemoveBookmarks(Entity $folder, Bookmark $bookmark) {
$insertedBookmark = $this->bookmarkMapper->insertOrUpdate($bookmark);
$this->folderMapper->removeFromFolders($insertedBookmark->getId(), [$folder->getId()]);
$bookmarks = $this->bookmarkMapper->findByFolder($folder->getId());
$this->assertNotContains($insertedBookmark->getId(), array_map(function($bookmark) {
return $bookmark->getId();
}, $bookmarks));
}
/**
* @return array
*/
public function singleBookmarksProvider() {
return array_map(function($props) {
return Db\Bookmark::fromArray($props);
}, [
'Simple URL with title and description' => ['url' => 'https://google.com/', 'title' => 'Google', 'description' => 'Search engine'],
'Simple URL with title' => ['url' => 'https://nextcloud.com/', 'title' => 'Nextcloud'],
'Simple URL' => ['url' => 'https://php.net/'],
'URL with unicode' => ['url' => 'https://de.wikipedia.org/wiki/Ü'],
]);
}
}