From 05377d023ef4d43818a4b42039c8143cb0f907e4 Mon Sep 17 00:00:00 2001 From: Sean Molenaar Date: Sat, 26 Dec 2020 13:09:41 +0100 Subject: [PATCH] Remove PHPunit integration tests Signed-off-by: Sean Molenaar --- CHANGELOG.md | 5 +- lib/Command/Updater/UpdateFeed.php | 2 +- lib/Controller/EntityApiSerializer.php | 76 - lib/Controller/FeedApiController.php | 26 +- lib/Controller/FeedController.php | 108 +- lib/Controller/FolderController.php | 8 +- lib/Controller/ItemController.php | 10 +- lib/Db/Feed.php | 10 + lib/Db/FeedMapper.php | 202 --- lib/Db/FeedMapperV2.php | 16 +- lib/Db/ItemMapper.php | 121 +- lib/Db/ItemMapperV2.php | 5 + lib/Db/NewsMapper.php | 158 --- lib/Fetcher/IFeedFetcher.php | 4 +- lib/Hooks/UserDeleteHook.php | 8 +- lib/Service/FeedService.php | 521 ------- lib/Service/FeedServiceV2.php | 134 +- lib/Service/FolderServiceV2.php | 59 +- lib/Service/ImportService.php | 125 ++ lib/Service/ItemService.php | 71 +- lib/Service/ItemServiceV2.php | 25 +- lib/Service/OpmlService.php | 7 - lib/Service/Service.php | 48 +- lib/Service/StatusService.php | 5 +- phpunit.xml | 1 + tests/Unit/Command/UpdateFeedTest.php | 6 +- .../Controller/EntityApiSerializerTest.php | 144 -- .../Unit/Controller/FeedApiControllerTest.php | 128 +- tests/Unit/Controller/FeedControllerTest.php | 267 +++- .../Unit/Controller/FolderControllerTest.php | 7 +- tests/Unit/Controller/ItemControllerTest.php | 16 +- tests/Unit/Db/FeedMapperTest.php | 451 ++++++ tests/Unit/Db/NewsMapperTest.php | 270 ++++ tests/Unit/Service/FeedServiceTest.php | 1248 ++++++----------- tests/Unit/Service/FolderServiceTest.php | 39 - tests/Unit/Service/ImportServiceTest.php | 232 +++ tests/Unit/Service/ItemServiceTest.php | 72 +- tests/Unit/Service/OPMLServiceTest.php | 143 ++ tests/Unit/Service/ServiceTest.php | 66 +- tests/Unit/Service/StatusServiceTest.php | 23 +- 40 files changed, 2431 insertions(+), 2436 deletions(-) delete mode 100644 lib/Controller/EntityApiSerializer.php delete mode 100644 lib/Db/FeedMapper.php delete mode 100644 lib/Db/NewsMapper.php delete mode 100644 lib/Service/FeedService.php create mode 100644 lib/Service/ImportService.php delete mode 100644 tests/Unit/Controller/EntityApiSerializerTest.php create mode 100644 tests/Unit/Db/FeedMapperTest.php create mode 100644 tests/Unit/Db/NewsMapperTest.php create mode 100644 tests/Unit/Service/ImportServiceTest.php create mode 100644 tests/Unit/Service/OPMLServiceTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 6874d02f6..a875cec74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ # Changelog All notable changes to this project will be documented in this file. - The format is almost based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), older entries don't fully match. ## [Unreleased] +### Changed +- Remove outdated feed DB code +- add background & hover for entries + ## [15.1.1] - 2020-12-27 ### Changed diff --git a/lib/Command/Updater/UpdateFeed.php b/lib/Command/Updater/UpdateFeed.php index 93fa2a8d3..4ffb8f57a 100644 --- a/lib/Command/Updater/UpdateFeed.php +++ b/lib/Command/Updater/UpdateFeed.php @@ -51,7 +51,7 @@ class UpdateFeed extends Command $feedId = $input->getArgument('feed-id'); $userId = $input->getArgument('user-id'); try { - $feed = $this->feedService->findForUser($userId, $feedId); + $feed = $this->feedService->find($userId, $feedId); $updated_feed = $this->feedService->fetch($feed); } catch (\Exception $e) { $output->writeln( diff --git a/lib/Controller/EntityApiSerializer.php b/lib/Controller/EntityApiSerializer.php deleted file mode 100644 index daa0f20e5..000000000 --- a/lib/Controller/EntityApiSerializer.php +++ /dev/null @@ -1,76 +0,0 @@ - - * @copyright 2012-2014 Bernhard Posselt - */ - -namespace OCA\News\Controller; - -use \OCA\News\Db\IAPI; - -/** - * Class EntityApiSerializer - * - * @package OCA\News\Controller - * @deprecated use ApiPayloadTrait - */ -class EntityApiSerializer -{ - - private $level; - - public function __construct($level) - { - $this->level = $level; - } - - - /** - * Call toAPI() method on all entities. Works on - * - * @param mixed $data : - * * Entity - * * Entity[] - * * array('level' => Entity[]) - * * Response - * @return array|mixed - */ - public function serialize($data) - { - - if ($data instanceof IAPI) { - return [$this->level => [$data->toAPI()]]; - } - - if (is_array($data) && array_key_exists($this->level, $data)) { - $data[$this->level] = $this->convert($data[$this->level]); - } elseif (is_array($data)) { - $data = [$this->level => $this->convert($data)]; - } - - return $data; - } - - - private function convert(array $entities) - { - $converted = []; - - foreach ($entities as $entity) { - if ($entity instanceof IAPI) { - $converted[] = $entity->toAPI(); - - // break if it contains anything else than entities - } else { - return $entities; - } - } - - return $converted; - } -} diff --git a/lib/Controller/FeedApiController.php b/lib/Controller/FeedApiController.php index eb6edcad2..af552f411 100644 --- a/lib/Controller/FeedApiController.php +++ b/lib/Controller/FeedApiController.php @@ -25,10 +25,8 @@ use \OCP\IRequest; use \OCP\IUserSession; use \OCP\AppFramework\Http; -use \OCA\News\Service\FeedService; use \OCA\News\Service\ItemService; use Psr\Log\LoggerInterface; -use function GuzzleHttp\Psr7\uri_for; class FeedApiController extends ApiController { @@ -45,12 +43,6 @@ class FeedApiController extends ApiController */ private $feedService; - /** - * TODO: Remove - * @var FeedService - */ - private $oldFeedService; - /** * @var LoggerInterface */ @@ -64,14 +56,12 @@ class FeedApiController extends ApiController public function __construct( IRequest $request, ?IUserSession $userSession, - FeedService $oldFeedService, FeedServiceV2 $feedService, ItemService $oldItemService, LoggerInterface $logger ) { parent::__construct($request, $userSession); $this->feedService = $feedService; - $this->oldFeedService = $oldFeedService; $this->oldItemService = $oldItemService; $this->logger = $logger; } @@ -189,11 +179,9 @@ class FeedApiController extends ApiController } try { - $this->oldFeedService->patch( - $feedId, - $this->getUserId(), - ['folderId' => $folderId] - ); + $feed = $this->feedService->find($this->getUserId(), $feedId); + $feed->setFolderId($folderId); + $this->feedService->update($this->getUserId(), $feed); } catch (ServiceNotFoundException $ex) { return $this->error($ex, Http::STATUS_NOT_FOUND); } @@ -215,11 +203,9 @@ class FeedApiController extends ApiController public function rename(int $feedId, string $feedTitle) { try { - $this->oldFeedService->patch( - $feedId, - $this->getUserId(), - ['title' => $feedTitle] - ); + $feed = $this->feedService->find($this->getUserId(), $feedId); + $feed->setTitle($feedTitle); + $this->feedService->update($this->getUserId(), $feed); } catch (ServiceNotFoundException $ex) { return $this->error($ex, Http::STATUS_NOT_FOUND); } diff --git a/lib/Controller/FeedController.php b/lib/Controller/FeedController.php index 5abcd3393..ba39fdf7a 100644 --- a/lib/Controller/FeedController.php +++ b/lib/Controller/FeedController.php @@ -15,14 +15,15 @@ namespace OCA\News\Controller; use OCA\News\Service\Exceptions\ServiceConflictException; use OCA\News\Service\Exceptions\ServiceNotFoundException; +use OCA\News\Service\FeedServiceV2; use OCA\News\Service\FolderServiceV2; +use OCA\News\Service\ImportService; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; use OCP\IConfig; use OCP\AppFramework\Http; use OCA\News\Service\ItemService; -use OCA\News\Service\FeedService; use OCA\News\Db\FeedType; use OCP\IUserSession; @@ -30,7 +31,9 @@ class FeedController extends Controller { use JSONHttpErrorTrait; - //TODO: Remove + /** + * @var FeedServiceV2 + */ private $feedService; //TODO: Remove private $itemService; @@ -38,6 +41,10 @@ class FeedController extends Controller * @var FolderServiceV2 */ private $folderService; + /** + * @var ImportService + */ + private $importService; /** * @var IConfig */ @@ -46,8 +53,9 @@ class FeedController extends Controller public function __construct( IRequest $request, FolderServiceV2 $folderService, - FeedService $feedService, + FeedServiceV2 $feedService, ItemService $itemService, + ImportService $importService, IConfig $settings, ?IUserSession $userSession ) { @@ -55,6 +63,7 @@ class FeedController extends Controller $this->folderService = $folderService; $this->feedService = $feedService; $this->itemService = $itemService; + $this->importService = $importService; $this->settings = $settings; } @@ -74,12 +83,13 @@ class FeedController extends Controller ]; try { - $params['newestItemId'] = - $this->itemService->getNewestItemId($this->getUserId()); + $id = $this->itemService->getNewestItemId($this->getUserId()); // An exception occurs if there is a newest item. If there is none, // simply ignore it and do not add the newestItemId + $params['newestItemId'] = $id; } catch (ServiceNotFoundException $ex) { + //NO-OP } return $params; @@ -102,24 +112,22 @@ class FeedController extends Controller 'lastViewedFeedType' ); - // cast from null to int is 0 - if ($feedType !== null) { - $feedType = (int) $feedType; - } - // check if feed or folder exists try { - if ($feedType === FeedType::FOLDER) { - if ($feedId === 0) { - $feedId = null; - } - $this->folderService->find($this->getUserId(), $feedId); - } elseif ($feedType === FeedType::FEED) { - $this->feedService->find($this->getUserId(), $feedId); + if ($feedType === null) { + throw new ServiceNotFoundException('First launch'); + } - // if its the first launch, those values will be null - } elseif ($feedType === null) { - throw new ServiceNotFoundException(''); + $feedType = intval($feedType); + switch ($feedType) { + case FeedType::FOLDER: + $this->folderService->find($this->getUserId(), $feedId); + break; + case FeedType::FEED: + $this->feedService->find($this->getUserId(), $feedId); + break; + default: + break; } } catch (ServiceNotFoundException $ex) { $feedId = 0; @@ -162,9 +170,10 @@ class FeedController extends Controller $this->feedService->purgeDeleted($this->getUserId(), false); $feed = $this->feedService->create( + $this->getUserId(), $url, $parentFolderId, - $this->getUserId(), + false, $title, $user, $password @@ -172,12 +181,12 @@ class FeedController extends Controller $params = ['feeds' => [$feed]]; try { - $params['newestItemId'] = - $this->itemService->getNewestItemId($this->getUserId()); - + $id = $this->itemService->getNewestItemId($this->getUserId()); // An exception occurs if there is a newest item. If there is none, // simply ignore it and do not add the newestItemId + $params['newestItemId'] = $id; } catch (ServiceNotFoundException $ex) { + //NO-OP } return $params; @@ -199,7 +208,9 @@ class FeedController extends Controller public function delete(int $feedId) { try { - $this->feedService->markDeleted($feedId, $this->getUserId()); + $feed = $this->feedService->find($this->getUserId(), $feedId); + $feed->setDeletedAt(time()); + $this->feedService->update($this->getUserId(), $feed); } catch (ServiceNotFoundException $ex) { return $this->error($ex, Http::STATUS_NOT_FOUND); } @@ -218,7 +229,8 @@ class FeedController extends Controller public function update(int $feedId) { try { - $feed = $this->feedService->update($this->getUserId(), $feedId); + $old_feed = $this->feedService->find($this->getUserId(), $feedId); + $feed = $this->feedService->fetch($old_feed); return [ 'feeds' => [ @@ -244,7 +256,7 @@ class FeedController extends Controller */ public function import(array $json): array { - $feed = $this->feedService->importArticles($json, $this->getUserId()); + $feed = $this->importService->importArticles($this->getUserId(), $json); $params = [ 'starred' => $this->itemService->starredCount($this->getUserId()) @@ -290,7 +302,9 @@ class FeedController extends Controller public function restore(int $feedId) { try { - $this->feedService->unmarkDeleted($feedId, $this->getUserId()); + $feed = $this->feedService->find($this->getUserId(), $feedId); + $feed->setDeletedAt(null); + $this->feedService->update($this->getUserId(), $feed); } catch (ServiceNotFoundException $ex) { return $this->error($ex, Http::STATUS_NOT_FOUND); } @@ -320,24 +334,32 @@ class FeedController extends Controller ?int $folderId = null, ?string $title = null ) { - $attributes = [ - 'pinned' => $pinned, - 'fullTextEnabled' => $fullTextEnabled, - 'updateMode' => $updateMode, - 'ordering' => $ordering, - 'title' => $title, - 'folderId' => $folderId === 0 ? null : $folderId - ]; + try { + $feed = $this->feedService->find($this->getUserId(), $feedId); + } catch (ServiceNotFoundException $ex) { + return $this->error($ex, Http::STATUS_NOT_FOUND); + } - $diff = array_filter( - $attributes, - function ($value) { - return $value !== null; - } - ); + $fId = $folderId === 0 ? null : $folderId; + $feed->setFolderId($fId); + if ($pinned !== null) { + $feed->setPinned($pinned); + } + if ($fullTextEnabled !== null) { + $feed->setFullTextEnabled($fullTextEnabled); + } + if ($updateMode !== null) { + $feed->setUpdateMode($updateMode); + } + if ($ordering !== null) { + $feed->setOrdering($ordering); + } + if ($title !== null) { + $feed->setTitle($title); + } try { - $this->feedService->patch($feedId, $this->getUserId(), $diff); + $this->feedService->update($this->getUserId(), $feed); } catch (ServiceNotFoundException $ex) { return $this->error($ex, Http::STATUS_NOT_FOUND); } diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index b33f46c54..acf3f0109 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -14,12 +14,12 @@ namespace OCA\News\Controller; use OCA\News\Service\Exceptions\ServiceException; +use OCA\News\Service\FeedServiceV2; use OCP\AppFramework\Http\JSONResponse; use \OCP\IRequest; use \OCP\AppFramework\Http; use \OCA\News\Service\FolderServiceV2; -use \OCA\News\Service\FeedService; use \OCA\News\Service\ItemService; use \OCA\News\Service\Exceptions\ServiceNotFoundException; use \OCA\News\Service\Exceptions\ServiceConflictException; @@ -33,7 +33,9 @@ class FolderController extends Controller * @var FolderServiceV2 */ private $folderService; - //TODO: Remove + /** + * @var FeedServiceV2 + */ private $feedService; //TODO: Remove private $itemService; @@ -41,7 +43,7 @@ class FolderController extends Controller public function __construct( IRequest $request, FolderServiceV2 $folderService, - FeedService $feedService, + FeedServiceV2 $feedService, ItemService $itemService, ?IUserSession $userSession ) { diff --git a/lib/Controller/ItemController.php b/lib/Controller/ItemController.php index 7506891d4..8783a5286 100644 --- a/lib/Controller/ItemController.php +++ b/lib/Controller/ItemController.php @@ -13,6 +13,7 @@ namespace OCA\News\Controller; +use OCA\News\Service\FeedServiceV2; use \OCP\IRequest; use \OCP\IConfig; use \OCP\AppFramework\Http; @@ -20,7 +21,6 @@ use \OCP\AppFramework\Http; use \OCA\News\Service\Exceptions\ServiceException; use \OCA\News\Service\Exceptions\ServiceNotFoundException; use \OCA\News\Service\ItemService; -use \OCA\News\Service\FeedService; use OCP\IUserSession; class ItemController extends Controller @@ -28,12 +28,18 @@ class ItemController extends Controller use JSONHttpErrorTrait; private $itemService; + /** + * @var FeedServiceV2 + */ private $feedService; + /** + * @var IConfig + */ private $settings; public function __construct( IRequest $request, - FeedService $feedService, + FeedServiceV2 $feedService, ItemService $itemService, IConfig $settings, ?IUserSession $userSession diff --git a/lib/Db/Feed.php b/lib/Db/Feed.php index 473912acc..1d5721d3b 100644 --- a/lib/Db/Feed.php +++ b/lib/Db/Feed.php @@ -25,6 +25,16 @@ class Feed extends Entity implements IAPI, \JsonSerializable { use EntityJSONSerializer; + /** + * Silently import new items + */ + const UPDATE_MODE_SILENT = 0; + + /** + * Mark new items as unread. + */ + const UPDATE_MODE_NORMAL = 1; + /** @var string */ protected $userId = ''; /** @var string */ diff --git a/lib/Db/FeedMapper.php b/lib/Db/FeedMapper.php deleted file mode 100644 index cf12c4dfa..000000000 --- a/lib/Db/FeedMapper.php +++ /dev/null @@ -1,202 +0,0 @@ - - * @author Bernhard Posselt - * @copyright 2012 Alessandro Cosentino - * @copyright 2012-2014 Bernhard Posselt - */ - -namespace OCA\News\Db; - -use OCA\News\Utility\Time; -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\IDBConnection; -use OCP\AppFramework\Db\Entity; - -/** - * Class LegacyFeedMapper - * - * @package OCA\News\Db - * @deprecated use FeedMapper - */ -class FeedMapper extends NewsMapper -{ - const TABLE_NAME = 'news_feeds'; - - public function __construct(IDBConnection $db, Time $time) - { - parent::__construct($db, $time, Feed::class); - } - - - public function find(string $userId, int $id) - { - $sql = 'SELECT `feeds`.*, `item_numbers`.`unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'JOIN ( ' . - 'SELECT `feeds`.`id`, COUNT(`items`.`id`) AS `unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'LEFT JOIN `*PREFIX*news_items` `items` ' . - 'ON `feeds`.`id` = `items`.`feed_id` ' . - // WARNING: this is a desperate attempt at making this query - // work because prepared statements dont work. This is a - // POSSIBLE SQL INJECTION RISK WHEN MODIFIED WITHOUT THOUGHT. - // think twice when changing this - 'AND `items`.`unread` = ? ' . - 'WHERE `feeds`.`id` = ? ' . - 'AND `feeds`.`user_id` = ? ' . - 'GROUP BY `feeds`.`id` ' . - ') `item_numbers` ' . - 'ON `item_numbers`.`id` = `feeds`.`id` '; - $params = [true, $id, $userId]; - - return $this->findEntity($sql, $params); - } - - - public function findAllFromUser(string $userId): array - { - $sql = 'SELECT `feeds`.*, `item_numbers`.`unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'JOIN ( ' . - 'SELECT `feeds`.`id`, COUNT(`items`.`id`) AS `unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` ' . - 'ON `feeds`.`folder_id` = `folders`.`id` ' . - 'LEFT JOIN `*PREFIX*news_items` `items` ' . - 'ON `feeds`.`id` = `items`.`feed_id` ' . - // WARNING: this is a desperate attempt at making this query - // work because prepared statements dont work. This is a - // POSSIBLE SQL INJECTION RISK WHEN MODIFIED WITHOUT THOUGHT. - // think twice when changing this - 'AND `items`.`unread` = ? ' . - 'WHERE `feeds`.`user_id` = ? ' . - 'AND (`feeds`.`folder_id` IS NULL ' . - 'OR `folders`.`deleted_at` = 0 ' . - ') ' . - 'AND `feeds`.`deleted_at` = 0 ' . - 'GROUP BY `feeds`.`id` ' . - ') `item_numbers` ' . - 'ON `item_numbers`.`id` = `feeds`.`id` '; - $params = [true, $userId]; - - return $this->findEntities($sql, $params); - } - - - public function findAll(): array - { - $sql = 'SELECT `feeds`.*, `item_numbers`.`unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'JOIN ( ' . - 'SELECT `feeds`.`id`, COUNT(`items`.`id`) AS `unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` ' . - 'ON `feeds`.`folder_id` = `folders`.`id` ' . - 'LEFT JOIN `*PREFIX*news_items` `items` ' . - 'ON `feeds`.`id` = `items`.`feed_id` ' . - // WARNING: this is a desperate attempt at making this query - // work because prepared statements dont work. This is a - // POSSIBLE SQL INJECTION RISK WHEN MODIFIED WITHOUT THOUGHT. - // think twice when changing this - 'AND `items`.`unread` = ? ' . - 'WHERE (`feeds`.`folder_id` IS NULL ' . - 'OR `folders`.`deleted_at` = 0 ' . - ') ' . - 'AND `feeds`.`deleted_at` = 0 ' . - 'GROUP BY `feeds`.`id` ' . - ') `item_numbers` ' . - 'ON `item_numbers`.`id` = `feeds`.`id` '; - - return $this->findEntities($sql, [true]); - } - - - public function findByUrlHash($hash, $userId) - { - $sql = 'SELECT `feeds`.*, `item_numbers`.`unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'JOIN ( ' . - 'SELECT `feeds`.`id`, COUNT(`items`.`id`) AS `unread_count` ' . - 'FROM `*PREFIX*news_feeds` `feeds` ' . - 'LEFT JOIN `*PREFIX*news_items` `items` ' . - 'ON `feeds`.`id` = `items`.`feed_id` ' . - // WARNING: this is a desperate attempt at making this query - // work because prepared statements dont work. This is a - // POSSIBLE SQL INJECTION RISK WHEN MODIFIED WITHOUT THOUGHT. - // think twice when changing this - 'AND `items`.`unread` = ? ' . - 'WHERE `feeds`.`url_hash` = ? ' . - 'AND `feeds`.`user_id` = ? ' . - 'GROUP BY `feeds`.`id` ' . - ') `item_numbers` ' . - 'ON `item_numbers`.`id` = `feeds`.`id` '; - $params = [true, $hash, $userId]; - - return $this->findEntity($sql, $params); - } - - - public function delete(Entity $entity): Entity - { - // someone please slap me for doing this manually :P - // we needz CASCADE + FKs please - $sql = 'DELETE FROM `*PREFIX*news_items` WHERE `feed_id` = ?'; - $params = [$entity->getId()]; - $this->execute($sql, $params); - - return parent::delete($entity); - } - - - /** - * @param int $deleteOlderThan if given gets all entries with a delete date - * older than that timestamp - * @param string $userId if given returns only entries from the given user - * @return array with the database rows - */ - public function getToDelete($deleteOlderThan = null, $userId = null) - { - $sql = 'SELECT * FROM `*PREFIX*news_feeds` ' . - 'WHERE `deleted_at` > 0 '; - $params = []; - - // sometimes we want to delete all entries - if ($deleteOlderThan !== null) { - $sql .= 'AND `deleted_at` < ? '; - $params[] = $deleteOlderThan; - } - - // we need to sometimes only delete feeds of a user - if ($userId !== null) { - $sql .= 'AND `user_id` = ?'; - $params[] = $userId; - } - - return $this->findEntities($sql, $params); - } - - - /** - * Deletes all feeds of a user, delete items first since the user_id - * is not defined in there - * - * @param string $userId the name of the user - */ - public function deleteUser($userId) - { - $sql = 'DELETE FROM `*PREFIX*news_feeds` WHERE `user_id` = ?'; - $this->execute($sql, [$userId]); - } - - public function findFromUser(string $userId, int $id): Entity - { - return $this->find($userId, $id); - } -} diff --git a/lib/Db/FeedMapperV2.php b/lib/Db/FeedMapperV2.php index 0ecb5ba4f..85a1bd3f3 100644 --- a/lib/Db/FeedMapperV2.php +++ b/lib/Db/FeedMapperV2.php @@ -52,7 +52,7 @@ class FeedMapperV2 extends NewsMapperV2 { $builder = $this->db->getQueryBuilder(); $builder->select('feeds.*', $builder->func()->count('items.id', 'unreadCount')) - ->from($this->tableName, 'feeds') + ->from(static::TABLE_NAME, 'feeds') ->leftJoin( 'feeds', ItemMapperV2::TABLE_NAME, @@ -82,8 +82,8 @@ class FeedMapperV2 extends NewsMapperV2 public function findFromUser(string $userId, int $id): Entity { $builder = $this->db->getQueryBuilder(); - $builder->addSelect('*') - ->from($this->tableName) + $builder->select('*') + ->from(static::TABLE_NAME) ->where('user_id = :user_id') ->andWhere('id = :id') ->setParameter(':user_id', $userId) @@ -101,7 +101,7 @@ class FeedMapperV2 extends NewsMapperV2 { $builder = $this->db->getQueryBuilder(); $builder->select('*') - ->from($this->tableName) + ->from(static::TABLE_NAME) ->where('deleted_at = 0'); return $this->findEntities($builder); @@ -121,8 +121,8 @@ class FeedMapperV2 extends NewsMapperV2 public function findByURL(string $userId, string $url): Entity { $builder = $this->db->getQueryBuilder(); - $builder->addSelect('*') - ->from($this->tableName) + $builder->select('*') + ->from(static::TABLE_NAME) ->where('user_id = :user_id') ->andWhere('url = :url') ->setParameter(':user_id', $userId) @@ -141,8 +141,8 @@ class FeedMapperV2 extends NewsMapperV2 public function findAllFromFolder(?int $id): array { $builder = $this->db->getQueryBuilder(); - $builder->addSelect('*') - ->from($this->tableName); + $builder->select('*') + ->from(static::TABLE_NAME); if (is_null($id)) { $builder->where('folder_id IS NULL'); diff --git a/lib/Db/ItemMapper.php b/lib/Db/ItemMapper.php index 65c6e0b15..71e31dbc4 100644 --- a/lib/Db/ItemMapper.php +++ b/lib/Db/ItemMapper.php @@ -17,6 +17,7 @@ use Exception; use OCA\News\Utility\Time; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\Mapper; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -27,13 +28,25 @@ use OCP\IDBConnection; * @package OCA\News\Db * @deprecated use ItemMapper */ -class ItemMapper extends NewsMapper +class ItemMapper extends Mapper { const TABLE_NAME = 'news_items'; + /** + * @var Time + */ + private $time; + + /** + * NewsMapper constructor. + * + * @param IDBConnection $db Database connection + * @param Time $time Time class + */ public function __construct(IDBConnection $db, Time $time) { - parent::__construct($db, $time, Item::class); + parent::__construct($db, static::TABLE_NAME, Item::class); + $this->time = $time; } private function makeSelectQuery( @@ -107,7 +120,7 @@ class ItemMapper extends NewsMapper /** * @param int $id * @param string $userId - * @return \OCA\News\Db\Item + * @return \OCA\News\Db\Item|Entity */ public function find(string $userId, int $id) { @@ -332,7 +345,15 @@ class ItemMapper extends NewsMapper return $this->findEntities($sql, $params); } - + /** + * @param $guidHash + * @param $feedId + * @param $userId + * + * @return Entity|Item + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ public function findByGuidHash($guidHash, $feedId, $userId) { $sql = $this->makeSelectQuery( @@ -411,23 +432,6 @@ class ItemMapper extends NewsMapper } - /** - * Deletes all items of a user - * - * @param string $userId the name of the user - */ - public function deleteUser($userId) - { - $sql = 'DELETE FROM `*PREFIX*news_items` ' . - 'WHERE `feed_id` IN (' . - 'SELECT `feeds`.`id` FROM `*PREFIX*news_feeds` `feeds` ' . - 'WHERE `feeds`.`user_id` = ?' . - ')'; - - $this->execute($sql, [$userId]); - } - - /** * Returns a list of ids and userid of all items */ @@ -492,29 +496,74 @@ class ItemMapper extends NewsMapper } } - /** - * NO-OP - * - * @param string $userId - * - * @return array - */ - public function findAllFromUser(string $userId): array + public function update(Entity $entity): Entity { - return []; + $entity->setLastModified($this->time->getMicroTime()); + return parent::update($entity); } - public function findFromUser(string $userId, int $id): Entity + public function insert(Entity $entity): Entity { - return $this->find($id, $userId); + $entity->setLastModified($this->time->getMicroTime()); + return parent::insert($entity); } /** - * NO-OP - * @return array + * Remove deleted items. + * + * @return void */ - public function findAll(): array + public function purgeDeleted(): void { - return []; + $builder = $this->db->getQueryBuilder(); + $builder->delete($this->tableName) + ->where('deleted_at != 0') + ->execute(); + } + /** + * Performs a SELECT query with all arguments appened to the WHERE clause + * The SELECT will be performed on the current table and take the entity + * that is related for transforming the properties into column names + * + * Important: This method does not filter marked as deleted rows! + * + * @param array $search an assoc array from property to filter value + * @param int|null $limit Output limit + * @param int|null $offset Output offset + * + * @depreacted Legacy function + * + * @return Entity[] + */ + public function where(array $search = [], ?int $limit = null, ?int $offset = null) + { + $entity = new $this->entityClass(); + + // turn keys into sql query filter, e.g. feedId -> feed_id = :feedId + $filter = array_map( + function ($property) use ($entity) { + // check if the property actually exists on the entity to prevent + // accidental Sql injection + if (!property_exists($entity, $property)) { + $msg = 'Property ' . $property . ' does not exist on ' + . $this->entityClass; + throw new \BadFunctionCallException($msg); + } + + $column = $entity->propertyToColumn($property); + return $column . ' = :' . $property; + }, + array_keys($search) + ); + + $andStatement = implode(' AND ', $filter); + + $sql = 'SELECT * FROM `' . $this->getTableName() . '`'; + + if (count($search) > 0) { + $sql .= 'WHERE ' . $andStatement; + } + + return $this->findEntities($sql, $search, $limit, $offset); } } diff --git a/lib/Db/ItemMapperV2.php b/lib/Db/ItemMapperV2.php index 71a6ba043..10ebe056b 100644 --- a/lib/Db/ItemMapperV2.php +++ b/lib/Db/ItemMapperV2.php @@ -120,6 +120,11 @@ class ItemMapperV2 extends NewsMapperV2 return $this->findEntity($builder); } + /** + * @param int $feedId + * + * @return array + */ public function findAllForFeed(int $feedId): array { $builder = $this->db->getQueryBuilder(); diff --git a/lib/Db/NewsMapper.php b/lib/Db/NewsMapper.php deleted file mode 100644 index b03c42c91..000000000 --- a/lib/Db/NewsMapper.php +++ /dev/null @@ -1,158 +0,0 @@ - - * @author Bernhard Posselt - * @copyright 2012 Alessandro Cosentino - * @copyright 2012-2014 Bernhard Posselt - */ - -namespace OCA\News\Db; - -use OCA\News\Utility\Time; -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\AppFramework\Db\QBMapper; -use OCP\IDBConnection; -use OCP\AppFramework\Db\Mapper; -use OCP\AppFramework\Db\Entity; - -/** - * Class NewsMapper - * - * @package OCA\News\Db - */ -abstract class NewsMapper extends Mapper -{ - const TABLE_NAME = ''; - - /** - * @var Time - */ - private $time; - - /** - * NewsMapper constructor. - * - * @param IDBConnection $db Database connection - * @param Time $time Time class - * @param string $entity Entity class - */ - public function __construct( - IDBConnection $db, - Time $time, - string $entity - ) { - parent::__construct($db, static::TABLE_NAME, $entity); - $this->time = $time; - } - - public function update(Entity $entity): Entity - { - $entity->setLastModified($this->time->getMicroTime()); - return parent::update($entity); - } - - public function insert(Entity $entity): Entity - { - $entity->setLastModified($this->time->getMicroTime()); - return parent::insert($entity); - } - - /** - * Remove deleted items. - * - * @return void - */ - public function purgeDeleted(): void - { - $builder = $this->db->getQueryBuilder(); - $builder->delete($this->tableName) - ->where('deleted_at != 0') - ->execute(); - } - - abstract public function find(string $userId, int $id); - - /** - * Find all items. - * - * @return Entity[] - */ - abstract public function findAll(): array; - - /** - * Find all items for a user. - * - * @param string $userId ID of the user - * - * @return Entity[] - */ - abstract public function findAllFromUser(string $userId): array; - - /** - * Find item for a user. - * - * @param string $userId ID of the user - * @param int $id ID of the item - * - * @return Feed - * - * @throws DoesNotExistException The item is not found - * @throws MultipleObjectsReturnedException Multiple items found - */ - abstract public function findFromUser(string $userId, int $id): Entity; - - - - /** - * Performs a SELECT query with all arguments appened to the WHERE clause - * The SELECT will be performed on the current table and take the entity - * that is related for transforming the properties into column names - * - * Important: This method does not filter marked as deleted rows! - * - * @param array $search an assoc array from property to filter value - * @param int|null $limit Output limit - * @param int|null $offset Output offset - * - * @depreacted Legacy function - * - * @return array - */ - public function where(array $search = [], ?int $limit = null, ?int $offset = null) - { - $entity = new $this->entityClass(); - - // turn keys into sql query filter, e.g. feedId -> feed_id = :feedId - $filter = array_map( - function ($property) use ($entity) { - // check if the property actually exists on the entity to prevent - // accidental Sql injection - if (!property_exists($entity, $property)) { - $msg = 'Property ' . $property . ' does not exist on ' - . $this->entityClass; - throw new \BadFunctionCallException($msg); - } - - $column = $entity->propertyToColumn($property); - return $column . ' = :' . $property; - }, - array_keys($search) - ); - - $andStatement = implode(' AND ', $filter); - - $sql = 'SELECT * FROM `' . $this->getTableName() . '`'; - - if (count($search) > 0) { - $sql .= 'WHERE ' . $andStatement; - } - - return $this->findEntities($sql, $search, $limit, $offset); - } -} diff --git a/lib/Fetcher/IFeedFetcher.php b/lib/Fetcher/IFeedFetcher.php index aaba9de67..bfa737805 100644 --- a/lib/Fetcher/IFeedFetcher.php +++ b/lib/Fetcher/IFeedFetcher.php @@ -14,6 +14,8 @@ namespace OCA\News\Fetcher; use FeedIo\Reader\ReadErrorException; +use OCA\News\Db\Feed; +use OCA\News\Db\Item; interface IFeedFetcher { @@ -29,7 +31,7 @@ interface IFeedFetcher * @param string|null $user if given, basic auth is set for this feed * @param string|null $password if given, basic auth is set for this feed. Ignored if user is empty * - * @return array an array containing the new feed and its items, first + * @return an array containing the new feed and its items, first * element being the Feed and second element being an array of Items * * @throws ReadErrorException if the Feed-IO fetcher encounters a problem diff --git a/lib/Hooks/UserDeleteHook.php b/lib/Hooks/UserDeleteHook.php index 128e6fa79..d406e5741 100644 --- a/lib/Hooks/UserDeleteHook.php +++ b/lib/Hooks/UserDeleteHook.php @@ -14,9 +14,9 @@ namespace OCA\News\Hooks; use OCA\News\AppInfo\Application; -use OCA\News\Service\ItemService; -use OCA\News\Service\FeedService; +use OCA\News\Service\FeedServiceV2; use OCA\News\Service\FolderServiceV2; +use OCA\News\Service\ItemServiceV2; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\User\Events\BeforeUserDeletedEvent; @@ -37,8 +37,8 @@ class UserDeleteHook implements IEventListener $container = $app->getContainer(); // order is important! - $container->get(ItemService::class)->deleteUser($userId); - $container->get(FeedService::class)->deleteUser($userId); + $container->get(ItemServiceV2::class)->deleteUser($userId); + $container->get(FeedServiceV2::class)->deleteUser($userId); $container->get(FolderServiceV2::class)->deleteUser($userId); } } diff --git a/lib/Service/FeedService.php b/lib/Service/FeedService.php deleted file mode 100644 index c671a035c..000000000 --- a/lib/Service/FeedService.php +++ /dev/null @@ -1,521 +0,0 @@ - - * @author Bernhard Posselt - * @copyright 2012 Alessandro Cosentino - * @copyright 2012-2014 Bernhard Posselt - */ - -namespace OCA\News\Service; - -use FeedIo\Reader\ReadErrorException; -use HTMLPurifier; - -use OCA\News\AppInfo\Application; -use OCP\IConfig; -use OCA\News\Service\Exceptions\ServiceConflictException; -use OCA\News\Service\Exceptions\ServiceNotFoundException; -use OCP\AppFramework\Db\Entity; -use OCP\IL10N; -use OCP\AppFramework\Db\DoesNotExistException; - -use OCA\News\Db\Feed; -use OCA\News\Db\Item; -use OCA\News\Db\FeedMapper; -use OCA\News\Db\ItemMapper; -use OCA\News\Fetcher\Fetcher; -use OCA\News\Utility\Time; -use Psr\Log\LoggerInterface; - -/** - * Class LegacyFeedService - * - * @package OCA\News\Service - * @deprecated use FeedServiceV2 - */ -class FeedService extends Service -{ - - private $feedFetcher; - private $itemMapper; - private $feedMapper; - private $l10n; - private $timeFactory; - private $autoPurgeMinimumInterval; - private $purifier; - private $loggerParams; - - public function __construct( - FeedMapper $legacyFeedMapper, - Fetcher $feedFetcher, - ItemMapper $legacyItemMapper, - LoggerInterface $logger, - IL10N $l10n, - Time $timeFactory, - IConfig $config, - HTMLPurifier $purifier - ) { - parent::__construct($legacyFeedMapper, $logger); - $this->feedFetcher = $feedFetcher; - $this->feedMapper = $legacyFeedMapper; - $this->itemMapper = $legacyItemMapper; - $this->logger = $logger; - $this->l10n = $l10n; - $this->timeFactory = $timeFactory; - $this->autoPurgeMinimumInterval = $config->getAppValue( - Application::NAME, - 'autoPurgeMinimumInterval', - Application::DEFAULT_SETTINGS['autoPurgeMinimumInterval'] - ); - $this->purifier = $purifier; - $this->loggerParams = ['app' => Application::NAME]; - } - - /** - * Finds all feeds of a user - * - * @param string $userId the name of the user - * - * @return Feed[] - */ - public function findAllForUser($userId, array $params = []): array - { - return $this->feedMapper->findAllFromUser($userId); - } - - - /** - * Finds all feeds from all users - * - * @return array of feeds - */ - public function findAllFromAllUsers() - { - return $this->findAll(); - } - - - /** - * Creates a new feed - * - * @param string $feedUrl the url to the feed - * @param int|null $folderId the folder where it should be put into, null for root - * folder - * @param string $userId for which user the feed should be created - * @param string|null $title if given, this is used for the opml feed title - * @param string|null $user if given, basic auth is set for this feed - * @param string|null $password if given, basic auth is set for this - * feed. Ignored if user is null or an empty string - * - * @return Feed the newly created feed - * @throws ServiceConflictException if the feed exists already - * @throws ServiceNotFoundException if the url points to an invalid feed - */ - public function create( - string $feedUrl, - ?int $folderId, - string $userId, - string $title = null, - string $user = null, - string $password = null - ) { - // first try if the feed exists already - try { - /** - * @var Feed $feed - * @var Item[] $items - */ - list($feed, $items) = $this->feedFetcher->fetch($feedUrl, true, null, false, $user, $password); - // try again if feed exists depending on the reported link - if ($feed === null) { - throw new ServiceNotFoundException($this->l10n->t('Can not add feed: Unable to parse feed')); - } - try { - $hash = $feed->getUrlHash(); - $this->feedMapper->findByUrlHash($hash, $userId); - throw new ServiceConflictException( - $this->l10n->t('Can not add feed: Exists already') - ); - } catch (DoesNotExistException $ex) { - // If no matching feed was found everything was ok - } - - // insert feed - $itemCount = count($items); - $feed->setBasicAuthUser($user); - $feed->setBasicAuthPassword($password); - $feed->setFolderId($folderId); - $feed->setUserId($userId); - $feed->setArticlesPerUpdate($itemCount); - - if (!empty($title)) { - $feed->setTitle($title); - } - - $feed = $this->feedMapper->insert($feed); - - // insert items in reverse order because the first one is usually - // the newest item - $unreadCount = 0; - foreach (array_reverse($items) as $item) { - $item->setFeedId($feed->getId()); - - // check if item exists (guidhash is the same) - // and ignore it if it does - try { - $this->itemMapper->findByGuidHash( - $item->getGuidHash(), - $item->getFeedId(), - $userId - ); - continue; - } catch (DoesNotExistException $ex) { - $unreadCount += 1; - $item->setBody($this->purifier->purify($item->getBody())); - $this->itemMapper->insert($item); - } - } - - // set unread count - $feed->setUnreadCount($unreadCount); - - return $feed; - } catch (ReadErrorException $ex) { - $this->logger->debug($ex->getMessage(), $this->loggerParams); - throw new ServiceNotFoundException($ex->getMessage()); - } - } - - - /** - * Runs all the feed updates - */ - public function updateAll() - { - // TODO: this method is not covered by any tests - $feeds = $this->feedMapper->findAll(); - foreach ($feeds as $feed) { - try { - $this->update($feed->getId(), $feed->getUserId()); - } catch (\Exception $ex) { - // something is really wrong here, log it - $this->logger->error( - 'Unexpected error when updating feed ' . $ex->getMessage(), - $this->loggerParams - ); - } - } - } - - - /** - * Updates a single feed - * - * @param string $userId the id of the user - * @param int $feedId the id of the feed that should be updated - * @param bool $forceUpdate update even if the article exists already - * - * @throws ServiceNotFoundException if the feed does not exist - * @return Feed the updated feed entity - */ - public function update(string $userId, int $feedId, $forceUpdate = false) - { - /** @var Feed $existingFeed */ - $existingFeed = $this->find($userId, $feedId); - - if ($existingFeed->getPreventUpdate() === true) { - return $existingFeed; - } - - // for backwards compability it can be that the location is not set - // yet, if so use the url - $location = $existingFeed->getLocation(); - if (!$location) { - $location = $existingFeed->getUrl(); - } - - try { - list($fetchedFeed, $items) = $this->feedFetcher->fetch( - $location, - false, - $existingFeed->getHttpLastModified(), - $existingFeed->getFullTextEnabled(), - $existingFeed->getBasicAuthUser(), - $existingFeed->getBasicAuthPassword() - ); - - // if there is no feed it means that no update took place - if (!$fetchedFeed) { - return $existingFeed; - } - - // update number of articles on every feed update - $itemCount = count($items); - - // this is needed to adjust to updates that add more items - // than when the feed was created. You can't update the count - // if it's lower because it may be due to the caching headers - // that were sent as the request and it might cause unwanted - // deletion and reappearing of feeds - if ($itemCount > $existingFeed->getArticlesPerUpdate()) { - $existingFeed->setArticlesPerUpdate($itemCount); - } - - $existingFeed->setHttpLastModified( - $fetchedFeed->getHttpLastModified() - ); - $existingFeed->setHttpEtag($fetchedFeed->getHttpEtag()); - $existingFeed->setLocation($fetchedFeed->getLocation()); - - // insert items in reverse order because the first one is - // usually the newest item - for ($i = $itemCount - 1; $i >= 0; $i--) { - $item = $items[$i]; - $item->setFeedId($existingFeed->getId()); - - try { - $dbItem = $this->itemMapper->findByGuidHash( - $item->getGuidHash(), - $feedId, - $userId - ); - - // in case of update - if ($forceUpdate - || $item->getUpdatedDate() > $dbItem->getUpdatedDate() - ) { - $dbItem->setTitle($item->getTitle()); - $dbItem->setUrl($item->getUrl()); - $dbItem->setAuthor($item->getAuthor()); - $dbItem->setSearchIndex($item->getSearchIndex()); - $dbItem->setRtl($item->getRtl()); - $dbItem->setLastModified($item->getLastModified()); - $dbItem->setPubDate($item->getPubDate()); - $dbItem->setUpdatedDate($item->getUpdatedDate()); - $dbItem->setEnclosureMime($item->getEnclosureMime()); - $dbItem->setEnclosureLink($item->getEnclosureLink()); - $dbItem->setBody( - $this->purifier->purify($item->getBody()) - ); - - // update modes: 0 nothing, 1 set unread - if ($existingFeed->getUpdateMode() === 1) { - $dbItem->setUnread(true); - } - - $this->itemMapper->update($dbItem); - } - } catch (DoesNotExistException $ex) { - $item->setBody( - $this->purifier->purify($item->getBody()) - ); - $this->itemMapper->insert($item); - } - } - - // mark feed as successfully updated - $existingFeed->setUpdateErrorCount(0); - $existingFeed->setLastUpdateError(''); - } catch (ReadErrorException $ex) { - $existingFeed->setUpdateErrorCount( - $existingFeed->getUpdateErrorCount() + 1 - ); - $existingFeed->setLastUpdateError($ex->getMessage()); - } - - $this->feedMapper->update($existingFeed); - - return $this->find($userId, $feedId); - } - - /** - * Import articles - * - * @param array $json the array with json - * @param string $userId the username - * - * @return Feed if one had to be created for nonexistent feeds - */ - public function importArticles($json, $userId) - { - $url = 'http://nextcloud/nofeed'; - $urlHash = md5($url); - - // build assoc array for fast access - $feeds = $this->findAllForUser($userId); - $feedsDict = []; - foreach ($feeds as $feed) { - $feedsDict[$feed->getLink()] = $feed; - } - - $createdFeed = false; - - // loop over all items and get the corresponding feed - // if the feed does not exist, create a separate feed for them - foreach ($json as $entry) { - $item = Item::fromImport($entry); - $feedLink = $entry['feedLink']; // this is not set on the item yet - - if (array_key_exists($feedLink, $feedsDict)) { - $feed = $feedsDict[$feedLink]; - $item->setFeedId($feed->getId()); - } elseif (array_key_exists($url, $feedsDict)) { - $feed = $feedsDict[$url]; - $item->setFeedId($feed->getId()); - } else { - $createdFeed = true; - $feed = new Feed(); - $feed->setUserId($userId); - $feed->setLink($url); - $feed->setUrl($url); - $feed->setTitle($this->l10n->t('Articles without feed')); - $feed->setAdded($this->timeFactory->getTime()); - $feed->setFolderId(null); - $feed->setPreventUpdate(true); - /** @var Feed $feed */ - $feed = $this->feedMapper->insert($feed); - - $item->setFeedId($feed->getId()); - $feedsDict[$feed->getLink()] = $feed; - } - - try { - // if item exists, copy the status - $existingItem = $this->itemMapper->findByGuidHash( - $item->getGuidHash(), - $feed->getId(), - $userId - ); - $existingItem->setStatus($item->getStatus()); - $this->itemMapper->update($existingItem); - } catch (DoesNotExistException $ex) { - $item->setBody($this->purifier->purify($item->getBody())); - $item->generateSearchIndex(); - $this->itemMapper->insert($item); - } - } - - if ($createdFeed) { - return $this->feedMapper->findByUrlHash($urlHash, $userId); - } - - return null; - } - - - /** - * Use this to mark a feed as deleted. That way it can be un-deleted - * - * @param int $feedId the id of the feed that should be deleted - * @param string $userId the name of the user for security reasons - * - * @throws ServiceNotFoundException when feed does not exist - */ - public function markDeleted(int $feedId, string $userId) - { - $feed = $this->find($userId, $feedId); - $feed->setDeletedAt($this->timeFactory->getTime()); - $this->feedMapper->update($feed); - } - - - /** - * Use this to undo a feed deletion - * - * @param int $feedId the id of the feed that should be restored - * @param string $userId the name of the user for security reasons - * - * @throws ServiceNotFoundException when feed does not exist - */ - public function unmarkDeleted(int $feedId, string $userId) - { - $feed = $this->find($userId, $feedId); - $feed->setDeletedAt(0); - $this->feedMapper->update($feed); - } - - - /** - * Deletes all deleted feeds - * - * @param string $userId if given it purges only feeds of that user - * @param boolean $useInterval defaults to true, if true it only purges - * entries in a given interval to give the user a chance to undo the - * deletion - */ - public function purgeDeleted($userId = null, $useInterval = true) - { - $deleteOlderThan = null; - - if ($useInterval) { - $now = $this->timeFactory->getTime(); - $deleteOlderThan = $now - $this->autoPurgeMinimumInterval; - } - - $toDelete = $this->feedMapper->getToDelete($deleteOlderThan, $userId); - - foreach ($toDelete as $feed) { - $this->feedMapper->delete($feed); - } - } - - - /** - * Deletes all feeds of a user, delete items first since the user_id - * is not defined in there - * - * @param string $userId the name of the user - */ - public function deleteUser($userId) - { - $this->feedMapper->deleteUser($userId); - } - - /** - * @param int $feedId ID of the feed. - * @param string $userId ID of the user. - * @param array $diff An array containing the fields to update, e.g.: - * - * [ - * 'ordering' => 1, - * 'fullTextEnabled' => true, - * 'pinned' => true, - * 'updateMode' => 0, - * 'title' => 'title' - * ] - * - * - * @throws ServiceNotFoundException if feed does not exist - * @return Feed The patched feed - */ - public function patch(int $feedId, string $userId, array $diff = []) - { - $feed = $this->find($userId, $feedId); - - foreach ($diff as $attribute => $value) { - $method = 'set' . ucfirst($attribute); - $feed->$method($value); - } - - // special feed updates - if (array_key_exists('fullTextEnabled', $diff)) { - // disable caching for the next update - $feed->setHttpEtag(''); - $feed->setHttpLastModified(0); - $this->feedMapper->update($feed); - return $this->update($userId, $feedId, true); - } - - return $this->feedMapper->update($feed); - } - - public function findAll(): array - { - return $this->feedMapper->findAll(); - } -} diff --git a/lib/Service/FeedServiceV2.php b/lib/Service/FeedServiceV2.php index 16402d0da..88f18a3bf 100644 --- a/lib/Service/FeedServiceV2.php +++ b/lib/Service/FeedServiceV2.php @@ -29,7 +29,6 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCA\News\Db\Feed; use OCA\News\Db\Item; -use OCA\News\Db\FeedMapper; use OCA\News\Db\ItemMapper; use OCA\News\Fetcher\Fetcher; use OCA\News\Config\Config; @@ -104,22 +103,6 @@ class FeedServiceV2 extends Service return $this->mapper->findAllFromUser($userId, $params); } - /** - * Finds a feed of a user - * - * @param string $userId the name of the user - * @param string $id the id of the feed - * - * @return Feed - * - * @throws DoesNotExistException - * @throws MultipleObjectsReturnedException - */ - public function findForUser(string $userId, string $id): Feed - { - return $this->mapper->findFromUser($userId, $id); - } - /** * @param int|null $id * @@ -139,6 +122,7 @@ class FeedServiceV2 extends Service */ public function findAllForUserRecursive(string $userId): array { + /** @var Feed[] $feeds */ $feeds = $this->mapper->findAllFromUser($userId); foreach ($feeds as &$feed) { @@ -167,12 +151,24 @@ class FeedServiceV2 extends Service * @return bool */ public function existsForUser(string $userID, string $url): bool + { + return $this->findByURL($userID, $url) !== null; + } + + /** + * Check if a feed exists for a user + * + * @param string $userID the name of the user + * @param string $url the feed URL + * + * @return Entity|Feed|null + */ + public function findByURL(string $userID, string $url): ?Entity { try { - $this->mapper->findByURL($userID, $url); - return true; + return $this->mapper->findByURL($userID, $url); } catch (DoesNotExistException $e) { - return false; + return null; } } @@ -274,58 +270,59 @@ class FeedServiceV2 extends Service $feed->getBasicAuthUser(), $feed->getBasicAuthPassword() ); - - // if there is no feed it means that no update took place - if (!$fetchedFeed) { - return $feed; - } - - // update number of articles on every feed update - $itemCount = count($items); - - // this is needed to adjust to updates that add more items - // than when the feed was created. You can't update the count - // if it's lower because it may be due to the caching headers - // that were sent as the request and it might cause unwanted - // deletion and reappearing of feeds - if ($itemCount > $feed->getArticlesPerUpdate()) { - $feed->setArticlesPerUpdate($itemCount); - } - - $feed->setHttpLastModified($fetchedFeed->getHttpLastModified()) - ->setHttpEtag($fetchedFeed->getHttpEtag()) - ->setLocation($fetchedFeed->getLocation()); - - // insert items in reverse order because the first one is - // usually the newest item - for ($i = $itemCount - 1; $i >= 0; $i--) { - $item = $items[$i]; - $item->setFeedId($feed->getId()) - ->setBody($this->purifier->purify($item->getBody())); - - // update modes: 0 nothing, 1 set unread - if ($feed->getUpdateMode() === 1) { - $item->setUnread(true); - } - - $this->itemService->insertOrUpdate($item); - } - - // mark feed as successfully updated - $feed->setUpdateErrorCount(0); - $feed->setLastUpdateError(null); } catch (ReadErrorException $ex) { $feed->setUpdateErrorCount($feed->getUpdateErrorCount() + 1); $feed->setLastUpdateError($ex->getMessage()); + + return $this->mapper->update($feed); } - return $this->mapper->update($feed); - } + // if there is no feed it means that no update took place + if (!$fetchedFeed) { + return $feed; + } - public function delete(string $user, int $id): void - { - $feed = $this->mapper->findFromUser($user, $id); - $this->mapper->delete($feed); + // update number of articles on every feed update + $itemCount = count($items); + + // this is needed to adjust to updates that add more items + // than when the feed was created. You can't update the count + // if it's lower because it may be due to the caching headers + // that were sent as the request and it might cause unwanted + // deletion and reappearing of feeds + if ($itemCount > $feed->getArticlesPerUpdate()) { + $feed->setArticlesPerUpdate($itemCount); + } + + $feed->setHttpLastModified($fetchedFeed->getHttpLastModified()) + ->setHttpEtag($fetchedFeed->getHttpEtag()) + ->setLocation($fetchedFeed->getLocation()); + + foreach (array_reverse($items) as &$item) { + $item->setFeedId($feed->getId()) + ->setBody($this->purifier->purify($item->getBody())); + + // update modes: 0 nothing, 1 set unread + if ($feed->getUpdateMode() === Feed::UPDATE_MODE_NORMAL) { + $item->setUnread(true); + } + + $item = $this->itemService->insertOrUpdate($item); + } + + + // mark feed as successfully updated + $feed->setUpdateErrorCount(0); + $feed->setLastUpdateError(null); + + $unreadCount = 0; + array_map(function (Item $item) use (&$unreadCount) { + if ($item->isUnread()) { + $unreadCount++; + } + }, $items); + + return $this->mapper->update($feed)->setUnreadCount($unreadCount); } /** @@ -341,6 +338,11 @@ class FeedServiceV2 extends Service $this->mapper->purgeDeleted($userID, $minTimestamp); } + /** + * Fetch all feeds. + * + * @see FeedServiceV2::fetch() + */ public function fetchAll(): void { foreach ($this->findAll() as $feed) { diff --git a/lib/Service/FolderServiceV2.php b/lib/Service/FolderServiceV2.php index ae04c8089..25903c17b 100644 --- a/lib/Service/FolderServiceV2.php +++ b/lib/Service/FolderServiceV2.php @@ -63,28 +63,6 @@ class FolderServiceV2 extends Service return $this->mapper->findAllFromUser($userId, $params); } - /** - * Finds a folder of a user - * - * @param string $userId The name/ID of the user - * @param int|null $folderId ID of the folder - * - * @return Folder - * - * @throws ServiceConflictException - * @throws ServiceNotFoundException - */ - public function findForUser(string $userId, ?int $folderId): Entity - { - try { - return $this->mapper->findFromUser($userId, $folderId); - } catch (DoesNotExistException $e) { - throw new ServiceNotFoundException('Folder not found'); - } catch (MultipleObjectsReturnedException $e) { - throw new ServiceConflictException('Multiple folders found'); - } - } - /** * Find all folders and it's feeds. * @@ -133,23 +111,6 @@ class FolderServiceV2 extends Service return $this->mapper->insert($folder); } - /** - * Delete a feed. - * - * @param string $userId Folder owner - * @param int $folderId Folder ID - * - * @return Folder - * @throws ServiceConflictException - * @throws ServiceNotFoundException - */ - public function delete(string $userId, int $folderId): Entity - { - $folder = $this->findForUser($userId, $folderId); - - return $this->mapper->delete($folder); - } - /** * Purge all deleted folders. * @@ -174,8 +135,9 @@ class FolderServiceV2 extends Service */ public function rename(string $userId, int $folderId, string $newName): Entity { - $folder = $this->findForUser($userId, $folderId); + $folder = $this->find($userId, $folderId); $folder->setName($newName); + return $this->mapper->update($folder); } @@ -192,7 +154,7 @@ class FolderServiceV2 extends Service */ public function markDelete(string $userId, int $folderId, bool $mark): Entity { - $folder = $this->findForUser($userId, $folderId); + $folder = $this->find($userId, $folderId); $time = $mark ? $this->timeFactory->getTime() : 0; $folder->setDeletedAt($time); @@ -212,21 +174,8 @@ class FolderServiceV2 extends Service */ public function open(string $userId, ?int $folderId, bool $open): Entity { - $folder = $this->findForUser($userId, $folderId); + $folder = $this->find($userId, $folderId); $folder->setOpened($open); return $this->mapper->update($folder); } - - /** - * Delete all folders of a user - * - * @param string $userId User ID/name - */ - public function deleteUser(string $userId): void - { - $folders = $this->findAllForUser($userId); - foreach ($folders as $folder) { - $this->mapper->delete($folder); - } - } } diff --git a/lib/Service/ImportService.php b/lib/Service/ImportService.php new file mode 100644 index 000000000..c3334fad2 --- /dev/null +++ b/lib/Service/ImportService.php @@ -0,0 +1,125 @@ + + * @author Bernhard Posselt + * @copyright 2012 Alessandro Cosentino + * @copyright 2012-2014 Bernhard Posselt + */ + +namespace OCA\News\Service; + +use \OCA\News\Db\Item; +use \OCA\News\Db\Feed; + +use \Psr\Log\LoggerInterface; +use \HTMLPurifier; + +/** + * Class ImportService + * + * @package OCA\News\Service + */ +class ImportService +{ + /** + * Items service. + * + * @var ItemServiceV2 + */ + protected $itemService; + /** + * Feeds service. + * + * @var FeedServiceV2 + */ + protected $feedService; + /** + * @var LoggerInterface + */ + protected $logger; + /** + * @var HTMLPurifier + */ + protected $purifier; + + /** + * FeedService constructor. + * + * @param FeedServiceV2 $feedService Service for feeds + * @param ItemServiceV2 $itemService Service to manage items + * @param HTMLPurifier $purifier HTML Purifier + * @param LoggerInterface $logger Logger + */ + public function __construct( + FeedServiceV2 $feedService, + ItemServiceV2 $itemService, + HTMLPurifier $purifier, + LoggerInterface $logger + ) { + $this->itemService = $itemService; + $this->feedService = $feedService; + $this->purifier = $purifier; + $this->logger = $logger; + } + + /** + * @param string $userId + * @param array $json + * + * @return array|null + */ + public function importArticles(string $userId, array $json) + { + $url = 'http://nextcloud/nofeed'; + + // build assoc array for fast access + $feeds = $this->feedService->findAllForUser($userId); + $feedsDict = []; + foreach ($feeds as $feed) { + $feedsDict[$feed->getLink()] = $feed; + } + + $createdFeed = false; + + // loop over all items and get the corresponding feed + // if the feed does not exist, create a separate feed for them + foreach ($json as $entry) { + $item = Item::fromImport($entry); + $feedLink = $entry['feedLink']; // this is not set on the item yet + + if (array_key_exists($feedLink, $feedsDict)) { + $feed = $feedsDict[$feedLink]; + } else { + $createdFeed = true; + $feed = new Feed(); + $feed->setUserId($userId) + ->setUrlHash(md5($url)) + ->setLink($url) + ->setUrl($url) + ->setTitle('Articles without feed') + ->setAdded(time()) + ->setFolderId(null) + ->setPreventUpdate(true); + + $feed = $this->feedService->insert($feed); + $feedsDict[$feed->getLink()] = $feed; + } + + $item->setFeedId($feed->getId()) + ->setBody($this->purifier->purify($item->getBody())) + ->generateSearchIndex(); + $this->itemService->insertOrUpdate($item); + } + + if (!$createdFeed) { + return null; + } + + return $this->feedService->findByURL($userId, $url); + } +} diff --git a/lib/Service/ItemService.php b/lib/Service/ItemService.php index ab5536137..1b459d49c 100644 --- a/lib/Service/ItemService.php +++ b/lib/Service/ItemService.php @@ -15,6 +15,7 @@ namespace OCA\News\Service; use OCA\News\AppInfo\Application; use OCA\News\Db\Item; +use OCA\News\Db\ItemMapperV2; use OCA\News\Service\Exceptions\ServiceNotFoundException; use OCP\AppFramework\Db\Entity; use OCP\IConfig; @@ -34,12 +35,22 @@ use Psr\Log\LoggerInterface; class ItemService extends Service { + /** + * @var IConfig + */ private $config; + /** + * @var Time + */ private $timeFactory; - private $itemMapper; + /** + * @var ItemMapper + */ + private $oldItemMapper; public function __construct( - ItemMapper $itemMapper, + ItemMapperV2 $itemMapper, + ItemMapper $oldItemMapper, Time $timeFactory, IConfig $config, LoggerInterface $logger @@ -47,7 +58,7 @@ class ItemService extends Service parent::__construct($itemMapper, $logger); $this->config = $config; $this->timeFactory = $timeFactory; - $this->itemMapper = $itemMapper; + $this->oldItemMapper = $oldItemMapper; } @@ -68,21 +79,21 @@ class ItemService extends Service { switch ($type) { case FeedType::FEED: - return $this->itemMapper->findAllNewFeed( + return $this->oldItemMapper->findAllNewFeed( $id, $updatedSince, $showAll, $userId ); case FeedType::FOLDER: - return $this->itemMapper->findAllNewFolder( + return $this->oldItemMapper->findAllNewFolder( $id, $updatedSince, $showAll, $userId ); default: - return $this->itemMapper->findAllNew( + return $this->oldItemMapper->findAllNew( $updatedSince, $type, $showAll, @@ -120,7 +131,7 @@ class ItemService extends Service ) { switch ($type) { case FeedType::FEED: - return $this->itemMapper->findAllFeed( + return $this->oldItemMapper->findAllFeed( $id, $limit, $offset, @@ -130,7 +141,7 @@ class ItemService extends Service $search ); case FeedType::FOLDER: - return $this->itemMapper->findAllFolder( + return $this->oldItemMapper->findAllFolder( $id, $limit, $offset, @@ -140,7 +151,7 @@ class ItemService extends Service $search ); default: - return $this->itemMapper->findAllItems( + return $this->oldItemMapper->findAllItems( $limit, $offset, $type, @@ -154,7 +165,7 @@ class ItemService extends Service public function findAllForUser(string $userId, array $params = []): array { - return $this->itemMapper->findAllFromUser($userId); + return $this->mapper->findAllFromUser($userId, $params); } @@ -171,18 +182,11 @@ class ItemService extends Service public function star($feedId, $guidHash, $isStarred, $userId) { try { - /** - * @var Item $item -*/ - $item = $this->itemMapper->findByGuidHash( - $guidHash, - $feedId, - $userId - ); + $item = $this->mapper->findByGuidHash($feedId, $guidHash); $item->setStarred($isStarred); - $this->itemMapper->update($item); + $this->mapper->update($item); } catch (DoesNotExistException $ex) { throw new ServiceNotFoundException($ex->getMessage()); } @@ -202,7 +206,7 @@ class ItemService extends Service { try { $lastModified = $this->timeFactory->getMicroTime(); - $this->itemMapper->readItem($itemId, $isRead, $lastModified, $userId); + $this->oldItemMapper->readItem($itemId, $isRead, $lastModified, $userId); } catch (DoesNotExistException $ex) { throw new ServiceNotFoundException($ex->getMessage()); } @@ -220,7 +224,7 @@ class ItemService extends Service public function readAll($highestItemId, $userId) { $time = $this->timeFactory->getMicroTime(); - $this->itemMapper->readAll($highestItemId, $time, $userId); + $this->oldItemMapper->readAll($highestItemId, $time, $userId); } @@ -236,7 +240,7 @@ class ItemService extends Service public function readFolder(?int $folderId, $highestItemId, $userId) { $time = $this->timeFactory->getMicroTime(); - $this->itemMapper->readFolder( + $this->oldItemMapper->readFolder( $folderId, $highestItemId, $time, @@ -257,7 +261,7 @@ class ItemService extends Service public function readFeed($feedId, $highestItemId, $userId) { $time = $this->timeFactory->getMicroTime(); - $this->itemMapper->readFeed($feedId, $highestItemId, $time, $userId); + $this->oldItemMapper->readFeed($feedId, $highestItemId, $time, $userId); } @@ -275,7 +279,7 @@ class ItemService extends Service Application::DEFAULT_SETTINGS['autoPurgeCount'] ); if ($count >= 0) { - $this->itemMapper->deleteReadOlderThanThreshold($count); + $this->oldItemMapper->deleteReadOlderThanThreshold($count); } } @@ -290,7 +294,7 @@ class ItemService extends Service public function getNewestItemId($userId) { try { - return $this->itemMapper->getNewestItemId($userId); + return $this->oldItemMapper->getNewestItemId($userId); } catch (DoesNotExistException $ex) { throw new ServiceNotFoundException($ex->getMessage()); } @@ -305,7 +309,7 @@ class ItemService extends Service */ public function starredCount($userId) { - return $this->itemMapper->starredCount($userId); + return $this->oldItemMapper->starredCount($userId); } @@ -315,18 +319,7 @@ class ItemService extends Service */ public function getUnreadOrStarred($userId) { - return $this->itemMapper->findAllUnreadOrStarred($userId); - } - - - /** - * Deletes all items of a user - * - * @param string $userId the name of the user - */ - public function deleteUser($userId) - { - $this->itemMapper->deleteUser($userId); + return $this->oldItemMapper->findAllUnreadOrStarred($userId); } @@ -335,7 +328,7 @@ class ItemService extends Service */ public function generateSearchIndices() { - $this->itemMapper->updateSearchIndices(); + $this->oldItemMapper->updateSearchIndices(); } public function findAll(): array diff --git a/lib/Service/ItemServiceV2.php b/lib/Service/ItemServiceV2.php index 0a04fd8ad..54cefa197 100644 --- a/lib/Service/ItemServiceV2.php +++ b/lib/Service/ItemServiceV2.php @@ -16,6 +16,7 @@ use OCA\News\AppInfo\Application; use OCA\News\Db\Item; use OCA\News\Db\ItemMapperV2; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Entity; use OCP\IConfig; use Psr\Log\LoggerInterface; @@ -76,8 +77,10 @@ class ItemServiceV2 extends Service * Insert an item or update. * * @param Item $item + * + * @return Entity|Item The updated/inserted item */ - public function insertOrUpdate(Item $item) + public function insertOrUpdate(Item $item): Entity { try { $db_item = $this->mapper->findByGuidHash($item->getFeedId(), $item->getGuidHash()); @@ -94,17 +97,24 @@ class ItemServiceV2 extends Service $item->resetUpdatedFields(); } - $this->mapper->update($item); + return $this->mapper->update($item); } catch (DoesNotExistException $exception) { - $this->mapper->insert($item); + return $this->mapper->insert($item); } } + /** + * @param int $feedId + * + * @return array + */ public function findAllForFeed(int $feedId): array { return $this->mapper->findAllForFeed($feedId); } + + public function purgeOverThreshold(int $threshold = null) { @@ -120,4 +130,13 @@ class ItemServiceV2 extends Service return $this->mapper->deleteOverThreshold($threshold); } + + /** + * @param int $feedId + * @param string $guidHash + */ + public function findForGuidHash(int $feedId, string $guidHash) + { + return $this->mapper->findByGuidHash($feedId, $guidHash); + } } diff --git a/lib/Service/OpmlService.php b/lib/Service/OpmlService.php index ea570cf68..932b5c9a5 100644 --- a/lib/Service/OpmlService.php +++ b/lib/Service/OpmlService.php @@ -29,11 +29,6 @@ class OpmlService */ private $feedService; - /** - * @var ItemService - */ - private $itemService; - /** * @var OPMLExporter */ @@ -42,12 +37,10 @@ class OpmlService public function __construct( FolderServiceV2 $folderService, FeedServiceV2 $feedService, - ItemServiceV2 $itemService, OPMLExporter $exporter ) { $this->folderService = $folderService; $this->feedService = $feedService; - $this->itemService = $itemService; $this->exporter = $exporter; } diff --git a/lib/Service/Service.php b/lib/Service/Service.php index 597a99647..ea7c3995d 100644 --- a/lib/Service/Service.php +++ b/lib/Service/Service.php @@ -43,7 +43,7 @@ abstract class Service * @param NewsMapperV2 $mapper * @param LoggerInterface $logger */ - public function __construct($mapper, LoggerInterface $logger) + public function __construct(NewsMapperV2 $mapper, LoggerInterface $logger) { $this->mapper = $mapper; $this->logger = $logger; @@ -76,11 +76,40 @@ abstract class Service * @throws ServiceNotFoundException if the entity does not exist, or there * are more than one of it */ - public function delete(string $userId, int $id) + public function delete(string $userId, int $id): Entity { $entity = $this->find($userId, $id); - $this->mapper->delete($entity); + return $this->mapper->delete($entity); + } + + + /** + * Insert an entity + * + * @param Entity $entity The entity to insert + * + * @return Entity The inserted entity + */ + public function insert(Entity $entity): Entity + { + return $this->mapper->insert($entity); + } + + + /** + * Update an entity + * + * @param string $userId the name of the user for security reasons + * @param Entity $entity the entity + * + * @throws ServiceNotFoundException if the entity does not exist, or there + * are more than one of it + */ + public function update(string $userId, Entity $entity): Entity + { + $this->find($userId, $entity->getId()); + return $this->mapper->update($entity); } @@ -104,4 +133,17 @@ abstract class Service throw new ServiceNotFoundException($ex->getMessage()); } } + + /** + * Delete all items of a user + * + * @param string $userId User ID/name + */ + public function deleteUser(string $userId): void + { + $items = $this->findAllForUser($userId); + foreach ($items as $item) { + $this->mapper->delete($item); + } + } } diff --git a/lib/Service/StatusService.php b/lib/Service/StatusService.php index f46624cd0..d33d99bad 100644 --- a/lib/Service/StatusService.php +++ b/lib/Service/StatusService.php @@ -28,12 +28,11 @@ class StatusService public function __construct( IConfig $settings, - IDBConnection $connection, - string $AppName + IDBConnection $connection ) { $this->settings = $settings; - $this->appName = $AppName; $this->connection = $connection; + $this->appName = Application::NAME; } /** diff --git a/phpunit.xml b/phpunit.xml index 08a34a72f..c5c549579 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,6 +11,7 @@ ./lib/AppInfo/Application.php ./lib/Controller/JSONHttpErrorTrait.php ./lib/**Exception.php + ./lib/Migration/**.php diff --git a/tests/Unit/Command/UpdateFeedTest.php b/tests/Unit/Command/UpdateFeedTest.php index bd0cd34e4..c0885f85a 100644 --- a/tests/Unit/Command/UpdateFeedTest.php +++ b/tests/Unit/Command/UpdateFeedTest.php @@ -74,7 +74,7 @@ class UpdateFeedTest extends TestCase ->method('getLastUpdateError'); $this->service->expects($this->exactly(1)) - ->method('findForUser') + ->method('find') ->with('admin', '1') ->willReturn($feed); @@ -108,7 +108,7 @@ class UpdateFeedTest extends TestCase ->willReturn('Problem'); $this->service->expects($this->exactly(1)) - ->method('findForUser') + ->method('find') ->with('admin', '1') ->willReturn($feed); @@ -140,7 +140,7 @@ class UpdateFeedTest extends TestCase $feed = $this->createMock(Feed::class); $this->service->expects($this->exactly(1)) - ->method('findForUser') + ->method('find') ->with('admin', '1') ->willReturn($feed); diff --git a/tests/Unit/Controller/EntityApiSerializerTest.php b/tests/Unit/Controller/EntityApiSerializerTest.php deleted file mode 100644 index 8709275e7..000000000 --- a/tests/Unit/Controller/EntityApiSerializerTest.php +++ /dev/null @@ -1,144 +0,0 @@ - - * @author Bernhard Posselt - * @copyright 2012 Alessandro Cosentino - * @copyright 2012-2014 Bernhard Posselt - */ - -namespace OCA\News\Tests\Unit\Controller; - -use OCA\News\Controller\EntityApiSerializer; -use \OCP\AppFramework\Http\Response; -use \OCP\AppFramework\Db\Entity; - -use \OCA\News\Db\Item; -use PHPUnit\Framework\TestCase; - -class TestEntity extends Entity -{ - -} - -class EntityApiSerializerTest extends TestCase -{ - - - public function testSerializeSingle() - { - $item = new Item(); - $item->setUnread(true); - $item->setId(3); - $item->setGuid('guid'); - $item->setGuidHash('guidhash'); - $item->setFeedId(123); - - $serializer = new EntityApiSerializer('items'); - $result = $serializer->serialize($item); - - $this->assertTrue($result['items'][0]['unread']); - } - - - public function testSerializeMultiple() - { - $item = new Item(); - $item->setUnread(true); - $item->setId(3); - $item->setGuid('guid'); - $item->setGuidHash('guidhash'); - $item->setFeedId(123); - - $item2 = new Item(); - $item2->setUnread(false); - $item2->setId(5); - $item2->setGuid('guid'); - $item2->setGuidHash('guidhash'); - $item2->setFeedId(123); - - $serializer = new EntityApiSerializer('items'); - - $result = $serializer->serialize([$item, $item2]); - - $this->assertTrue($result['items'][0]['unread']); - $this->assertFalse($result['items'][1]['unread']); - } - - - public function testResponseNoChange() - { - $response = new Response(); - $serializer = new EntityApiSerializer('items'); - - $result = $serializer->serialize($response); - - $this->assertEquals($response, $result); - } - - - public function testCompleteArraysTransformed() - { - $item = new Item(); - $item->setUnread(true); - $item->setId(3); - $item->setGuid('guid'); - $item->setGuidHash('guidhash'); - $item->setFeedId(123); - - $item2 = new Item(); - $item2->setUnread(false); - $item2->setId(5); - $item2->setGuid('guid'); - $item2->setGuidHash('guidhash'); - $item2->setFeedId(123); - - $serializer = new EntityApiSerializer('items'); - - $in = [ - 'items' => [$item, $item2], - 'test' => 1 - ]; - - $result = $serializer->serialize($in); - - $this->assertTrue($result['items'][0]['unread']); - $this->assertFalse($result['items'][1]['unread']); - $this->assertEquals(1, $result['test']); - } - - - public function testNoEntityNoChange() - { - $serializer = new EntityApiSerializer('items'); - - $in = [ - 'items' => ['hi', '2'], - 'test' => 1 - ]; - - $result = $serializer->serialize($in); - - $this->assertEquals('hi', $result['items'][0]); - $this->assertEquals('2', $result['items'][1]); - $this->assertEquals(1, $result['test']); - } - - - public function testEntitiesNoChange() - { - $serializer = new EntityApiSerializer('items'); - - $in = [ - 'items' => [new TestEntity()] - ]; - - $result = $serializer->serialize($in); - - $this->assertEquals($in, $result); - } -} \ No newline at end of file diff --git a/tests/Unit/Controller/FeedApiControllerTest.php b/tests/Unit/Controller/FeedApiControllerTest.php index 9cfc47cec..b31d9fd43 100644 --- a/tests/Unit/Controller/FeedApiControllerTest.php +++ b/tests/Unit/Controller/FeedApiControllerTest.php @@ -15,18 +15,15 @@ namespace OCA\News\Tests\Unit\Controller; +use Exception; use OCA\News\Controller\FeedApiController; -use OCA\News\Service\FeedService; use OCA\News\Service\FeedServiceV2; use OCA\News\Service\ItemService; -use OCA\News\Service\ItemServiceV2; -use OCA\News\Utility\PsrLogger; use \OCP\AppFramework\Http; use \OCA\News\Service\Exceptions\ServiceNotFoundException; use \OCA\News\Service\Exceptions\ServiceConflictException; use \OCA\News\Db\Feed; -use OCP\ILogger; use OCP\IRequest; use OCP\IUser; use OCP\IUserSession; @@ -36,10 +33,6 @@ use Psr\Log\LoggerInterface; class FeedApiControllerTest extends TestCase { - /** - * @var \PHPUnit\Framework\MockObject\MockObject|FeedService - */ - private $oldFeedService; /** * @var \PHPUnit\Framework\MockObject\MockObject|FeedServiceV2 @@ -57,7 +50,10 @@ class FeedApiControllerTest extends TestCase private $logger; private $class; - private $user; + /** + * @var string + */ + private $userID = '123'; private $msg; protected function setUp(): void @@ -72,18 +68,15 @@ class FeedApiControllerTest extends TestCase $userSession = $this->getMockBuilder(IUserSession::class) ->disableOriginalConstructor() ->getMock(); - $this->user = $this->getMockBuilder(IUser::class) + $user = $this->getMockBuilder(IUser::class) ->disableOriginalConstructor() ->getMock(); $userSession->expects($this->any()) ->method('getUser') - ->will($this->returnValue($this->user)); - $this->user->expects($this->any()) + ->will($this->returnValue($user)); + $user->expects($this->any()) ->method('getUID') - ->will($this->returnValue('123')); - $this->oldFeedService = $this->getMockBuilder(FeedService::class) - ->disableOriginalConstructor() - ->getMock(); + ->will($this->returnValue($this->userID)); $this->feedService = $this->getMockBuilder(FeedServiceV2::class) ->disableOriginalConstructor() ->getMock(); @@ -93,7 +86,6 @@ class FeedApiControllerTest extends TestCase $this->class = new FeedApiController( $request, $userSession, - $this->oldFeedService, $this->feedService, $this->itemService, $this->logger @@ -110,25 +102,26 @@ class FeedApiControllerTest extends TestCase $this->itemService->expects($this->once()) ->method('starredCount') - ->with($this->equalTo($this->user->getUID())) + ->with($this->equalTo($this->userID)) ->will($this->returnValue($starredCount)); $this->itemService->expects($this->once()) ->method('getNewestItemId') - ->with($this->equalTo($this->user->getUID())) + ->with($this->equalTo($this->userID)) ->will($this->returnValue($newestItemId)); $this->feedService->expects($this->once()) ->method('findAllForUser') - ->with($this->equalTo($this->user->getUID())) + ->with($this->equalTo($this->userID)) ->will($this->returnValue($feeds)); $response = $this->class->index(); $this->assertEquals( [ - 'feeds' => [$feeds[0]->toAPI()], - 'starredCount' => $starredCount, - 'newestItemId' => $newestItemId - ], $response + 'feeds' => [$feeds[0]->toAPI()], + 'starredCount' => $starredCount, + 'newestItemId' => $newestItemId + ], + $response ); } @@ -140,24 +133,25 @@ class FeedApiControllerTest extends TestCase $this->itemService->expects($this->once()) ->method('starredCount') - ->with($this->equalTo($this->user->getUID())) + ->with($this->equalTo($this->userID)) ->will($this->returnValue($starredCount)); $this->itemService->expects($this->once()) ->method('getNewestItemId') - ->with($this->equalTo($this->user->getUID())) + ->with($this->equalTo($this->userID)) ->will($this->throwException(new ServiceNotFoundException(''))); $this->feedService->expects($this->once()) ->method('findAllForUser') - ->with($this->equalTo($this->user->getUID())) + ->with($this->equalTo($this->userID)) ->will($this->returnValue($feeds)); $response = $this->class->index(); $this->assertEquals( [ - 'feeds' => [$feeds[0]->toAPI()], - 'starredCount' => $starredCount, - ], $response + 'feeds' => [$feeds[0]->toAPI()], + 'starredCount' => $starredCount, + ], + $response ); } @@ -167,7 +161,7 @@ class FeedApiControllerTest extends TestCase $this->feedService->expects($this->once()) ->method('delete') ->with( - $this->equalTo($this->user->getUID()), + $this->equalTo($this->userID), $this->equalTo(2) ); @@ -202,7 +196,7 @@ class FeedApiControllerTest extends TestCase $this->feedService->expects($this->once()) ->method('create') - ->with($this->user->getUID(), 'url', 3) + ->with($this->userID, 'url', 3) ->will($this->returnValue($feeds[0])); $this->itemService->expects($this->once()) ->method('getNewestItemId') @@ -229,7 +223,7 @@ class FeedApiControllerTest extends TestCase $this->feedService->expects($this->once()) ->method('create') - ->with($this->user->getUID(), 'ho', 3) + ->with($this->userID, 'ho', 3) ->will($this->returnValue($feeds[0])); $this->itemService->expects($this->once()) ->method('getNewestItemId') @@ -239,8 +233,9 @@ class FeedApiControllerTest extends TestCase $this->assertEquals( [ - 'feeds' => [$feeds[0]->toAPI()] - ], $response + 'feeds' => [$feeds[0]->toAPI()] + ], + $response ); } @@ -288,7 +283,7 @@ class FeedApiControllerTest extends TestCase ->with( $this->equalTo(3), $this->equalTo(30), - $this->equalTo($this->user->getUID()) + $this->equalTo($this->userID) ); $this->class->read(3, 30); @@ -297,13 +292,18 @@ class FeedApiControllerTest extends TestCase public function testMove() { - $this->oldFeedService->expects($this->once()) - ->method('patch') - ->with( - $this->equalTo(3), - $this->equalTo($this->user->getUID()), - $this->equalTo(['folderId' => 30]) - ); + $feed = $this->getMockBuilder(Feed::class)->getMock(); + $feed->expects($this->once()) + ->method('setFolderId') + ->with(30) + ->will($this->returnSelf()); + $this->feedService->expects($this->once()) + ->method('find') + ->with($this->userID, 3) + ->will($this->returnValue($feed)); + $this->feedService->expects($this->once()) + ->method('update') + ->with($this->userID, $feed); $this->class->move(3, 30); } @@ -311,8 +311,8 @@ class FeedApiControllerTest extends TestCase public function testMoveDoesNotExist() { - $this->oldFeedService->expects($this->once()) - ->method('patch') + $this->feedService->expects($this->once()) + ->method('update') ->will( $this->throwException(new ServiceNotFoundException($this->msg)) ); @@ -327,18 +327,20 @@ class FeedApiControllerTest extends TestCase public function testRename() { - $feedId = 3; - $feedTitle = 'test'; + $feed = $this->getMockBuilder(Feed::class)->getMock(); + $feed->expects($this->once()) + ->method('setTitle') + ->with('test') + ->will($this->returnSelf()); + $this->feedService->expects($this->once()) + ->method('find') + ->with($this->userID, 3) + ->will($this->returnValue($feed)); + $this->feedService->expects($this->once()) + ->method('update') + ->with($this->userID, $feed); - $this->oldFeedService->expects($this->once()) - ->method('patch') - ->with( - $this->equalTo($feedId), - $this->equalTo($this->user->getUID()), - $this->equalTo(['title' => $feedTitle]) - ); - - $this->class->rename($feedId, $feedTitle); + $this->class->rename(3, 'test'); } @@ -347,13 +349,9 @@ class FeedApiControllerTest extends TestCase $feedId = 3; $feedTitle = 'test'; - $this->oldFeedService->expects($this->once()) - ->method('patch') - ->with( - $this->equalTo($feedId), - $this->equalTo($this->user->getUID()), - $this->equalTo(['title' => $feedTitle]) - ) + $this->feedService->expects($this->once()) + ->method('find') + ->with($this->userID, 3) ->will($this->throwException(new ServiceNotFoundException('hi'))); $result = $this->class->rename($feedId, $feedTitle); @@ -365,7 +363,7 @@ class FeedApiControllerTest extends TestCase } - public function testfromAllUsers() + public function testFromAllUsers() { $feed = new Feed(); $feed->setUrl(3); @@ -405,7 +403,7 @@ class FeedApiControllerTest extends TestCase $userId = 'hi'; $this->feedService->expects($this->once()) ->method('find') - ->will($this->throwException(new \Exception($this->msg))); + ->will($this->throwException(new Exception($this->msg))); $this->logger->expects($this->once()) ->method('debug') diff --git a/tests/Unit/Controller/FeedControllerTest.php b/tests/Unit/Controller/FeedControllerTest.php index aba8cdc1f..487ef0af1 100644 --- a/tests/Unit/Controller/FeedControllerTest.php +++ b/tests/Unit/Controller/FeedControllerTest.php @@ -14,8 +14,10 @@ namespace OCA\News\Tests\Unit\Controller; use OCA\News\Controller\FeedController; -use OCA\News\Service\FeedService; +use OCA\News\Db\Folder; +use OCA\News\Service\FeedServiceV2; use OCA\News\Service\FolderServiceV2; +use OCA\News\Service\ImportService; use OCA\News\Service\ItemService; use OCP\AppFramework\Http; @@ -45,10 +47,13 @@ class FeedControllerTest extends TestCase */ private $folderService; /** - * TODO: Remove - * @var MockObject|FeedService + * @var MockObject|FeedServiceV2 */ private $feedService; + /** + * @var MockObject|ImportService + */ + private $importService; /** * TODO: Remove * @var MockObject|ItemService @@ -86,7 +91,11 @@ class FeedControllerTest extends TestCase ->disableOriginalConstructor() ->getMock(); $this->feedService = $this - ->getMockBuilder(FeedService::class) + ->getMockBuilder(FeedServiceV2::class) + ->disableOriginalConstructor() + ->getMock(); + $this->importService = $this + ->getMockBuilder(ImportService::class) ->disableOriginalConstructor() ->getMock(); $this->folderService = $this @@ -110,6 +119,7 @@ class FeedControllerTest extends TestCase $this->folderService, $this->feedService, $this->itemService, + $this->importService, $this->settings, $this->userSession ); @@ -136,11 +146,11 @@ class FeedControllerTest extends TestCase ->will($this->returnValue($result['feeds'])); $this->itemService->expects($this->once()) ->method('getNewestItemId') - ->with($this->equalTo($this->uid)) + ->with($this->uid) ->will($this->throwException(new ServiceNotFoundException(''))); $this->itemService->expects($this->once()) ->method('starredCount') - ->with($this->equalTo($this->uid)) + ->with($this->uid) ->will($this->returnValue($result['starred'])); $response = $this->class->index(); @@ -160,15 +170,15 @@ class FeedControllerTest extends TestCase ]; $this->feedService->expects($this->once()) ->method('findAllForUser') - ->with($this->equalTo($this->uid)) + ->with($this->uid) ->will($this->returnValue($result['feeds'])); $this->itemService->expects($this->once()) ->method('getNewestItemId') - ->with($this->equalTo($this->uid)) + ->with($this->uid) ->will($this->returnValue($result['newestItemId'])); $this->itemService->expects($this->once()) ->method('starredCount') - ->with($this->equalTo($this->uid)) + ->with($this->uid) ->will($this->returnValue($result['starred'])); $response = $this->class->index(); @@ -229,6 +239,32 @@ class FeedControllerTest extends TestCase } + public function testActiveFolder() + { + $type = FeedType::FOLDER; + $folder = new Folder(); + $folder->setId(3); + + $result = [ + 'activeFeed' => [ + 'id' => 3, + 'type' => 1 + ] + ]; + + $this->folderService->expects($this->once()) + ->method('find') + ->with($this->uid, 3) + ->will($this->returnValue($folder)); + + $this->activeInitMocks(3, $type); + + $response = $this->class->active(); + + $this->assertEquals($result, $response); + } + + public function testActiveFolderDoesNotExist() { $id = 3; @@ -276,15 +312,10 @@ class FeedControllerTest extends TestCase ->will($this->returnValue($result['newestItemId'])); $this->feedService->expects($this->once()) ->method('purgeDeleted') - ->with($this->equalTo($this->uid), $this->equalTo(false)); + ->with($this->uid, false); $this->feedService->expects($this->once()) ->method('create') - ->with( - $this->equalTo('hi'), - $this->equalTo(4), - $this->equalTo($this->uid), - $this->equalTo('yo') - ) + ->with($this->uid, 'hi', 4, false, 'yo') ->will($this->returnValue($result['feeds'][0])); $response = $this->class->create('hi', 4, 'yo'); @@ -293,13 +324,37 @@ class FeedControllerTest extends TestCase } + public function testCreateOldFolderId() + { + $result = [ + 'feeds' => [new Feed()], + 'newestItemId' => 3 + ]; + + $this->itemService->expects($this->once()) + ->method('getNewestItemId') + ->will($this->returnValue($result['newestItemId'])); + $this->feedService->expects($this->once()) + ->method('purgeDeleted') + ->with($this->uid, false); + $this->feedService->expects($this->once()) + ->method('create') + ->with($this->uid, 'hi', null, false, 'yo') + ->will($this->returnValue($result['feeds'][0])); + + $response = $this->class->create('hi', 0, 'yo'); + + $this->assertEquals($result, $response); + } + + public function testCreateNoItems() { $result = ['feeds' => [new Feed()]]; $this->feedService->expects($this->once()) ->method('purgeDeleted') - ->with($this->equalTo($this->uid), $this->equalTo(false)); + ->with($this->uid, false); $this->itemService->expects($this->once()) ->method('getNewestItemId') @@ -307,12 +362,7 @@ class FeedControllerTest extends TestCase $this->feedService->expects($this->once()) ->method('create') - ->with( - $this->equalTo('hi'), - $this->equalTo(4), - $this->equalTo($this->uid), - $this->equalTo('yo') - ) + ->with($this->uid, 'hi', 4, false, 'yo') ->will($this->returnValue($result['feeds'][0])); $response = $this->class->create('hi', 4, 'yo'); @@ -327,7 +377,7 @@ class FeedControllerTest extends TestCase $ex = new ServiceNotFoundException($msg); $this->feedService->expects($this->once()) ->method('purgeDeleted') - ->with($this->equalTo($this->uid), $this->equalTo(false)); + ->with($this->uid, false); $this->feedService->expects($this->once()) ->method('create') ->will($this->throwException($ex)); @@ -348,7 +398,7 @@ class FeedControllerTest extends TestCase $ex = new ServiceConflictException($msg); $this->feedService->expects($this->once()) ->method('purgeDeleted') - ->with($this->equalTo($this->uid), $this->equalTo(false)); + ->with($this->uid, false); $this->feedService->expects($this->once()) ->method('create') ->will($this->throwException($ex)); @@ -363,9 +413,17 @@ class FeedControllerTest extends TestCase public function testDelete() { + $feed = $this->getMockBuilder(Feed::class) + ->getMock(); + $feed->expects($this->once()) + ->method('setDeletedAt'); $this->feedService->expects($this->once()) - ->method('markDeleted') - ->with($this->equalTo(4)); + ->method('find') + ->with('jack', 4) + ->willReturn($feed); + $this->feedService->expects($this->once()) + ->method('update') + ->with('jack', $feed); $this->class->delete(4); } @@ -376,7 +434,7 @@ class FeedControllerTest extends TestCase $msg = 'hehe'; $this->feedService->expects($this->once()) - ->method('markDeleted') + ->method('find') ->will($this->throwException(new ServiceNotFoundException($msg))); $response = $this->class->delete(4); @@ -402,8 +460,13 @@ class FeedControllerTest extends TestCase ]; $this->feedService->expects($this->once()) - ->method('update') - ->with($this->equalTo($this->uid), $this->equalTo(4)) + ->method('find') + ->with($this->uid, 4) + ->will($this->returnValue($feed)); + + $this->feedService->expects($this->once()) + ->method('fetch') + ->with($feed) ->will($this->returnValue($feed)); $response = $this->class->update(4); @@ -415,8 +478,8 @@ class FeedControllerTest extends TestCase public function testUpdateReturnsJSONError() { $this->feedService->expects($this->once()) - ->method('update') - ->with($this->equalTo($this->uid), $this->equalTo(4)) + ->method('find') + ->with($this->uid, 4) ->will($this->throwException(new ServiceNotFoundException('NO!'))); $response = $this->class->update(4); @@ -436,17 +499,14 @@ class FeedControllerTest extends TestCase 'feeds' => [$feed] ]; - $this->feedService->expects($this->once()) + $this->importService->expects($this->once()) ->method('importArticles') - ->with( - $this->equalTo(['json']), - $this->equalTo($this->uid) - ) + ->with($this->uid, ['json'],) ->will($this->returnValue($feed)); $this->itemService->expects($this->once()) ->method('starredCount') - ->with($this->equalTo($this->uid)) + ->with($this->uid) ->will($this->returnValue(3)); $response = $this->class->import(['json']); @@ -457,17 +517,14 @@ class FeedControllerTest extends TestCase public function testImportCreatesNoAdditionalFeed() { - $this->feedService->expects($this->once()) + $this->importService->expects($this->once()) ->method('importArticles') - ->with( - $this->equalTo(['json']), - $this->equalTo($this->uid) - ) + ->with($this->uid, ['json']) ->will($this->returnValue(null)); $this->itemService->expects($this->once()) ->method('starredCount') - ->with($this->equalTo($this->uid)) + ->with($this->uid) ->will($this->returnValue(3)); $response = $this->class->import(['json']); @@ -489,7 +546,7 @@ class FeedControllerTest extends TestCase $this->itemService->expects($this->once()) ->method('readFeed') - ->with($this->equalTo(4), $this->equalTo(5), $this->uid); + ->with(4, 5, $this->uid); $response = $this->class->read(4, 5); $this->assertEquals($expected, $response); @@ -498,9 +555,21 @@ class FeedControllerTest extends TestCase public function testRestore() { + $feed = $this->getMockBuilder(Feed::class) + ->getMock(); + + $feed->expects($this->once()) + ->method('setDeletedAt') + ->with(null); + $this->feedService->expects($this->once()) - ->method('unmarkDeleted') - ->with($this->equalTo(4)); + ->method('find') + ->with($this->uid, 4) + ->willReturn($feed); + + $this->feedService->expects($this->once()) + ->method('update') + ->with($this->uid, $feed); $this->class->restore(4); } @@ -511,7 +580,8 @@ class FeedControllerTest extends TestCase $msg = 'hehe'; $this->feedService->expects($this->once()) - ->method('unmarkDeleted') + ->method('find') + ->with($this->uid, 4) ->will($this->throwException(new ServiceNotFoundException($msg))); $response = $this->class->restore(4); @@ -523,17 +593,46 @@ class FeedControllerTest extends TestCase public function testPatch() { - $expected = [ - 'pinned' => true, - 'fullTextEnabled' => true, - 'updateMode' => 1 - ]; - $this->feedService->expects($this->once()) - ->method('patch') - ->with(4, $this->uid, $expected) - ->will($this->returnValue(1)); + $feed = $this->getMockBuilder(Feed::class) + ->getMock(); - $this->class->patch(4, true, true, 1); + $feed->expects($this->once()) + ->method('setFolderId') + ->with(null); + + $feed->expects($this->once()) + ->method('setPinned') + ->with(true); + + $feed->expects($this->once()) + ->method('setFullTextEnabled') + ->with(false); + + + $feed->expects($this->once()) + ->method('setUpdateMode') + ->with(1); + + + $feed->expects($this->never()) + ->method('setOrdering') + ->with(true); + + + $feed->expects($this->never()) + ->method('setTitle') + ->with(true); + + $this->feedService->expects($this->once()) + ->method('find') + ->with($this->uid, 4) + ->will($this->returnValue($feed)); + + $this->feedService->expects($this->once()) + ->method('update') + ->with($this->uid, $feed); + + $this->class->patch(4, true, false, 1); } public function testPatchDoesNotExist() @@ -541,7 +640,8 @@ class FeedControllerTest extends TestCase $msg = 'hehe'; $this->feedService->expects($this->once()) - ->method('patch') + ->method('find') + ->with($this->uid, 4) ->will($this->throwException(new ServiceNotFoundException($msg))); $response = $this->class->patch(4, 2); @@ -551,5 +651,54 @@ class FeedControllerTest extends TestCase $this->assertEquals($response->getStatus(), Http::STATUS_NOT_FOUND); } + public function testPatchDoesNotExistOnUpdate() + { + $feed = $this->getMockBuilder(Feed::class) + ->getMock(); + + $feed->expects($this->once()) + ->method('setFolderId') + ->with(1); + + $feed->expects($this->once()) + ->method('setPinned') + ->with(true); + + $feed->expects($this->once()) + ->method('setFullTextEnabled') + ->with(false); + + + $feed->expects($this->once()) + ->method('setUpdateMode') + ->with(1); + + + $feed->expects($this->once()) + ->method('setOrdering') + ->with(1); + + + $feed->expects($this->once()) + ->method('setTitle') + ->with('title'); + + $this->feedService->expects($this->once()) + ->method('find') + ->with($this->uid, 4) + ->will($this->returnValue($feed)); + + $this->feedService->expects($this->once()) + ->method('update') + ->with($this->uid, $feed) + ->will($this->throwException(new ServiceNotFoundException('test'))); + + $response = $this->class->patch(4, 2, false, 1, 1, 1, 'title'); + $params = json_decode($response->render(), true); + + $this->assertEquals('test', $params['message']); + $this->assertEquals($response->getStatus(), Http::STATUS_NOT_FOUND); + } + } diff --git a/tests/Unit/Controller/FolderControllerTest.php b/tests/Unit/Controller/FolderControllerTest.php index 522b3a754..18f4114b6 100644 --- a/tests/Unit/Controller/FolderControllerTest.php +++ b/tests/Unit/Controller/FolderControllerTest.php @@ -14,7 +14,7 @@ namespace OCA\News\Tests\Unit\Controller; use OCA\News\Controller\FolderController; -use OCA\News\Service\FeedService; +use OCA\News\Service\FeedServiceV2; use OCA\News\Service\FolderServiceV2; use OCA\News\Service\ItemService; use \OCP\AppFramework\Http; @@ -39,6 +39,9 @@ class FolderControllerTest extends TestCase */ private $folderService; private $itemService; + /** + * @var MockObject|FeedServiceV2 + */ private $feedService; /** * @var MockObject|IUser @@ -60,7 +63,7 @@ class FolderControllerTest extends TestCase $this->folderService = $this->getMockBuilder(FolderServiceV2::class) ->disableOriginalConstructor() ->getMock(); - $this->feedService = $this->getMockBuilder(FeedService::class) + $this->feedService = $this->getMockBuilder(FeedServiceV2::class) ->disableOriginalConstructor() ->getMock(); $this->itemService = $this->getMockBuilder(ItemService::class) diff --git a/tests/Unit/Controller/ItemControllerTest.php b/tests/Unit/Controller/ItemControllerTest.php index 12f851ade..3048f62f9 100644 --- a/tests/Unit/Controller/ItemControllerTest.php +++ b/tests/Unit/Controller/ItemControllerTest.php @@ -14,7 +14,7 @@ namespace OCA\News\Tests\Unit\Controller; use OCA\News\Controller\ItemController; -use OCA\News\Service\FeedService; +use OCA\News\Service\FeedServiceV2; use OCA\News\Service\ItemService; use \OCP\AppFramework\Http; @@ -34,9 +34,21 @@ class ItemControllerTest extends TestCase { private $appName; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|IConfig + */ private $settings; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|ItemService + */ private $itemService; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|FeedServiceV2 + */ private $feedService; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|IRequest + */ private $request; /** * @var \PHPUnit\Framework\MockObject\MockObject|IUser @@ -64,7 +76,7 @@ class ItemControllerTest extends TestCase ->disableOriginalConstructor() ->getMock(); $this->feedService = - $this->getMockBuilder(FeedService::class) + $this->getMockBuilder(FeedServiceV2::class) ->disableOriginalConstructor() ->getMock(); $this->request = $this->getMockBuilder(IRequest::class) diff --git a/tests/Unit/Db/FeedMapperTest.php b/tests/Unit/Db/FeedMapperTest.php new file mode 100644 index 000000000..59e1440fb --- /dev/null +++ b/tests/Unit/Db/FeedMapperTest.php @@ -0,0 +1,451 @@ + + * @author Bernhard Posselt + * @copyright 2012 Alessandro Cosentino + * @copyright 2012-2014 Bernhard Posselt + */ + +namespace OCA\News\Tests\Unit\Db; + +use OCA\News\Db\Feed; +use OCA\News\Db\FeedMapperV2; +use OCA\News\Db\Folder; +use OCA\News\Utility\Time; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\DB\QueryBuilder\IFunctionBuilder; + +class FeedMapperTest extends MapperTestUtility +{ + /** @var FeedMapperV2 */ + private $class; + /** @var Feeds[] */ + private $feeds; + + /** + * @covers \OCA\News\Db\FeedMapperV2::__construct + */ + protected function setUp(): void + { + parent::setUp(); + + $this->class = new FeedMapperV2($this->db, new Time()); + + // create mock folders + $feed1 = new Feed(); + $feed1->setId(4); + $feed1->resetUpdatedFields(); + $feed2 = new Feed(); + $feed2->setId(5); + $feed2->resetUpdatedFields(); + + $this->feeds = [$feed1, $feed2]; + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::__construct + */ + public function testSetUpSuccess(): void + { + $this->assertEquals('news_feeds', $this->class->getTableName()); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findAllFromUser + */ + public function testFindAllFromUser() + { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $funcbuilder = $this->getMockBuilder(IFunctionBuilder::class) + ->getMock(); + + $funcbuilder->expects($this->once()) + ->method('count') + ->with('items.id', 'unreadCount') + ->will($this->returnValue('COUNT_FUNC')); + + $this->builder->expects($this->once()) + ->method('func') + ->will($this->returnValue($funcbuilder)); + + $this->builder->expects($this->once()) + ->method('select') + ->with('feeds.*', 'COUNT_FUNC') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds', 'feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('leftJoin') + ->with('feeds', 'news_items', 'items', 'items.feed_id = feeds.id AND items.unread = :unread') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('feeds.user_id = :user_id') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('andWhere') + ->with('feeds.deleted_at = 0') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('groupby') + ->with('feeds.id') + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(2)) + ->method('setParameter') + ->withConsecutive([':unread', true], [':user_id', 'jack']) + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(3)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 4], + ['id' => 5], + null + ); + + $result = $this->class->findAllFromUser('jack', []); + $this->assertEquals($this->feeds, $result); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findFromUser + */ + public function testFindFromUser() + { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $this->builder->expects($this->once()) + ->method('select') + ->with('*') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('user_id = :user_id') + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(1)) + ->method('andWhere') + ->withConsecutive(['id = :id']) + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(2)) + ->method('setParameter') + ->withConsecutive([':user_id', 'jack'], [':id', 1]) + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(2)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 4], + false + ); + + $result = $this->class->findFromUser('jack', 1); + $this->assertEquals($this->feeds[0], $result); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findFromUser + */ + public function testFindFromUserEmpty() + { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $this->builder->expects($this->once()) + ->method('select') + ->with('*') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('user_id = :user_id') + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(1)) + ->method('andWhere') + ->withConsecutive(['id = :id']) + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(2)) + ->method('setParameter') + ->withConsecutive([':user_id', 'jack'], [':id', 1]) + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(1)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + false + ); + + $this->expectException(DoesNotExistException::class); + $this->class->findFromUser('jack', 1); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findByURL + */ + public function testFindByUrl() + { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $this->builder->expects($this->once()) + ->method('select') + ->with('*') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('user_id = :user_id') + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(1)) + ->method('andWhere') + ->withConsecutive(['url = :url']) + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(2)) + ->method('setParameter') + ->withConsecutive([':user_id', 'jack'], [':url', 'https://url.com']) + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(2)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 4], + false + ); + + $result = $this->class->findByURL('jack', 'https://url.com'); + $this->assertEquals($this->feeds[0], $result); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findFromUser + */ + public function testFindFromUserDuplicate() + { + + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $this->builder->expects($this->once()) + ->method('select') + ->with('*') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('user_id = :user_id') + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(1)) + ->method('andWhere') + ->withConsecutive(['id = :id']) + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(2)) + ->method('setParameter') + ->withConsecutive([':user_id', 'jack'], [':id', 1]) + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(2)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 1], + ['id' => 2] + ); + + $this->expectException(MultipleObjectsReturnedException::class); + $this->class->findFromUser('jack', 1); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findAll + */ + public function testFindAll() + { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $this->builder->expects($this->once()) + ->method('select') + ->with('*') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('deleted_at = 0') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(3)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 4], + ['id' => 5], + null + ); + + $result = $this->class->findAll(); + $this->assertEquals($this->feeds, $result); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findAllFromFolder + */ + public function testFindAllFromFolder() + { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $this->builder->expects($this->once()) + ->method('select') + ->with('*') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('folder_id = :folder_id') + ->will($this->returnSelf()); + + $this->builder->expects($this->exactly(1)) + ->method('setParameter') + ->withConsecutive([':folder_id', 1]) + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(3)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 4], + ['id' => 5], + null + ); + + $result = $this->class->findAllFromFolder(1); + $this->assertEquals($this->feeds, $result); + } + + /** + * @covers \OCA\News\Db\FeedMapperV2::findAllFromFolder + */ + public function testFindAllFromRootFolder() + { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->builder); + + $this->builder->expects($this->once()) + ->method('select') + ->with('*') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('from') + ->with('news_feeds') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('where') + ->with('folder_id IS NULL') + ->will($this->returnSelf()); + + $this->builder->expects($this->once()) + ->method('execute') + ->will($this->returnValue($this->cursor)); + + $this->cursor->expects($this->exactly(3)) + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 4], + ['id' => 5], + null + ); + + $result = $this->class->findAllFromFolder(null); + $this->assertEquals($this->feeds, $result); + } +} \ No newline at end of file diff --git a/tests/Unit/Db/NewsMapperTest.php b/tests/Unit/Db/NewsMapperTest.php new file mode 100644 index 000000000..b6d4422c2 --- /dev/null +++ b/tests/Unit/Db/NewsMapperTest.php @@ -0,0 +1,270 @@ + + * @author Bernhard Posselt + * @copyright 2012 Alessandro Cosentino + * @copyright 2012-2014 Bernhard Posselt + */ + +namespace OCA\News\Tests\Unit\Db; + +use OCA\News\Db\Feed; +use OCA\News\Db\FeedMapperV2; +use OCA\News\Db\Folder; +use OCA\News\Db\NewsMapperV2; +use OCA\News\Utility\Time; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\DB\QueryBuilder\IFunctionBuilder; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use Test\TestCase; + +abstract class TmpNewsMapper extends NewsMapperV2 { + const TABLE_NAME = 'NAME'; +} + +class NewsMapperTest extends TestCase +{ + /** @var IDBConnection */ + private $db; + /** @var Time */ + private $time; + /** @var NewsMapperV2 */ + private $class; + + /** + * @covers \OCA\News\Db\NewsMapperV2::__construct + */ + protected function setUp(): void + { + $this->db = $this->getMockBuilder(IDBConnection::class) + ->getMock(); + $this->time = $this->getMockBuilder(Time::class) + ->getMock(); + + $this->class = $this->getMockBuilder(TmpNewsMapper::class) + ->setConstructorArgs([$this->db, $this->time, 'entity']) + ->getMockForAbstractClass(); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::__construct + */ + public function testSetUpSuccess(): void + { + $this->assertEquals('NAME', $this->class->getTableName()); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::update + */ + public function testUpdateNoChange() + { + $feed = $this->getMockBuilder(Feed::class) + ->getMock(); + + $this->time->expects($this->never()) + ->method('getMicroTime') + ->willReturn('1'); + + $feed->expects($this->never()) + ->method('setLastModified') + ->with('1'); + + $feed->expects($this->exactly(2)) + ->method('getUpdatedFields') + ->willReturn([]); + + $result = $this->class->update($feed); + $this->assertEquals($feed, $result); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::update + */ + public function testUpdateChange() + { + $this->expectException('InvalidArgumentException'); + + $feed = $this->getMockBuilder(Feed::class) + ->getMock(); + + $this->time->expects($this->once()) + ->method('getMicroTime') + ->willReturn('1'); + + $feed->expects($this->once()) + ->method('setLastModified') + ->with('1'); + + $feed->expects($this->exactly(2)) + ->method('getUpdatedFields') + ->willReturn(['a' => 'b']); + + $result = $this->class->update($feed); + $this->assertEquals($feed, $result); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::insert + */ + public function testInsert() + { + $feed = $this->getMockBuilder(Feed::class) + ->getMock(); + $qb = $this->getMockBuilder(IQueryBuilder::class) + ->getMock(); + + $this->time->expects($this->once()) + ->method('getMicroTime') + ->willReturn('1'); + + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($qb); + + $feed->expects($this->once()) + ->method('setLastModified') + ->with('1'); + + $feed->expects($this->once()) + ->method('getUpdatedFields') + ->willReturn([]); + + $result = $this->class->insert($feed); + $this->assertEquals($feed, $result); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::purgeDeleted + */ + public function testPurgeEmptyAll() + { + $qb = $this->getMockBuilder(IQueryBuilder::class) + ->getMock(); + + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($qb); + + $qb->expects($this->once()) + ->method('delete') + ->with('NAME') + ->will($this->returnSelf()); + + $qb->expects($this->once()) + ->method('andWhere') + ->with('deleted_at != 0') + ->will($this->returnSelf()); + + $qb->expects($this->once()) + ->method('execute'); + + $result = $this->class->purgeDeleted(null, null); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::purgeDeleted + */ + public function testPurgeUser() + { + $qb = $this->getMockBuilder(IQueryBuilder::class) + ->getMock(); + + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($qb); + + $qb->expects($this->once()) + ->method('delete') + ->with('NAME') + ->will($this->returnSelf()); + + $qb->expects($this->exactly(2)) + ->method('andWhere') + ->withConsecutive(['deleted_at != 0'], ['user_id = :user_id']) + ->will($this->returnSelf()); + + $qb->expects($this->once()) + ->method('setParameter') + ->with(':user_id', 'jack') + ->will($this->returnSelf()); + + $qb->expects($this->once()) + ->method('execute'); + + $result = $this->class->purgeDeleted('jack', null); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::purgeDeleted + */ + public function testPurgeTime() + { + $qb = $this->getMockBuilder(IQueryBuilder::class) + ->getMock(); + + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($qb); + + $qb->expects($this->once()) + ->method('delete') + ->with('NAME') + ->will($this->returnSelf()); + + $qb->expects($this->exactly(2)) + ->method('andWhere') + ->withConsecutive(['deleted_at != 0'], ['deleted_at < :deleted_at']) + ->will($this->returnSelf()); + + $qb->expects($this->once()) + ->method('setParameter') + ->with(':deleted_at', 1) + ->will($this->returnSelf()); + + $qb->expects($this->once()) + ->method('execute'); + + $result = $this->class->purgeDeleted(null, 1); + } + + /** + * @covers \OCA\News\Db\NewsMapperV2::purgeDeleted + */ + public function testPurgeBoth() + { + $qb = $this->getMockBuilder(IQueryBuilder::class) + ->getMock(); + + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($qb); + + $qb->expects($this->once()) + ->method('delete') + ->with('NAME') + ->will($this->returnSelf()); + + $qb->expects($this->exactly(3)) + ->method('andWhere') + ->withConsecutive(['deleted_at != 0'], ['user_id = :user_id'], ['deleted_at < :deleted_at']) + ->will($this->returnSelf()); + + $qb->expects($this->exactly(2)) + ->method('setParameter') + ->withConsecutive([':user_id', 'jack'], [':deleted_at', 1]) + ->will($this->returnSelf()); + + $qb->expects($this->once()) + ->method('execute'); + + $result = $this->class->purgeDeleted('jack', 1); + } +} \ No newline at end of file diff --git a/tests/Unit/Service/FeedServiceTest.php b/tests/Unit/Service/FeedServiceTest.php index 961d5d5e7..2d45cea70 100644 --- a/tests/Unit/Service/FeedServiceTest.php +++ b/tests/Unit/Service/FeedServiceTest.php @@ -14,20 +14,21 @@ namespace OCA\News\Tests\Unit\Service; +use FeedIo\Explorer; use FeedIo\Reader\ReadErrorException; use OC\L10N\L10N; -use OCA\News\Db\FeedMapper; -use OCA\News\Db\ItemMapper; -use OCA\News\Service\FeedService; +use OCA\News\Db\FeedMapperV2; +use OCA\News\Fetcher\FeedFetcher; +use OCA\News\Service\Exceptions\ServiceConflictException; use OCA\News\Service\Exceptions\ServiceNotFoundException; +use OCA\News\Service\FeedServiceV2; +use OCA\News\Service\ItemServiceV2; use OCA\News\Utility\Time; use OCP\AppFramework\Db\DoesNotExistException; use OCA\News\Db\Feed; use OCA\News\Db\Item; -use OCA\News\Fetcher\Fetcher; -use OCA\News\Fetcher\FetcherException; use OCP\IConfig; use OCP\IL10N; @@ -39,25 +40,25 @@ class FeedServiceTest extends TestCase { /** - * @var \PHPUnit\Framework\MockObject\MockObject|FeedMapper + * @var \PHPUnit\Framework\MockObject\MockObject|FeedMapperV2 */ - private $feedMapper; + private $mapper; /** - * @var \PHPUnit\Framework\MockObject\MockObject|ItemMapper + * @var \PHPUnit\Framework\MockObject\MockObject|ItemServiceV2 */ - private $itemMapper; + private $itemService; - /** @var FeedService */ - private $feedService; + /** @var FeedServiceV2 */ + private $class; /** * @var string */ - private $user; + private $uid; /** - * @var \PHPUnit\Framework\MockObject\MockObject|Fetcher + * @var \PHPUnit\Framework\MockObject\MockObject|FeedFetcher */ private $fetcher; @@ -86,6 +87,11 @@ class FeedServiceTest extends TestCase */ private $logger; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|Explorer + */ + private $explorer; + protected function setUp(): void { $this->logger = $this->getMockBuilder(LoggerInterface::class) @@ -102,16 +108,20 @@ class FeedServiceTest extends TestCase $this->l10n = $this->getMockBuilder(IL10N::class) ->disableOriginalConstructor() ->getMock(); - $this->feedMapper = $this - ->getMockBuilder(FeedMapper::class) + $this->mapper = $this + ->getMockBuilder(FeedMapperV2::class) ->disableOriginalConstructor() ->getMock(); $this->fetcher = $this - ->getMockBuilder(Fetcher::class) + ->getMockBuilder(FeedFetcher::class) ->disableOriginalConstructor() ->getMock(); - $this->itemMapper = $this - ->getMockBuilder(ItemMapper::class) + $this->explorer = $this + ->getMockBuilder(Explorer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->itemService = $this + ->getMockBuilder(ItemServiceV2::class) ->disableOriginalConstructor() ->getMock(); $this->purifier = $this @@ -126,26 +136,29 @@ class FeedServiceTest extends TestCase ->with('news', 'autoPurgeMinimumInterval') ->will($this->returnValue($this->autoPurgeMinimumInterval)); - $this->feedService = new FeedService( - $this->feedMapper, - $this->fetcher, $this->itemMapper, $this->logger, $this->l10n, - $timeFactory, $config, $this->purifier + $this->class = new FeedServiceV2( + $this->mapper, + $this->fetcher, + $this->itemService, + $this->explorer, + $this->purifier, + $this->logger ); - $this->user = 'jack'; + $this->uid = 'jack'; } /** - * @covers \OCA\News\Service\FeedService::findAll + * @covers \OCA\News\Service\FeedServiceV2::findAll */ public function testFindAll() { $this->response = []; - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('findAllFromUser') - ->with($this->user) + ->with($this->uid) ->will($this->returnValue([])); - $result = $this->feedService->findAllForUser($this->user); + $result = $this->class->findAllForUser($this->uid); $this->assertEquals([], $result); } @@ -154,12 +167,20 @@ class FeedServiceTest extends TestCase { $ex = new ReadErrorException('hi'); $url = 'test'; - $this->fetcher->expects($this->once()) + + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, $url) + ->willReturn(new Feed()); + + $this->fetcher->expects($this->never()) ->method('fetch') ->with($url) ->will($this->throwException($ex)); - $this->expectException(ServiceNotFoundException::class); - $this->feedService->create($url, 1, $this->user); + + $this->expectException(ServiceConflictException::class); + $this->expectExceptionMessage('Feed with this URL exists'); + $this->class->create($this->uid, $url, 1); } public function testCreate() @@ -167,7 +188,6 @@ class FeedServiceTest extends TestCase $url = 'http://test'; $folderId = 10; $createdFeed = new Feed(); - $ex = new DoesNotExistException('yo'); $createdFeed->setUrl($url); $createdFeed->setUrlHash('hsssi'); $createdFeed->setLink($url); @@ -185,20 +205,22 @@ class FeedServiceTest extends TestCase [$item1, $item2] ]; - $this->feedMapper->expects($this->once()) - ->method('findByUrlHash') - ->with( - $this->equalTo($createdFeed->getUrlHash()), - $this->equalTo($this->user) - ) - ->will($this->throwException($ex)); + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, $url) + ->will($this->throwException(new DoesNotExistException('no'))); + $this->explorer->expects($this->once()) + ->method('discover') + ->with($url) + ->will($this->returnValue([])); $this->fetcher->expects($this->once()) ->method('fetch') - ->with($this->equalTo($url)) + ->with($url) ->will($this->returnValue($return)); - $this->feedMapper->expects($this->once()) + + $this->mapper->expects($this->once()) ->method('insert') - ->with($this->equalTo($createdFeed)) + ->with($createdFeed) ->will( $this->returnCallback( function () use ($createdFeed) { @@ -207,31 +229,128 @@ class FeedServiceTest extends TestCase } ) ); - $this->itemMapper->expects($this->exactly(2)) - ->method('findByGuidHash') - ->withConsecutive( - [$item2->getGuidHash(), $item2->getFeedId(), $this->user], - [$item1->getGuidHash(), $item1->getFeedId(), $this->user] - ) - ->will($this->throwException($ex)); - $this->purifier->expects($this->exactly(2)) - ->method('purify') - ->withConsecutive( - [$return[1][1]->getBody()], - [$return[1][0]->getBody()] - ) - ->willReturnOnConsecutiveCalls( - $return[1][1]->getBody(), - $return[1][0]->getBody() + $feed = $this->class->create( + $this->uid, $url, $folderId, false, null, + 'user', 'pass' + ); + + $this->assertEquals($feed->getFolderId(), $folderId); + $this->assertEquals($feed->getUrl(), $url); + $this->assertEquals($feed->getArticlesPerUpdate(), 2); + $this->assertEquals($feed->getBasicAuthUser(), 'user'); + $this->assertEquals($feed->getBasicAuthPassword(), 'pass'); + } + + public function testCreateSetsTitle() + { + $url = 'http://test'; + $folderId = 10; + $createdFeed = new Feed(); + $createdFeed->setUrl($url); + $createdFeed->setUrlHash('hsssi'); + $createdFeed->setLink($url); + $createdFeed->setTitle('hehoy'); + $createdFeed->setBasicAuthUser('user'); + $createdFeed->setBasicAuthPassword('pass'); + $item1 = new Item(); + $item1->setFeedId(4); + $item1->setGuidHash('hi'); + $item2 = new Item(); + $item2->setFeedId(4); + $item2->setGuidHash('yo'); + $return = [ + $createdFeed, + [$item1, $item2] + ]; + + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, $url) + ->will($this->throwException(new DoesNotExistException('no'))); + $this->explorer->expects($this->once()) + ->method('discover') + ->with($url) + ->will($this->returnValue([])); + $this->fetcher->expects($this->once()) + ->method('fetch') + ->with($url) + ->will($this->returnValue($return)); + + $this->mapper->expects($this->once()) + ->method('insert') + ->with($createdFeed) + ->will( + $this->returnCallback( + function () use ($createdFeed) { + $createdFeed->setId(4); + return $createdFeed; + } + ) ); - $this->itemMapper->expects($this->exactly(2)) - ->method('insert') - ->withConsecutive([$return[1][1]], [$return[1][0]]); + $feed = $this->class->create( + $this->uid, $url, $folderId, false, 'title', + 'user', 'pass' + ); - $feed = $this->feedService->create( - $url, $folderId, $this->user, null, + $this->assertEquals($feed->getFolderId(), $folderId); + $this->assertEquals($feed->getUrl(), $url); + $this->assertEquals($feed->getArticlesPerUpdate(), 2); + $this->assertEquals($feed->getTitle(), 'title'); + $this->assertEquals($feed->getBasicAuthUser(), 'user'); + $this->assertEquals($feed->getBasicAuthPassword(), 'pass'); + } + + public function testCreateDiscovers() + { + $url = 'http://test'; + $folderId = 10; + $createdFeed = new Feed(); + $createdFeed->setUrl($url); + $createdFeed->setUrlHash('hsssi'); + $createdFeed->setLink($url); + $createdFeed->setTitle('hehoy'); + $createdFeed->setBasicAuthUser('user'); + $createdFeed->setBasicAuthPassword('pass'); + $item1 = new Item(); + $item1->setFeedId(4); + $item1->setGuidHash('hi'); + $item2 = new Item(); + $item2->setFeedId(4); + $item2->setGuidHash('yo'); + $return = [ + $createdFeed, + [$item1, $item2] + ]; + + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, $url) + ->will($this->throwException(new DoesNotExistException('no'))); + $this->explorer->expects($this->once()) + ->method('discover') + ->with($url) + ->will($this->returnValue(['http://discover.test'])); + $this->fetcher->expects($this->once()) + ->method('fetch') + ->with('http://discover.test') + ->will($this->returnValue($return)); + + $this->mapper->expects($this->once()) + ->method('insert') + ->with($createdFeed) + ->will( + $this->returnCallback( + function () use ($createdFeed) { + $createdFeed->setId(4); + return $createdFeed; + } + ) + ); + + $feed = $this->class->create( + $this->uid, $url, $folderId, false, null, 'user', 'pass' ); @@ -263,18 +382,15 @@ class FeedServiceTest extends TestCase [$item1, $item2] ]; - $this->feedMapper->expects($this->once()) - ->method('findByUrlHash') - ->with( - $this->equalTo($createdFeed->getUrlHash()), - $this->equalTo($this->user) - ) + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, $url) ->will($this->throwException($ex)); $this->fetcher->expects($this->once()) ->method('fetch') ->with($this->equalTo($url)) ->will($this->returnValue($return)); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('insert') ->with($this->equalTo($createdFeed)) ->will( @@ -285,26 +401,32 @@ class FeedServiceTest extends TestCase } ) ); - $this->itemMapper->expects($this->exactly(2)) - ->method('findByGuidHash') - ->withConsecutive( - [$item2->getGuidHash(), $item2->getFeedId(), $this->user], - [$item1->getGuidHash(), $item1->getFeedId(), $this->user] - ) - ->willReturnOnConsecutiveCalls($this->throwException($ex), null); - $this->purifier->expects($this->exactly(1)) - ->method('purify') - ->withConsecutive([$return[1][1]->getBody()]) - ->willReturnOnConsecutiveCalls($return[1][1]->getBody()); - $this->itemMapper->expects($this->exactly(1)) - ->method('insert') - ->withConsecutive([$return[1][1]]); - $feed = $this->feedService->create($url, $folderId, $this->user); + $feed = $this->class->create($this->uid, $url, $folderId); $this->assertEquals($feed->getFolderId(), $folderId); $this->assertEquals($feed->getUrl(), $url); - $this->assertEquals(1, $feed->getUnreadCount()); + } + + public function testCreateUnableToFetchFeed() + { + $url = 'http://test'; + $folderId = 10; + + $this->fetcher->expects($this->once()) + ->method('fetch') + ->with($url) + ->willReturn([null, []]); + + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, $url) + ->will($this->throwException(new DoesNotExistException('no'))); + + $this->expectException(ServiceNotFoundException::class); + $this->expectExceptionMessage('Failed to fetch feed'); + + $this->class->create($this->uid, $url, $folderId); } public function testCreateUnableToParseFeed() @@ -314,687 +436,216 @@ class FeedServiceTest extends TestCase $this->fetcher->expects($this->once()) ->method('fetch') - ->with($this->equalTo($url)) - ->willReturn([null, []]); + ->with($url) + ->will($this->throwException(new ReadErrorException('ERROR'))); - $this->l10n->expects($this->once()) - ->method('t') - ->with($this->equalTo('Can not add feed: Unable to parse feed')) - ->willReturn('Can not add feed: Unable to parse feed'); + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, $url) + ->will($this->throwException(new DoesNotExistException('no'))); $this->expectException(ServiceNotFoundException::class); - $this->expectExceptionMessage('Can not add feed: Unable to parse feed'); + $this->expectExceptionMessage('ERROR'); - $this->feedService->create($url, $folderId, $this->user); + $this->class->create($this->uid, $url, $folderId); } - public function testUpdateCreatesNewEntry() + public function testFetchReturnsOnBlock() { - $feed = new Feed(); - $feed->setId(3); - $feed->setArticlesPerUpdate(1); - $feed->setLink('http://test'); - $feed->setUrl('http://test'); - $feed->setUrlHash('yo'); - $feed->setHttpLastModified(3); - $feed->setHttpEtag(4); + $feed = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->getMock(); - $item = new Item(); - $item->setGuidHash(md5('hi')); - $item->setFeedId(3); - $items = [$item]; + $feed->expects($this->once()) + ->method('getPreventUpdate') + ->will($this->returnValue(TRUE)); - $ex = new DoesNotExistException('hi'); + $this->assertSame($feed, $this->class->fetch($feed)); + } - $fetchReturn = [$feed, $items]; + public function testFetchAllReturnsOnAllBlock() + { + $feed = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->mapper->expects($this->once()) + ->method('findAll') + ->will($this->returnValue([$feed, $feed])); + + $feed->expects($this->exactly(2)) + ->method('getPreventUpdate') + ->will($this->returnValue(TRUE)); + + $this->class->fetchAll(); + } + + public function testFetchReturnsOnReadError() + { + $feed = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->getMock(); + + $feed->expects($this->once()) + ->method('getPreventUpdate') + ->will($this->returnValue(FALSE)); + + $feed->expects($this->once()) + ->method('getLocation') + ->will($this->returnValue('location')); - $this->feedMapper->expects($this->exactly(2)) - ->method('findFromUser') - ->with($this->user, $feed->getId()) - ->will($this->returnValue($feed)); $this->fetcher->expects($this->once()) - ->method('fetch') - ->with( - $this->equalTo('http://test'), - $this->equalTo(false), - $this->equalTo(3), - $this->equalTo(''), - $this->equalTo('') - ) - ->will($this->returnValue($fetchReturn)); - $this->feedMapper->expects($this->exactly(1)) + ->method('fetch') + ->will($this->throwException(new ReadErrorException('FAIL'))); + + $feed->expects($this->once()) + ->method('getUpdateErrorCount') + ->will($this->returnValue(1)); + + $feed->expects($this->once()) + ->method('setUpdateErrorCount') + ->with(2); + + $feed->expects($this->once()) + ->method('setLastUpdateError') + ->with('FAIL'); + + $this->mapper->expects($this->once()) ->method('update') - ->with($this->equalTo($feed)); - $this->itemMapper->expects($this->once()) - ->method('findByGuidHash') - ->with( - $this->equalTo($items[0]->getGuidHash()), - $this->equalTo($items[0]->getFeedId()), - $this->equalTo($this->user) - ) - ->will($this->throwException($ex)); - $this->purifier->expects($this->exactly(1)) - ->method('purify') - ->with($this->equalTo($items[0]->getBody())) - ->will($this->returnValue($items[0]->getBody())); - $this->itemMapper->expects($this->once()) - ->method('insert') - ->with($this->equalTo($items[0])); + ->with($feed); - - $return = $this->feedService->update($this->user, $feed->getId()); - - $this->assertEquals($return, $feed); + $this->class->fetch($feed); } - public function testForceUpdateUpdatesEntry() + public function testFetchReturnsNoUpdate() { - $feed = new Feed(); - $feed->setId(3); - $feed->setArticlesPerUpdate(1); - $feed->setLink('http://test'); - $feed->setUrl('http://test'); - $feed->setUrlHash('yo'); - $feed->setHttpLastModified(3); - $feed->setHttpEtag(4); + $feed = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->getMock(); - $item = new Item(); - $item->setGuidHash(md5('hi')); - $item->setFeedId(3); - $items = [$item]; + $feed->expects($this->once()) + ->method('getPreventUpdate') + ->will($this->returnValue(FALSE)); - $ex = new DoesNotExistException('hi'); + $feed->expects($this->once()) + ->method('getLocation') + ->will($this->returnValue('location')); - $fetchReturn = [$feed, $items]; - - $this->feedMapper->expects($this->exactly(2)) - ->method('findFromUser') - ->with($this->user, $feed->getId()) - ->will($this->returnValue($feed)); $this->fetcher->expects($this->once()) - ->method('fetch') - ->with( - $this->equalTo('http://test'), - $this->equalTo(false), - $this->equalTo(3), - $this->equalTo(''), - $this->equalTo('') - ) - ->will($this->returnValue($fetchReturn)); - $this->feedMapper->expects($this->exactly(1)) - ->method('update') - ->with($this->equalTo($feed)); - $this->itemMapper->expects($this->once()) - ->method('findByGuidHash') - ->with( - $this->equalTo($items[0]->getGuidHash()), - $this->equalTo($items[0]->getFeedId()), - $this->equalTo($this->user) - ) - ->will($this->returnValue($items[0])); - $this->purifier->expects($this->exactly(1)) - ->method('purify') - ->with($this->equalTo($items[0]->getBody())) - ->will($this->returnValue($items[0]->getBody())); - $this->itemMapper->expects($this->once()) - ->method('update') - ->with($this->equalTo($items[0])); + ->method('fetch') + ->will($this->returnValue([null, []])); - - $return = $this->feedService->update($this->user, $feed->getId(), true); - - $this->assertEquals($return, $feed); - } - - private function createUpdateFeed() - { - $feed = new Feed(); - $feed->setId(3); - $feed->setArticlesPerUpdate(1); - $feed->setLink('http://test'); - $feed->setUrl('http://test'); - $feed->setUrlHash('yo'); - $feed->setHttpLastModified(3); - $feed->setHttpEtag(4); - return $feed; - } - - private function createUpdateItem() - { - $item = new Item(); - $item->setGuidHash(md5('hi')); - $item->setFeedId(3); - $item->setPubDate(2); - $item->setUpdatedDate(2); - $item->setTitle('hey'); - $item->setAuthor('aut'); - $item->setBody('new'); - $item->setUnread(false); - return $item; - } - - private function createUpdateItem2() - { - $item = new Item(); - $item->setGuidHash(md5('hi')); - $item->setFeedId(3); - $item->setPubDate(1); - $item->setUpdatedDate(1); - $item->setTitle('ho'); - $item->setAuthor('auto'); - $item->setBody('old'); - $item->setUnread(false); - return $item; - } - - public function testUpdateUpdatesWhenUpdatedDateIsNewer() - { - $feed = $this->createUpdateFeed(); - $item = $this->createUpdateItem(); - $item2 = $this->createUpdateItem2(); - - $items = [$item]; - - $fetchReturn = [$feed, $items]; - - $this->feedMapper->expects($this->exactly(2)) - ->method('findFromUser') - ->will($this->returnValue($feed)); - $this->fetcher->expects($this->once()) - ->method('fetch') - ->will($this->returnValue($fetchReturn)); - $this->feedMapper->expects($this->exactly(1)) + $this->mapper->expects($this->never()) ->method('update'); - $this->itemMapper->expects($this->once()) - ->method('findByGuidHash') - ->will($this->returnValue($item2)); - $this->purifier->expects($this->exactly(1)) + + $this->assertSame($feed, $this->class->fetch($feed)); + } + + public function testFetchSucceedsEmptyItems() + { + $feed = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->getMock(); + + $feed->expects($this->once()) + ->method('getPreventUpdate') + ->will($this->returnValue(FALSE)); + + $feed->expects($this->once()) + ->method('getLocation') + ->will($this->returnValue('location')); + + $feed->expects($this->once()) + ->method('setUnreadCount') + ->with(0) + ->will($this->returnSelf()); + + $new_feed = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fetcher->expects($this->once()) + ->method('fetch') + ->will($this->returnValue([$new_feed, []])); + + $this->mapper->expects($this->exactly(1)) + ->method('update') + ->with($feed) + ->will($this->returnValue($feed)); + + $this->assertEquals($feed, $this->class->fetch($feed)); + } + + public function testFetchSucceedsFullItems() + { + $feed = Feed::fromParams([ + 'id' => 1, + 'location' => 'url.com', + 'updateMode' => 1, + ]); + + $new_feed = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->getMock(); + + $item1 = Item::fromParams(['id' => 1, 'body' => '1']); + $item2 = Item::fromParams(['id' => 2, 'body' => '2']); + $this->fetcher->expects($this->once()) + ->method('fetch') + ->will($this->returnValue([$new_feed, [$item1, $item2]])); + + $this->mapper->expects($this->exactly(1)) + ->method('update') + ->with($feed) + ->will($this->returnValue($feed)); + + $this->purifier->expects($this->exactly(2)) ->method('purify') - ->will($this->returnValue($items[0]->getBody())); - $this->itemMapper->expects($this->once()) - ->method('update') - ->with($this->equalTo($item2)); + ->withConsecutive(['2', null], ['1', null]) + ->will($this->returnArgument(0)); - - $return = $this->feedService->update($this->user, $feed->getId()); - - $this->assertEquals($return, $feed); - } - - - public function testUpdateSetsUnreadIfModeIsOne() - { - $feed = $this->createUpdateFeed(); - $feed->setUpdateMode(1); - $item = $this->createUpdateItem(); - $item2 = $this->createUpdateItem2(); - $item3 = $this->createUpdateItem(); - $item3->setUnread(true); - - $items = [$item]; - - $fetchReturn = [$feed, $items]; - - $this->feedMapper->expects($this->exactly(2)) - ->method('findFromUser') - ->will($this->returnValue($feed)); - $this->fetcher->expects($this->once()) - ->method('fetch') - ->will($this->returnValue($fetchReturn)); - $this->feedMapper->expects($this->exactly(1)) - ->method('update'); - $this->itemMapper->expects($this->once()) - ->method('findByGuidHash') - ->will($this->returnValue($item2)); - $this->purifier->expects($this->exactly(1)) - ->method('purify') - ->will($this->returnValue($items[0]->getBody())); - $this->itemMapper->expects($this->once()) - ->method('update') - ->with($this->equalTo($item3)); - - $return = $this->feedService->update($this->user, $feed->getId()); - - $this->assertEquals($return, $feed); - - } - - public function testUpdateUpdatesArticlesPerFeedCount() - { - $feed = new Feed(); - $feed->setId(3); - $feed->setUrl('http://example.com'); - $feed->setUrlHash('yo'); - - $existingFeed = new Feed(); - $existingFeed->setId(3); - $existingFeed->setUrl('http://example.com'); - $feed->setArticlesPerUpdate(2); - - $item = new Item(); - $item->setGuidHash(md5('hi')); - $item->setFeedId(3); - $items = [$item]; - - $this->feedMapper->expects($this->any()) - ->method('findFromUser') - ->will($this->returnValue($existingFeed)); - - $this->fetcher->expects($this->once()) - ->method('fetch') - ->will($this->returnValue([$feed, $items])); - - $this->feedMapper->expects($this->once()) - ->method('update') - ->with($this->equalTo($existingFeed)); - - $this->itemMapper->expects($this->any()) - ->method('findByGuidHash') - ->will($this->returnValue($item)); - - $this->feedService->update($this->user, $feed->getId()); - } - - public function testUpdateFails() - { - $feed = new Feed(); - $feed->setId(3); - $feed->setUrl('http://example.com'); - $feed->setUpdateErrorCount(0); - $feed->setLastUpdateError(''); - - $expectedFeed = new Feed(); - $expectedFeed->setId(3); - $expectedFeed->setUrl('http://example.com'); - $expectedFeed->setUpdateErrorCount(1); - $expectedFeed->setLastUpdateError('hi'); - - $ex = new ReadErrorException('hi'); - - $this->feedMapper->expects($this->exactly(2)) - ->method('findFromUser') - ->with($this->user, $feed->getId()) - ->willReturnOnConsecutiveCalls($feed, $expectedFeed); - $this->fetcher->expects($this->once()) - ->method('fetch') - ->will($this->throwException($ex)); - $this->logger->expects($this->any()) - ->method('debug'); - - $this->feedMapper->expects($this->exactly(1)) - ->method('update') - ->with($expectedFeed) - ->will($this->returnValue($expectedFeed)); - - $return = $this->feedService->update($this->user, $feed->getId()); - - $this->assertEquals($return, $expectedFeed); - } - - - public function testUpdateDoesNotFindEntry() - { - $feed = new Feed(); - $feed->setId(3); - - $ex = new DoesNotExistException(''); - - $this->feedMapper->expects($this->exactly(1)) - ->method('findFromUser') - ->with($this->user, $feed->getId()) - ->will($this->throwException($ex)); - - $this->expectException(ServiceNotFoundException::class); - $this->feedService->update($this->user, $feed->getId()); - } - - - public function testUpdatePassesFullText() - { - $feed = new Feed(); - $feed->setId(3); - $feed->setUrl('https://goo.com'); - $feed->setHttpLastModified(123); - $feed->setFullTextEnabled(true); - - $ex = new DoesNotExistException(''); - - $this->feedMapper->expects($this->exactly(1)) - ->method('findFromUser') - ->with($this->user, $feed->getId()) + $this->itemService->expects($this->exactly(2)) + ->method('insertOrUpdate') + ->withConsecutive([$item2], [$item1]) ->will($this->returnValue($feed)); - $this->fetcher->expects($this->once()) - ->method('fetch') - ->with( - $this->equalTo($feed->getUrl()), - $this->equalTo(false), - $this->equalTo($feed->getHttpLastModified()), - $this->equalTo($feed->getFullTextEnabled()) - ) - ->will($this->throwException($ex)); - - $this->expectException(DoesNotExistException::class); - $this->feedService->update($this->user, $feed->getId()); + $this->assertSame($feed, $this->class->fetch($feed)); + $this->assertEquals(2, $feed->getUnreadCount()); } - - public function testUpdateDoesNotFindUpdatedEntry() - { - $feed = new Feed(); - $feed->setId(3); - $feed->setArticlesPerUpdate(1); - $feed->setUrl('http://example.com'); - - $item = new Item(); - $item->setGuidHash(md5('hi')); - $item->setPubDate(3333); - $item->setId(4); - $items = [$item]; - - $item2 = new Item(); - $item2->setPubDate(111); - - $fetchReturn = [$feed, $items]; - $ex = new DoesNotExistException(''); - - $this->feedMapper->expects($this->exactly(2)) - ->method('findFromUser') - ->with($this->user, $feed->getId()) - ->willReturnOnConsecutiveCalls($feed, $this->throwException($ex)); - $this->feedMapper->expects($this->exactly(1)) - ->method('update') - ->with($this->equalTo($feed)); - $this->fetcher->expects($this->once()) - ->method('fetch') - ->will($this->returnValue($fetchReturn)); - $this->itemMapper->expects($this->once()) - ->method('findByGuidHash') - ->with( - $this->equalTo($item->getGuidHash()), - $this->equalTo($feed->getId()), - $this->equalTo($this->user) - ) - ->will($this->returnValue($item2)); - - $this->expectException(ServiceNotFoundException::class); - $this->feedService->update($this->user, $feed->getId()); - } - - - public function testUpdateDoesntUpdateIfFeedIsPrevented() - { - $feedId = 3; - $feed = new Feed(); - $feed->setFolderId(16); - $feed->setId($feedId); - $feed->setPreventUpdate(true); - - $this->feedMapper->expects($this->once()) - ->method('findFromUser') - ->with($this->user, $feed->getId()) - ->will($this->returnValue($feed)); - $this->fetcher->expects($this->never()) - ->method('fetch'); - - $this->feedService->update($this->user, $feedId); - } - - - public function testUpdateDoesntUpdateIfNoFeed() - { - $feedId = 3; - $feed = new Feed(); - $feed->setFolderId(16); - $feed->setId($feedId); - $feed->setUrl('http://example.com'); - - $this->feedMapper->expects($this->once()) - ->method('findFromUser') - ->with($this->user, $feed->getId()) - ->will($this->returnValue($feed)); - $this->fetcher->expects($this->once()) - ->method('fetch') - ->will($this->returnValue([null, null])); - - $return = $this->feedService->update($this->user, $feedId); - $this->assertEquals($feed, $return); - } - - - public function testMove() - { - $feedId = 3; - $folderId = 4; - $feed = new Feed(); - $feed->setFolderId(16); - $feed->setId($feedId); - - $this->feedMapper->expects($this->once()) - ->method('findFromUser') - ->with($this->user, $feedId) - ->will($this->returnValue($feed)); - - $this->feedMapper->expects($this->once()) - ->method('update') - ->with($this->equalTo($feed)); - - $this->feedService->patch( - $feedId, $this->user, ['folderId' => $folderId] - ); - - $this->assertEquals($folderId, $feed->getFolderId()); - } - - - public function testRenameFeed() - { - $feedId = 3; - $feedTitle = "New Feed Title"; - $feed = new Feed(); - $feed->setTitle("Feed Title"); - $feed->setId($feedId); - - $this->feedMapper->expects($this->once()) - ->method('findFromUser') - ->with($this->equalTo($this->user), $this->equalTo($feedId)) - ->will($this->returnValue($feed)); - - $this->feedMapper->expects($this->once()) - ->method('update') - ->with($this->equalTo($feed)); - - $this->feedService->patch( - $feedId, $this->user, ['title' => $feedTitle] - ); - - $this->assertEquals($feedTitle, $feed->getTitle()); - } - - - public function testImportArticles() - { - $url = 'http://nextcloud/nofeed'; - - $feed = new Feed(); - $feed->setId(3); - $feed->setUserId($this->user); - $feed->setUrl($url); - $feed->setLink($url); - $feed->setTitle('Articles without feed'); - $feed->setAdded($this->time); - $feed->setFolderId(0); - $feed->setPreventUpdate(true); - - $feeds = [$feed]; - - $item = new Item(); - $item->setFeedId(3); - $item->setAuthor('john'); - $item->setGuid('s'); - $item->setGuidHash('03c7c0ace395d80182db07ae2c30f034'); - $item->setTitle('hey'); - $item->setPubDate(333); - $item->setBody('come over'); - $item->setEnclosureMime('mime'); - $item->setEnclosureLink('lin'); - $item->setUnread(true); - $item->setStarred(false); - $item->generateSearchIndex(); - - $json = $item->toExport(['feed3' => $feed]); - - $items = [$json]; - - $this->feedMapper->expects($this->once()) - ->method('findAllFromUser') - ->with($this->equalTo($this->user)) - ->will($this->returnValue($feeds)); - - $this->itemMapper->expects($this->once()) - ->method('findByGuidHash') - ->will($this->throwException(new DoesNotExistException('yo'))); - $this->itemMapper->expects($this->once()) - ->method('insert') - ->with($this->equalTo($item)); - - $this->purifier->expects($this->once()) - ->method('purify') - ->with($this->equalTo($item->getBody())) - ->will($this->returnValue($item->getBody())); - - $result = $this->feedService->importArticles($items, $this->user); - - $this->assertEquals(null, $result); - } - - - public function testImportArticlesCreatesOwnFeedWhenNotFound() - { - $url = 'http://nextcloud/args'; - - $feed = new Feed(); - $feed->setId(3); - $feed->setUserId($this->user); - $feed->setUrl($url); - $feed->setLink($url); - $feed->setTitle('Articles without feed'); - $feed->setAdded($this->time); - $feed->setFolderId(0); - $feed->setPreventUpdate(true); - - $feeds = [$feed]; - - $item = new Item(); - $item->setFeedId(3); - $item->setAuthor('john'); - $item->setGuid('s'); - $item->setGuidHash('03c7c0ace395d80182db07ae2c30f034'); - $item->setTitle('hey'); - $item->setPubDate(333); - $item->setBody('come over'); - $item->setEnclosureMime('mime'); - $item->setEnclosureLink('lin'); - $item->setUnread(true); - $item->setStarred(false); - $item->generateSearchIndex(); - - $json = $item->toExport(['feed3' => $feed]); - $json2 = $json; - // believe it or not this copies stuff :D - $json2['feedLink'] = 'http://test.com'; - - $items = [$json, $json2]; - - $insertFeed = new Feed(); - $insertFeed->setLink('http://nextcloud/nofeed'); - $insertFeed->setUrl('http://nextcloud/nofeed'); - $insertFeed->setUserId($this->user); - $insertFeed->setTitle('Articles without feed'); - $insertFeed->setAdded($this->time); - $insertFeed->setPreventUpdate(true); - $insertFeed->setFolderId(null); - - $this->l10n->expects($this->once()) - ->method('t') - ->will($this->returnValue('Articles without feed')); - $this->feedMapper->expects($this->once()) - ->method('findAllFromUser') - ->with($this->equalTo($this->user)) - ->will($this->returnValue($feeds)); - $this->feedMapper->expects($this->once()) - ->method('insert') - ->with($this->equalTo($insertFeed)) - ->will( - $this->returnCallback( - function () use ($insertFeed) { - $insertFeed->setId(3); - return $insertFeed; - } - ) - ); - - - $this->itemMapper->expects($this->exactly(2)) - ->method('findByGuidHash') - ->withConsecutive(['03c7c0ace395d80182db07ae2c30f034', 3, $this->user], ['03c7c0ace395d80182db07ae2c30f034', 3, $this->user]) - ->willReturnOnConsecutiveCalls($this->throwException(new DoesNotExistException('yo')), $item); - $this->purifier->expects($this->once()) - ->method('purify') - ->with($this->equalTo($item->getBody())) - ->will($this->returnValue($item->getBody())); - $this->itemMapper->expects($this->exactly(1)) - ->method('insert') - ->with($this->equalTo($item)); - $this->itemMapper->expects($this->exactly(1)) - ->method('update') - ->with($this->equalTo($item)); - - $this->feedMapper->expects($this->once()) - ->method('findByUrlHash') - ->will($this->returnValue($feed)); - - $result = $this->feedService->importArticles($items, $this->user); - - $this->assertEquals($feed, $result); - } - - public function testMarkDeleted() { - $id = 3; - $feed = new Feed(); - $feed2 = new Feed(); + $feed = Feed::fromParams(['id' => 3]); + $feed2 = Feed::fromParams(['id' => 3]); $feed2->setDeletedAt($this->time); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('findFromUser') - ->with($this->equalTo($this->user), $this->equalTo($id)) + ->with($this->uid, 3) ->will($this->returnValue($feed)); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('update') - ->with($this->equalTo($feed2)); + ->with($feed); - $this->feedService->markDeleted($id, $this->user); + $this->class->update($this->uid, $feed); } public function testUnmarkDeleted() { - $id = 3; - $feed = new Feed(); - $feed2 = new Feed(); + $feed = Feed::fromParams(['id' => 3]); + $feed2 = Feed::fromParams(['id' => 3]); $feed2->setDeletedAt(0); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('findFromUser') - ->with($this->equalTo($this->user), $this->equalTo($id)) + ->with($this->uid, 3) ->will($this->returnValue($feed)); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('update') - ->with($this->equalTo($feed2)); + ->with($feed2); - $this->feedService->unmarkDeleted($id, $this->user); + $this->class->update($this->uid, $feed); } @@ -1007,76 +658,50 @@ class FeedServiceTest extends TestCase $feeds = [$feed1, $feed2]; $time = $this->time - $this->autoPurgeMinimumInterval; - $this->feedMapper->expects($this->once()) - ->method('getToDelete') - ->with($this->equalTo($time), $this->equalTo($this->user)) - ->will($this->returnValue($feeds)); - $this->feedMapper->expects($this->exactly(2)) - ->method('delete') - ->withConsecutive([$feed1], [$feed2]); - $this->feedService->purgeDeleted($this->user); + $this->mapper->expects($this->exactly(1)) + ->method('purgeDeleted') + ->with($this->uid, 1); + + $this->class->purgeDeleted($this->uid, 1); } public function testPurgeDeletedWithoutInterval() { - $feed1 = new Feed(); - $feed1->setId(3); - $feed2 = new Feed(); - $feed2->setId(5); - $feeds = [$feed1, $feed2]; + $this->mapper->expects($this->exactly(1)) + ->method('purgeDeleted') + ->with($this->uid, false); - $this->feedMapper->expects($this->once()) - ->method('getToDelete') - ->with($this->equalTo(null), $this->equalTo($this->user)) - ->will($this->returnValue($feeds)); - $this->feedMapper->expects($this->exactly(2)) - ->method('delete') - ->withConsecutive([$feed1], [$feed2]); - - $this->feedService->purgeDeleted($this->user, false); + $this->class->purgeDeleted($this->uid, false); } public function testfindAllFromAllUsers() { $expected = ['hi']; - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('findAll') ->will($this->returnValue($expected)); - $result = $this->feedService->findAllFromAllUsers(); + $result = $this->class->findAll(); $this->assertEquals($expected, $result); } - public function testDeleteUser() - { - $this->feedMapper->expects($this->once()) - ->method('deleteUser') - ->will($this->returnValue($this->user)); - - $this->feedService->deleteUser($this->user); - } - - public function testOrdering() { $feed = Feed::fromRow(['id' => 3]); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('findFromUser') - ->with( - $this->equalTo($this->user), - $this->equalTo($feed->getId()) - ) + ->with($this->uid, $feed->getId()) ->will($this->returnValue($feed)); $feed->setOrdering(2); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('update') - ->with($this->equalTo($feed)); + ->with($feed); - $this->feedService->patch(3, $this->user, ['ordering' => 2]); + $this->class->update($this->uid, $feed); } @@ -1091,53 +716,114 @@ class FeedServiceTest extends TestCase ] ); $feed2 = Feed::fromRow(['id' => 3]); - $this->feedMapper->expects($this->exactly(2)) + $this->mapper->expects($this->exactly(1)) ->method('findFromUser') - ->with( - $this->equalTo($this->user), - $this->equalTo($feed->getId()) - ) - ->willReturnOnConsecutiveCalls($this->returnValue($feed), $this->throwException(new DoesNotExistException(''))); + ->with($this->uid,$feed->getId()) + ->willReturnOnConsecutiveCalls($this->returnValue($feed)); - $feed2->setFullTextEnabled(true); - $feed2->setHttpEtag(''); - $feed2->setHttpLastModified(0); - $this->feedMapper->expects($this->exactly(1)) + $feed2->setFullTextEnabled(false); + $feed2->setHttpEtag('a'); + $feed2->setHttpLastModified('1'); + $feed2->resetUpdatedFields(); + + $this->mapper->expects($this->exactly(1)) ->method('update') - ->with($this->equalTo($feed2)); + ->with($feed2); - $this->expectException(ServiceNotFoundException::class); - - $this->feedService->patch(3, $this->user, ['fullTextEnabled' => true]); + $this->class->update($this->uid, $feed); } public function testPatchDoesNotExist() { $this->expectException('OCA\News\Service\Exceptions\ServiceNotFoundException'); $feed = Feed::fromRow(['id' => 3]); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('findFromUser') ->will($this->throwException(new DoesNotExistException(''))); - $this->feedService->patch(3, $this->user); + $this->class->update($this->uid, $feed); } public function testSetPinned() { $feed = Feed::fromRow(['id' => 3, 'pinned' => false]); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('findFromUser') - ->with($this->user, $feed->getId()) + ->with($this->uid, $feed->getId()) ->will($this->returnValue($feed)); $feed->setPinned(true); - $this->feedMapper->expects($this->once()) + $this->mapper->expects($this->once()) ->method('update') - ->with($this->equalTo($feed)); + ->with($feed); - $this->feedService->patch(3, $this->user, ['pinned' => true]); + $this->class->update($this->uid, $feed); } + public function testExistsForUser() + { + $feed = Feed::fromRow(['id' => 3, 'pinned' => false]); + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, 'url') + ->will($this->returnValue($feed)); + + $this->assertTrue($this->class->existsForUser($this->uid, 'url')); + } + + public function testDoesNotExistsForUser() + { + $this->mapper->expects($this->once()) + ->method('findByURL') + ->with($this->uid, 'url') + ->will($this->throwException(new DoesNotExistException('no!'))); + + $this->assertFalse($this->class->existsForUser($this->uid, 'url')); + } + + public function testFindAllFromUser() + { + $this->mapper->expects($this->once()) + ->method('findAllFromUser') + ->with($this->uid, []) + ->will($this->returnValue([])); + + $this->assertEquals([], $this->class->findAllForUser($this->uid, [])); + } + + public function testFindAllFromFolder() + { + $this->mapper->expects($this->once()) + ->method('findAllFromFolder') + ->with(null) + ->will($this->returnValue([])); + + $this->assertEquals([], $this->class->findAllFromFolder(null)); + } + + public function testFindAllFromUserRecursive() + { + $feed1 = new Feed(); + $feed1->setId(1); + + $feed2 = new Feed(); + $feed2->setId(2); + + $this->mapper->expects($this->once()) + ->method('findAllFromUser') + ->with($this->uid) + ->will($this->returnValue([$feed1, $feed2])); + + $this->itemService->expects($this->exactly(2)) + ->method('findAllForFeed') + ->withConsecutive([1], [2]) + ->willReturn(['a']); + + $feeds = $this->class->findAllForUserRecursive($this->uid); + $this->assertEquals(['a'], $feeds[0]->items); + $this->assertEquals(['a'], $feeds[1]->items); + } + } diff --git a/tests/Unit/Service/FolderServiceTest.php b/tests/Unit/Service/FolderServiceTest.php index d616da736..2b55ee01a 100644 --- a/tests/Unit/Service/FolderServiceTest.php +++ b/tests/Unit/Service/FolderServiceTest.php @@ -132,45 +132,6 @@ class FolderServiceTest extends TestCase $this->assertEquals($result[0]->feeds, $feeds); } - public function testFindForUser() - { - $return = new Folder(); - $this->mapper->expects($this->once()) - ->method('findFromUser') - ->with('jack', 1) - ->will($this->returnValue($return)); - - $result = $this->class->findForUser('jack', 1); - - $this->assertEquals($return, $result); - } - - public function testFindForUserEmpty() - { - $this->expectException(ServiceNotFoundException::class); - $this->expectExceptionMessage('Folder not found'); - - $this->mapper->expects($this->once()) - ->method('findFromUser') - ->with('jack', 1) - ->will($this->throwException(new DoesNotExistException(''))); - - $this->class->findForUser('jack', 1); - } - - public function testFindForUserDupe() - { - $this->expectException(ServiceConflictException::class); - $this->expectExceptionMessage('Multiple folders found'); - - $this->mapper->expects($this->once()) - ->method('findFromUser') - ->with('jack', 1) - ->will($this->throwException(new MultipleObjectsReturnedException(''))); - - $this->class->findForUser('jack', 1); - } - public function testCreate() { diff --git a/tests/Unit/Service/ImportServiceTest.php b/tests/Unit/Service/ImportServiceTest.php new file mode 100644 index 000000000..2a6a7b106 --- /dev/null +++ b/tests/Unit/Service/ImportServiceTest.php @@ -0,0 +1,232 @@ + + * @author Bernhard Posselt + * @copyright 2012 Alessandro Cosentino + * @copyright 2012-2014 Bernhard Posselt + */ + + +namespace OCA\News\Tests\Unit\Service; + +use FeedIo\Explorer; +use FeedIo\Reader\ReadErrorException; + +use OC\L10N\L10N; +use OCA\News\Db\FeedMapperV2; +use OCA\News\Fetcher\FeedFetcher; +use OCA\News\Service\Exceptions\ServiceConflictException; +use OCA\News\Service\Exceptions\ServiceNotFoundException; +use OCA\News\Service\FeedServiceV2; +use OCA\News\Service\ImportService; +use OCA\News\Service\ItemServiceV2; +use OCA\News\Utility\Time; +use OCP\AppFramework\Db\DoesNotExistException; + +use OCA\News\Db\Feed; +use OCA\News\Db\Item; +use OCP\IConfig; +use OCP\IL10N; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + + +class ImportServiceTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\MockObject|ItemServiceV2 + */ + private $itemService; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|FeedServiceV2 + */ + private $feedService; + + /** @var ImportService */ + private $class; + + /** + * @var string + */ + private $uid; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|LoggerInterface + */ + private $logger; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\HTMLPurifier + */ + private $purifier; + + protected function setUp(): void + { + $this->logger = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->itemService = $this + ->getMockBuilder(ItemServiceV2::class) + ->disableOriginalConstructor() + ->getMock(); + $this->feedService = $this + ->getMockBuilder(FeedServiceV2::class) + ->disableOriginalConstructor() + ->getMock(); + $this->purifier = $this + ->getMockBuilder(\HTMLPurifier::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->time = 333333; + + $this->class = new ImportService( + $this->feedService, + $this->itemService, + $this->purifier, + $this->logger + ); + $this->uid = 'jack'; + } + + + public function testImportArticles() + { + $url = 'http://nextcloud/nofeed'; + + $feed = new Feed(); + $feed->setId(3); + $feed->setUserId($this->uid); + $feed->setUrl($url); + $feed->setLink($url); + $feed->setTitle('Articles without feed'); + $feed->setAdded($this->time); + $feed->setFolderId(0); + $feed->setPreventUpdate(true); + + $feeds = [$feed]; + + $item = new Item(); + $item->setFeedId(3); + $item->setAuthor('john'); + $item->setGuid('s'); + $item->setGuidHash('03c7c0ace395d80182db07ae2c30f034'); + $item->setTitle('hey'); + $item->setPubDate(333); + $item->setBody('come over'); + $item->setEnclosureMime('mime'); + $item->setEnclosureLink('lin'); + $item->setUnread(true); + $item->setStarred(false); + $item->generateSearchIndex(); + + $json = $item->toExport(['feed3' => $feed]); + + $items = [$json]; + + $this->feedService->expects($this->once()) + ->method('findAllForUser') + ->with($this->equalTo($this->uid)) + ->will($this->returnValue($feeds)); + + $this->itemService->expects($this->once()) + ->method('insertOrUpdate') + ->with($item); + + $this->purifier->expects($this->once()) + ->method('purify') + ->with($this->equalTo($item->getBody())) + ->will($this->returnValue($item->getBody())); + + $result = $this->class->importArticles($this->uid, $items); + + $this->assertEquals(null, $result); + } + + + public function testImportArticlesCreatesOwnFeedWhenNotFound() + { + $url = 'http://nextcloud/args'; + + $feed = new Feed(); + $feed->setId(3); + $feed->setUserId($this->uid); + $feed->setUrl($url); + $feed->setLink($url); + $feed->setTitle('Articles without feed'); + $feed->setAdded($this->time); + $feed->setFolderId(0); + $feed->setPreventUpdate(true); + + $feeds = [$feed]; + + $item = new Item(); + $item->setFeedId(3); + $item->setAuthor('john'); + $item->setGuid('s'); + $item->setGuidHash('03c7c0ace395d80182db07ae2c30f034'); + $item->setTitle('hey'); + $item->setPubDate(333); + $item->setBody('come over'); + $item->setEnclosureMime('mime'); + $item->setEnclosureLink('lin'); + $item->setUnread(true); + $item->setStarred(false); + $item->generateSearchIndex(); + + $json = $item->toExport(['feed3' => $feed]); + $json2 = $json; + // believe it or not this copies stuff :D + $json2['feedLink'] = 'http://test.com'; + + $items = [$json, $json2]; + + $insertFeed = new Feed(); + $insertFeed->setLink('http://nextcloud/nofeed'); + $insertFeed->setUrl('http://nextcloud/nofeed'); + $insertFeed->setUserId($this->uid); + $insertFeed->setTitle('Articles without feed'); + $insertFeed->setAdded($this->time); + $insertFeed->setPreventUpdate(true); + $insertFeed->setFolderId(null); + + $this->feedService->expects($this->once()) + ->method('findAllForUser') + ->with($this->equalTo($this->uid)) + ->will($this->returnValue($feeds)); + $this->feedService->expects($this->once()) + ->method('insert') + ->will( + $this->returnCallback( + function () use ($insertFeed) { + $insertFeed->setId(3); + return $insertFeed; + } + ) + ); + + $this->itemService->expects($this->exactly(2)) + ->method('insertOrUpdate') + ->withConsecutive([$item]); + $this->purifier->expects($this->exactly(2)) + ->method('purify') + ->with($this->equalTo($item->getBody())) + ->will($this->returnValue($item->getBody())); + + $this->feedService->expects($this->once()) + ->method('findByUrl') + ->will($this->returnValue($feed)); + + $result = $this->class->importArticles($this->uid, $items); + + $this->assertEquals($feed, $result); + } + +} diff --git a/tests/Unit/Service/ItemServiceTest.php b/tests/Unit/Service/ItemServiceTest.php index 98c97b31f..89328b6ec 100644 --- a/tests/Unit/Service/ItemServiceTest.php +++ b/tests/Unit/Service/ItemServiceTest.php @@ -15,6 +15,7 @@ namespace OCA\News\Tests\Unit\Service; use OC\Log; use OCA\News\Db\ItemMapper; +use OCA\News\Db\ItemMapperV2; use OCA\News\Service\ItemService; use OCA\News\Service\Exceptions\ServiceNotFoundException; use OCA\News\Utility\PsrLogger; @@ -35,6 +36,11 @@ class ItemServiceTest extends TestCase /** * @var \PHPUnit\Framework\MockObject\MockObject|ItemMapper */ + private $oldItemMapper; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|ItemMapperV2 + */ private $mapper; /** * @var ItemService @@ -79,7 +85,10 @@ class ItemServiceTest extends TestCase $this->timeFactory->expects($this->any()) ->method('getMicroTime') ->will($this->returnValue($this->time)); - $this->mapper = $this->getMockBuilder(ItemMapper::class) + $this->mapper = $this->getMockBuilder(ItemMapperV2::class) + ->disableOriginalConstructor() + ->getMock(); + $this->oldItemMapper = $this->getMockBuilder(ItemMapper::class) ->disableOriginalConstructor() ->getMock(); $this->config = $this->getMockBuilder(IConfig::class) @@ -92,6 +101,7 @@ class ItemServiceTest extends TestCase $this->itemService = new ItemService( $this->mapper, + $this->oldItemMapper, $this->timeFactory, $this->config, $this->logger @@ -109,7 +119,7 @@ class ItemServiceTest extends TestCase public function testFindAllNewFeed() { $type = FeedType::FEED; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllNewFeed') ->with( $this->equalTo(3), @@ -127,7 +137,7 @@ class ItemServiceTest extends TestCase public function testFindAllNewFolder() { $type = FeedType::FOLDER; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllNewFolder') ->with( $this->equalTo(3), @@ -145,7 +155,7 @@ class ItemServiceTest extends TestCase public function testFindAllNew() { $type = FeedType::STARRED; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllNew') ->with( $this->equalTo(20333), @@ -166,7 +176,7 @@ class ItemServiceTest extends TestCase public function testFindAllFeed() { $type = FeedType::FEED; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllFeed') ->with( $this->equalTo(3), @@ -190,7 +200,7 @@ class ItemServiceTest extends TestCase public function testFindAllFolder() { $type = FeedType::FOLDER; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllFolder') ->with( $this->equalTo(3), @@ -214,7 +224,7 @@ class ItemServiceTest extends TestCase public function testFindAll() { $type = FeedType::STARRED; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllItems') ->with( $this->equalTo(20), @@ -240,7 +250,7 @@ class ItemServiceTest extends TestCase $type = FeedType::STARRED; $search = ['test']; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllItems') ->with( $this->equalTo(20), @@ -269,22 +279,16 @@ class ItemServiceTest extends TestCase $guidHash = md5('hihi'); $item = new Item(); - $item->setStatus(128); $item->setId($itemId); $item->setStarred(false); $expectedItem = new Item(); - $expectedItem->setStatus(128); $expectedItem->setStarred(true); $expectedItem->setId($itemId); $this->mapper->expects($this->once()) ->method('findByGuidHash') - ->with( - $this->equalTo($guidHash), - $this->equalTo($feedId), - $this->equalTo('jack') - ) + ->with($feedId, $guidHash) ->will($this->returnValue($item)); $this->mapper->expects($this->once()) @@ -316,11 +320,7 @@ class ItemServiceTest extends TestCase $this->mapper->expects($this->once()) ->method('findByGuidHash') - ->with( - $this->equalTo($guidHash), - $this->equalTo($feedId), - $this->equalTo('jack') - ) + ->with($feedId, $guidHash) ->will($this->returnValue($item)); $this->mapper->expects($this->once()) @@ -346,7 +346,7 @@ class ItemServiceTest extends TestCase $expectedItem->setId($itemId); $expectedItem->setLastModified($this->time); - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('readItem') ->with( $this->equalTo($itemId), @@ -364,7 +364,7 @@ class ItemServiceTest extends TestCase { $this->expectException(ServiceNotFoundException::class); - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('readItem') ->will($this->throwException(new DoesNotExistException(''))); @@ -387,7 +387,7 @@ class ItemServiceTest extends TestCase { $highestItemId = 6; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('readAll') ->with( $this->equalTo($highestItemId), @@ -404,7 +404,7 @@ class ItemServiceTest extends TestCase $folderId = 3; $highestItemId = 6; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('readFolder') ->with( $this->equalTo($folderId), @@ -422,7 +422,7 @@ class ItemServiceTest extends TestCase $feedId = 3; $highestItemId = 6; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('readFeed') ->with( $this->equalTo($feedId), @@ -441,7 +441,7 @@ class ItemServiceTest extends TestCase ->method('getAppValue') ->with('news', 'autoPurgeCount') ->will($this->returnValue(2)); - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('deleteReadOlderThanThreshold') ->with($this->equalTo(2)); @@ -454,7 +454,7 @@ class ItemServiceTest extends TestCase ->method('getAppValue') ->with('news', 'autoPurgeCount') ->will($this->returnValue(-1)); - $this->mapper->expects($this->never()) + $this->oldItemMapper->expects($this->never()) ->method('deleteReadOlderThanThreshold'); $this->itemService->autoPurgeOld(); @@ -463,7 +463,7 @@ class ItemServiceTest extends TestCase public function testGetNewestItemId() { - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('getNewestItemId') ->with($this->equalTo('jack')) ->will($this->returnValue(12)); @@ -475,7 +475,7 @@ class ItemServiceTest extends TestCase public function testGetNewestItemIdDoesNotExist() { - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('getNewestItemId') ->with($this->equalTo('jack')) ->will( @@ -493,7 +493,7 @@ class ItemServiceTest extends TestCase { $star = 18; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('starredCount') ->with($this->equalTo('jack')) ->will($this->returnValue($star)); @@ -508,7 +508,7 @@ class ItemServiceTest extends TestCase { $star = 18; - $this->mapper->expects($this->once()) + $this->oldItemMapper->expects($this->once()) ->method('findAllUnreadOrStarred') ->with($this->equalTo('jack')) ->will($this->returnValue($star)); @@ -519,15 +519,5 @@ class ItemServiceTest extends TestCase } - public function testDeleteUser() - { - $this->mapper->expects($this->once()) - ->method('deleteUser') - ->will($this->returnValue('jack')); - - $this->itemService->deleteUser('jack'); - } - - } diff --git a/tests/Unit/Service/OPMLServiceTest.php b/tests/Unit/Service/OPMLServiceTest.php new file mode 100644 index 000000000..6f117c066 --- /dev/null +++ b/tests/Unit/Service/OPMLServiceTest.php @@ -0,0 +1,143 @@ + + * @author Bernhard Posselt + * @copyright 2012 Alessandro Cosentino + * @copyright 2012-2014 Bernhard Posselt + */ + + +namespace OCA\News\Tests\Unit\Service; + +use FeedIo\Explorer; +use FeedIo\Reader\ReadErrorException; + +use OC\L10N\L10N; +use OCA\News\Db\FeedMapperV2; +use OCA\News\Db\Folder; +use OCA\News\Fetcher\FeedFetcher; +use OCA\News\Service\Exceptions\ServiceConflictException; +use OCA\News\Service\Exceptions\ServiceNotFoundException; +use OCA\News\Service\FeedServiceV2; +use OCA\News\Service\FolderServiceV2; +use OCA\News\Service\ImportService; +use OCA\News\Service\ItemServiceV2; +use OCA\News\Service\OpmlService; +use OCA\News\Utility\OPMLExporter; +use OCA\News\Utility\Time; +use OCP\AppFramework\Db\DoesNotExistException; + +use OCA\News\Db\Feed; +use OCA\News\Db\Item; +use OCP\IConfig; +use OCP\IL10N; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + + +class OPMLServiceTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\MockObject|FolderServiceV2 + */ + private $folderService; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|FeedServiceV2 + */ + private $feedService; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|OPMLExporter + */ + private $exporter; + + /** @var OpmlService */ + private $class; + + /** + * @var string + */ + private $uid; + + protected function setUp(): void + { + $this->exporter = $this->getMockBuilder(OPMLExporter::class) + ->disableOriginalConstructor() + ->getMock(); + $this->folderService = $this + ->getMockBuilder(FolderServiceV2::class) + ->disableOriginalConstructor() + ->getMock(); + $this->feedService = $this + ->getMockBuilder(FeedServiceV2::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->time = 333333; + + $this->class = new OpmlService( + $this->folderService, + $this->feedService, + $this->exporter + ); + $this->uid = 'jack'; + } + + public function testExportEmpty() + { + $this->feedService->expects($this->once()) + ->method('findAllForUser') + ->will($this->returnValue([])); + $this->folderService->expects($this->once()) + ->method('findAllForUser') + ->will($this->returnValue([])); + + $domdoc = $this->getMockBuilder(\DOMDocument::class) + ->getMock(); + + $this->exporter->expects($this->once()) + ->method('build') + ->with([], []) + ->will($this->returnValue($domdoc)); + + $domdoc->expects($this->once()) + ->method('saveXML') + ->will($this->returnValue('doc')); + + $this->assertEquals('doc', $this->class->export('jack')); + } + + public function testExportSuccess() + { + $feed = Feed::fromParams(['id' => 1]); + $folder = Folder::fromParams(['id' => 1]); + $this->feedService->expects($this->once()) + ->method('findAllForUser') + ->will($this->returnValue([$feed])); + $this->folderService->expects($this->once()) + ->method('findAllForUser') + ->will($this->returnValue([$folder])); + + $domdoc = $this->getMockBuilder(\DOMDocument::class) + ->getMock(); + + $this->exporter->expects($this->once()) + ->method('build') + ->with([$folder], [$feed]) + ->will($this->returnValue($domdoc)); + + $domdoc->expects($this->once()) + ->method('saveXML') + ->will($this->returnValue('doc')); + + $this->assertEquals('doc', $this->class->export('jack')); + } + +} diff --git a/tests/Unit/Service/ServiceTest.php b/tests/Unit/Service/ServiceTest.php index ce4e2e61f..cfaf82c95 100644 --- a/tests/Unit/Service/ServiceTest.php +++ b/tests/Unit/Service/ServiceTest.php @@ -15,6 +15,7 @@ namespace OCA\News\Tests\Unit\Service; use OCA\News\Db\Feed; use OCA\News\Db\ItemMapper; +use OCA\News\Db\ItemMapperV2; use OCA\News\Service\Exceptions\ServiceNotFoundException; use OCA\News\Service\Service; use \OCP\AppFramework\Db\DoesNotExistException; @@ -24,41 +25,24 @@ use \OCA\News\Db\Folder; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; - -class TestLegacyService extends Service -{ - public function __construct($mapper, $logger) - { - parent::__construct($mapper, $logger); - } - - public function findAllForUser(string $userId, array $params = []): array - { - // TODO: Implement findAllForUser() method. - } - - public function findAll(): array - { - // TODO: Implement findAll() method. - } -} - class ServiceTest extends TestCase { protected $mapper; protected $logger; - protected $newsService; + protected $class; protected function setUp(): void { - $this->mapper = $this->getMockBuilder(ItemMapper::class) + $this->mapper = $this->getMockBuilder(ItemMapperV2::class) ->disableOriginalConstructor() ->getMock(); $this->logger = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->newsService = new TestLegacyService($this->mapper, $this->logger); + $this->class = $this->getMockBuilder(Service::class) + ->setConstructorArgs([$this->mapper, $this->logger]) + ->getMockForAbstractClass(); } @@ -77,7 +61,19 @@ class ServiceTest extends TestCase ->with($this->equalTo($user), $this->equalTo($id)) ->will($this->returnValue($folder)); - $this->newsService->delete($user, $id); + $this->class->delete($user, $id); + } + + + public function testInsert() + { + $folder = new Folder(); + + $this->mapper->expects($this->once()) + ->method('insert') + ->with($this->equalTo($folder)); + + $this->class->insert($folder); } @@ -91,7 +87,7 @@ class ServiceTest extends TestCase ->with($this->equalTo($user), $this->equalTo($id)) ->will($this->returnValue(new Feed())); - $this->newsService->find($user, $id); + $this->class->find($user, $id); } @@ -104,7 +100,7 @@ class ServiceTest extends TestCase ->will($this->throwException($ex)); $this->expectException(ServiceNotFoundException::class); - $this->newsService->find('', 1); + $this->class->find('', 1); } @@ -117,7 +113,25 @@ class ServiceTest extends TestCase ->will($this->throwException($ex)); $this->expectException(ServiceNotFoundException::class); - $this->newsService->find('', 1); + $this->class->find('', 1); + } + + + public function testDeleteUser() + { + $feed1 = Feed::fromParams(['id' => 1]); + $feed2 = Feed::fromParams(['id' => 2]); + + $this->class->expects($this->once()) + ->method('findAllForUser') + ->with('') + ->willReturn([$feed1, $feed2]); + + $this->mapper->expects($this->exactly(2)) + ->method('delete') + ->withConsecutive([$feed1], [$feed2]); + + $this->class->deleteUser(''); } } diff --git a/tests/Unit/Service/StatusServiceTest.php b/tests/Unit/Service/StatusServiceTest.php index 9f87303ae..dfe3dfd55 100644 --- a/tests/Unit/Service/StatusServiceTest.php +++ b/tests/Unit/Service/StatusServiceTest.php @@ -45,12 +45,9 @@ class StatusServiceTest extends TestCase $this->connection = $this->getMockBuilder(IDBConnection::class) ->disableOriginalConstructor() ->getMock(); - $this->service = new StatusService($this->settings, $this->connection, 'news'); + $this->service = new StatusService($this->settings, $this->connection); } - /** - * @covers \OCA\News\Service\StatusService::getStatus - */ public function testGetStatus() { $this->settings->expects($this->exactly(3)) @@ -81,9 +78,6 @@ class StatusServiceTest extends TestCase $this->assertEquals($expected, $response); } - /** - * @covers \OCA\News\Service\StatusService::getStatus - */ public function testGetStatusNoCorrectCronAjax() { $this->settings->expects($this->exactly(3)) @@ -114,9 +108,6 @@ class StatusServiceTest extends TestCase $this->assertEquals($expected, $response); } - /** - * @covers \OCA\News\Service\StatusService::getStatus - */ public function testGetStatusNoCorrectCronTurnedOff() { $this->settings->expects($this->exactly(3)) @@ -147,9 +138,6 @@ class StatusServiceTest extends TestCase $this->assertEquals($expected, $response); } - /** - * @covers \OCA\News\Service\StatusService::getStatus - */ public function testGetStatusReportsNon4ByteText() { $this->settings->expects($this->exactly(3)) @@ -180,9 +168,6 @@ class StatusServiceTest extends TestCase $this->assertEquals($expected, $response); } - /** - * @covers \OCA\News\Service\StatusService::isCronProperlyConfigured - */ public function testIsProperlyConfiguredNone() { $this->settings->expects($this->exactly(2)) @@ -200,9 +185,6 @@ class StatusServiceTest extends TestCase $this->assertFalse($response); } - /** - * @covers \OCA\News\Service\StatusService::isCronProperlyConfigured - */ public function testIsProperlyConfiguredModeCronNoSystem() { $this->settings->expects($this->exactly(2)) @@ -220,9 +202,6 @@ class StatusServiceTest extends TestCase $this->assertTrue($response); } - /** - * @covers \OCA\News\Service\StatusService::isCronProperlyConfigured - */ public function testIsProperlyConfiguredModeCron() { $this->settings->expects($this->exactly(2))