diff --git a/TODO b/TODO index 885aa05c..a0e8da58 100644 --- a/TODO +++ b/TODO @@ -8,6 +8,10 @@ everything related to posts: - comment count - fix broken thumbnails if no external software is installed + - uploading post + - remove hard dependency on gd2 in PostService::setContentFromString + (getimagesizefromstring) + - single post view - post content - post tags @@ -124,17 +128,10 @@ refactors: separate PostTagDao for this) - post view proxy should retrieve full tags and full user - centralize markdown prefix decorators - - move getPostContentPath i getAvatarSourcePath to Entity, include it in + - move getPostContentPath and getAvatarSourcePath to Entity, include it in ViewProxy and make presenters use it instead of duplicating the code - after implementing Post::getPostContentPath, make paths include file extensions so that apache can guess mime types when it serves the files - - add afterSave, afterDelete in Dao layer - - refactor to file content management - - move fileService dependency to Dao layer - - move setContent i getContent to entities - - add afterSave that talks to fileService to save the content - - inside afterLoad inject method wchi talks to fileService to read the - content - add enum validation in IValidatables (needs refactors of enums and possible disposal of EnumHelper in favor of something more subtle) - (idea) keep denormalized data in separate tables, i.e. tag usages in diff --git a/src/Controllers/PostContentController.php b/src/Controllers/PostContentController.php index 1410f201..9aacabed 100644 --- a/src/Controllers/PostContentController.php +++ b/src/Controllers/PostContentController.php @@ -29,16 +29,16 @@ final class PostContentController extends AbstractController public function getPostContent($postName) { $post = $this->postService->getByName($postName); - $source = $this->postService->getPostContentPath($post); + $source = $post->getContentPath(); $this->fileService->serve($source); } public function getPostThumbnail($postName, $size) { $post = $this->postService->getByName($postName); - $source = $this->postService->getPostThumbnailSourcePath($post); + $source = $post->getThumbnailSourceContentPath(); if (!$this->fileService->exists($source)) - $source = $this->postService->getPostContentPath($post); + $source = $post->getContentPath(); $sizedSource = $this->thumbnailService->getOrGenerate($source, $size, $size); $this->fileService->serve($sizedSource); diff --git a/src/Controllers/UserAvatarController.php b/src/Controllers/UserAvatarController.php index b1d24fd9..fe750c4f 100644 --- a/src/Controllers/UserAvatarController.php +++ b/src/Controllers/UserAvatarController.php @@ -38,15 +38,15 @@ final class UserAvatarController extends AbstractController break; case \Szurubooru\Entities\User::AVATAR_STYLE_BLANK: - $this->serveFromFile($this->userService->getBlankAvatarSourcePath(), $size); + $this->serveFromFile($this->getBlankAvatarSourcePath(), $size); break; case \Szurubooru\Entities\User::AVATAR_STYLE_MANUAL: - $this->serveFromFile($this->userService->getCustomAvatarSourcePath($user), $size); + $this->serveFromFile($user->getCustomAvatarSourceContentPath(), $size); break; default: - $this->serveFromFile($this->userService->getBlankAvatarSourcePath(), $size); + $this->serveFromFile($this->getBlankAvatarSourcePath(), $size); break; } } @@ -59,9 +59,14 @@ final class UserAvatarController extends AbstractController private function serveFromFile($file, $size) { if (!$this->fileService->exists($file)) - $file = $this->userService->getBlankAvatarSourcePath(); + $file = $this->getBlankAvatarSourcePath(); $sizedFile = $this->thumbnailService->getOrGenerate($file, $size, $size); $this->fileService->serve($sizedFile); } + + private function getBlankAvatarSourcePath() + { + return 'avatars' . DIRECTORY_SEPARATOR . 'blank.png'; + } } diff --git a/src/Dao/AbstractDao.php b/src/Dao/AbstractDao.php index 6165704c..f1c151ae 100644 --- a/src/Dao/AbstractDao.php +++ b/src/Dao/AbstractDao.php @@ -66,6 +66,10 @@ abstract class AbstractDao implements ICrudDao public function deleteAll() { + foreach ($this->findAll() as $entity) + { + $this->beforeDelete($entity); + } $this->fpdo->deleteFrom($this->tableName)->execute(); } @@ -94,17 +98,32 @@ abstract class AbstractDao implements ICrudDao return count(iterator_to_array($this->fpdo->from($this->tableName)->limit(1))) > 0; } + protected function findBy($columnName, $value) + { + $entities = []; + $query = $this->fpdo->from($this->tableName)->where($columnName, $value); + foreach ($query as $arrayEntity) + { + $entity = $this->entityConverter->toEntity($arrayEntity); + $entities[$entity->getId()] = $entity; + } + return $entities; + } + protected function findOneBy($columnName, $value) { - $arrayEntity = iterator_to_array($this->fpdo->from($this->tableName)->where($columnName, $value)); - if (!$arrayEntity) + $arrayEntities = $this->findBy($columnName, $value); + if (!$arrayEntities) return null; - - return $this->entityConverter->toEntity($arrayEntity[0]); + return array_shift($arrayEntities); } protected function deleteBy($columnName, $value) { + foreach ($this->findBy($columnName, $value) as $entity) + { + $this->beforeDelete($entity); + } $this->fpdo->deleteFrom($this->tableName)->where($columnName, $value)->execute(); } @@ -115,4 +134,8 @@ abstract class AbstractDao implements ICrudDao protected function afterSave(\Szurubooru\Entities\Entity $entity) { } + + protected function beforeDelete(\Szurubooru\Entities\Entity $entity) + { + } } diff --git a/src/Dao/PostDao.php b/src/Dao/PostDao.php index 1d5a73fa..3440f16e 100644 --- a/src/Dao/PostDao.php +++ b/src/Dao/PostDao.php @@ -3,12 +3,21 @@ namespace Szurubooru\Dao; class PostDao extends AbstractDao implements ICrudDao { - public function __construct(\Szurubooru\DatabaseConnection $databaseConnection) + private $fileService; + private $thumbnailService; + + public function __construct( + \Szurubooru\DatabaseConnection $databaseConnection, + \Szurubooru\Services\FileService $fileService, + \Szurubooru\Services\ThumbnailService $thumbnailService) { parent::__construct( $databaseConnection, 'posts', new \Szurubooru\Dao\EntityConverters\PostEntityConverter()); + + $this->fileService = $fileService; + $this->thumbnailService = $thumbnailService; } public function findByName($name) @@ -21,17 +30,35 @@ class PostDao extends AbstractDao implements ICrudDao return $this->findOneBy('contentChecksum', $checksum); } - protected function afterLoad(\Szurubooru\Entities\Entity $entity) + protected function afterLoad(\Szurubooru\Entities\Entity $post) { - $entity->setLazyLoader('tags', function(\Szurubooru\Entities\Post $post) + $post->setLazyLoader( + \Szurubooru\Entities\Post::LAZY_LOADER_CONTENT, + function(\Szurubooru\Entities\Post $post) + { + return $this->fileService->load($post->getContentPath()); + }); + + $post->setLazyLoader( + \Szurubooru\Entities\Post::LAZY_LOADER_THUMBNAIL_SOURCE_CONTENT, + function(\Szurubooru\Entities\Post $post) + { + return $this->fileService->load($post->getThumbnailSourceContentPath()); + }); + + $post->setLazyLoader( + \Szurubooru\Entities\Post::LAZY_LOADER_TAGS, + function(\Szurubooru\Entities\Post $post) { return $this->getTags($post); }); } - protected function afterSave(\Szurubooru\Entities\Entity $entity) + protected function afterSave(\Szurubooru\Entities\Entity $post) { - $this->syncTags($entity->getId(), $entity->getTags()); + $this->syncContent($post); + $this->syncThumbnailSourceContent($post); + $this->syncTags($post->getId(), $post->getTags()); } private function getTags(\Szurubooru\Entities\Post $post) @@ -44,6 +71,28 @@ class PostDao extends AbstractDao implements ICrudDao return $result; } + private function syncContent(\Szurubooru\Entities\Post $post) + { + $targetPath = $post->getContentPath(); + $content = $post->getContent(); + if ($content) + $this->fileService->save($targetPath, $content); + else + $this->fileService->delete($targetPath, $content); + $this->thumbnailService->deleteUsedThumbnails($targetPath); + } + + private function syncThumbnailSourceContent(\Szurubooru\Entities\Post $post) + { + $targetPath = $post->getThumbnailSourceContentPath(); + $content = $post->getThumbnailSourceContent(); + if ($content) + $this->fileService->save($targetPath, $content); + else + $this->fileService->delete($targetPath); + $this->thumbnailService->deleteUsedThumbnails($targetPath); + } + private function syncTags($postId, array $tags) { $existingTags = array_map( diff --git a/src/Dao/UserDao.php b/src/Dao/UserDao.php index e919b95c..630fb863 100644 --- a/src/Dao/UserDao.php +++ b/src/Dao/UserDao.php @@ -3,12 +3,21 @@ namespace Szurubooru\Dao; class UserDao extends AbstractDao implements ICrudDao { - public function __construct(\Szurubooru\DatabaseConnection $databaseConnection) + private $fileService; + private $thumbnailService; + + public function __construct( + \Szurubooru\DatabaseConnection $databaseConnection, + \Szurubooru\Services\FileService $fileService, + \Szurubooru\Services\ThumbnailService $thumbnailService) { parent::__construct( $databaseConnection, 'users', new \Szurubooru\Dao\EntityConverters\UserEntityConverter()); + + $this->fileService = $fileService; + $this->thumbnailService = $thumbnailService; } public function findByName($userName) @@ -36,4 +45,33 @@ class UserDao extends AbstractDao implements ICrudDao $this->deleteBy('name', $userName); $this->fpdo->deleteFrom('tokens')->where('additionalData', $userName); } + + protected function afterLoad(\Szurubooru\Entities\Entity $user) + { + $user->setLazyLoader( + \Szurubooru\Entities\User::LAZY_LOADER_CUSTOM_AVATAR_SOURCE_CONTENT, + function(\Szurubooru\Entities\User $user) + { + $avatarSource = $user->getCustomAvatarSourceContentPath(); + return $this->fileService->load($avatarSource); + }); + } + + protected function afterSave(\Szurubooru\Entities\Entity $user) + { + $targetPath = $user->getCustomAvatarSourceContentPath(); + $content = $user->getCustomAvatarSourceContent(); + if ($content) + $this->fileService->save($targetPath, $content); + else + $this->fileService->delete($targetPath); + $this->thumbnailService->deleteUsedThumbnails($targetPath); + } + + protected function afterDelete(\Szurubooru\Entities\Entity $user) + { + $avatarSource = $user->getCustomAvatarSourceContentPath(); + $this->fileService->delete($avatarSource); + $this->thumbnailService->deleteUsedThumbnails($avatarSource); + } } diff --git a/src/Entities/Post.php b/src/Entities/Post.php index 3e1f8bf5..8704ad1d 100644 --- a/src/Entities/Post.php +++ b/src/Entities/Post.php @@ -12,6 +12,10 @@ final class Post extends Entity const POST_TYPE_VIDEO = 3; const POST_TYPE_YOUTUBE = 4; + const LAZY_LOADER_TAGS = 'tags'; + const LAZY_LOADER_CONTENT = 'content'; + const LAZY_LOADER_THUMBNAIL_SOURCE_CONTENT = 'thumbnailSourceContent'; + protected $name; protected $userId; protected $uploadTime; @@ -175,11 +179,41 @@ final class Post extends Entity public function getTags() { - return $this->lazyLoad('tags', []); + return $this->lazyLoad(self::LAZY_LOADER_TAGS, []); } public function setTags(array $tags) { - $this->lazySave('tags', $tags); + $this->lazySave(self::LAZY_LOADER_TAGS, $tags); + } + + public function getContent() + { + return $this->lazyLoad(self::LAZY_LOADER_CONTENT, null); + } + + public function setContent($content) + { + $this->lazySave(self::LAZY_LOADER_CONTENT, $content); + } + + public function getThumbnailSourceContent() + { + return $this->lazyLoad(self::LAZY_LOADER_THUMBNAIL_SOURCE_CONTENT, null); + } + + public function setThumbnailSourceContent($content) + { + $this->lazySave(self::LAZY_LOADER_THUMBNAIL_SOURCE_CONTENT, $content); + } + + public function getContentPath() + { + return 'posts' . DIRECTORY_SEPARATOR . $this->getName(); + } + + public function getThumbnailSourceContentPath() + { + return 'posts' . DIRECTORY_SEPARATOR . $this->getName() . '-custom-thumb'; } } diff --git a/src/Entities/User.php b/src/Entities/User.php index 6878f9e1..40889c0d 100644 --- a/src/Entities/User.php +++ b/src/Entities/User.php @@ -14,6 +14,8 @@ final class User extends Entity const AVATAR_STYLE_MANUAL = 2; const AVATAR_STYLE_BLANK = 3; + const LAZY_LOADER_CUSTOM_AVATAR_SOURCE_CONTENT = 'customAvatarContent'; + protected $name; protected $email; protected $emailUnconfirmed; @@ -124,4 +126,19 @@ final class User extends Entity { $this->browsingSettings = $browsingSettings; } + + public function getCustomAvatarSourceContent() + { + return $this->lazyLoad(self::LAZY_LOADER_CUSTOM_AVATAR_SOURCE_CONTENT, null); + } + + public function setCustomAvatarSourceContent($content) + { + $this->lazySave(self::LAZY_LOADER_CUSTOM_AVATAR_SOURCE_CONTENT, $content); + } + + public function getCustomAvatarSourceContentPath() + { + return 'avatars' . DIRECTORY_SEPARATOR . $this->getId(); + } } diff --git a/src/Helpers/InputReader.php b/src/Helpers/InputReader.php index f7ff3d44..a47d9c08 100644 --- a/src/Helpers/InputReader.php +++ b/src/Helpers/InputReader.php @@ -27,6 +27,8 @@ final class InputReader extends \ArrayObject public function decodeBase64($base64string) { + if ($base64string === null) + return null; $commaPosition = strpos($base64string, ','); if ($commaPosition !== null) $base64string = substr($base64string, $commaPosition + 1); diff --git a/src/Services/FileService.php b/src/Services/FileService.php index 15adb4f6..25e7cf1e 100644 --- a/src/Services/FileService.php +++ b/src/Services/FileService.php @@ -79,6 +79,14 @@ class FileService unlink($fullPath); } + public function load($source) + { + if (!$this->exists($source)) + return null; + $fullPath = $this->getFullPath($source); + return file_get_contents($fullPath); + } + public function save($destination, $data) { $this->createFolders($destination); diff --git a/src/Services/PostService.php b/src/Services/PostService.php index b070a2b9..cba99731 100644 --- a/src/Services/PostService.php +++ b/src/Services/PostService.php @@ -8,9 +8,9 @@ class PostService private $transactionManager; private $postDao; private $postSearchService; - private $fileService; private $timeService; private $authService; + private $fileService; public function __construct( \Szurubooru\Config $config, @@ -27,9 +27,9 @@ class PostService $this->transactionManager = $transactionManager; $this->postDao = $postDao; $this->postSearchService = $postSearchService; - $this->fileService = $fileService; $this->timeService = $timeService; $this->authService = $authService; + $this->fileService = $fileService; } public function getByNameOrId($postNameOrId) @@ -92,16 +92,6 @@ class PostService return $this->transactionManager->commit($transactionFunc); } - public function getPostContentPath(\Szurubooru\Entities\Post $post) - { - return 'posts' . DIRECTORY_SEPARATOR . $post->getName(); - } - - public function getPostThumbnailSourcePath(\Szurubooru\Entities\Post $post) - { - return 'posts' . DIRECTORY_SEPARATOR . $post->getName() . '-custom-thumb'; - } - private function updatePostSafety(\Szurubooru\Entities\Post $post, $newSafety) { $post->setSafety($newSafety); @@ -145,15 +135,13 @@ class PostService $post->setContentChecksum(sha1($content)); $this->assertNoPostWithThisContentChecksum($post); - $target = $this->getPostContentPath($post); - $this->fileService->save($target, $content); - $fullPath = $this->fileService->getFullPath($target); + $post->setContent($content); - list ($imageWidth, $imageHeight) = getimagesize($fullPath); + list ($imageWidth, $imageHeight) = getimagesizefromstring($content); $post->setImageWidth($imageWidth); $post->setImageHeight($imageHeight); - $post->setOriginalFileSize(filesize($fullPath)); + $post->setOriginalFileSize(strlen($content)); } private function updatePostContentFromUrl(\Szurubooru\Entities\Post $post, $url) @@ -178,7 +166,7 @@ class PostService $this->assertNoPostWithThisContentChecksum($post); $youtubeThumbnailUrl = 'http://img.youtube.com/vi/' . $youtubeId . '/mqdefault.jpg'; $youtubeThumbnail = $this->fileService->download($youtubeThumbnailUrl); - $this->fileService->save($this->getPostThumbnailSourcePath($post), $youtubeThumbnail); + $post->setThumbnailSourceContent($youtubeThumbnail); } else { diff --git a/src/Services/UserService.php b/src/Services/UserService.php index ff65ce9e..baaa98ba 100644 --- a/src/Services/UserService.php +++ b/src/Services/UserService.php @@ -152,10 +152,6 @@ class UserService $transactionFunc = function() use ($user) { $this->userDao->deleteById($user->getId()); - - $avatarSource = $this->getCustomAvatarSourcePath($user); - $this->fileService->delete($avatarSource); - $this->thumbnailService->deleteUsedThumbnails($avatarSource); }; $this->transactionManager->commit($transactionFunc); } @@ -212,16 +208,6 @@ class UserService $this->transactionManager->commit($transactionFunc); } - public function getCustomAvatarSourcePath(\Szurubooru\Entities\User $user) - { - return 'avatars' . DIRECTORY_SEPARATOR . $user->getId(); - } - - public function getBlankAvatarSourcePath() - { - return 'avatars' . DIRECTORY_SEPARATOR . 'blank.png'; - } - private function updateUserAvatarStyle(\Szurubooru\Entities\User $user, $newAvatarStyle) { $user->setAvatarStyle($newAvatarStyle); @@ -229,9 +215,7 @@ class UserService private function updateUserAvatarContent(\Szurubooru\Entities\User $user, $newAvatarContent) { - $target = $this->getCustomAvatarSourcePath($user); - $this->fileService->save($target, $newAvatarContent); - $this->thumbnailService->deleteUsedThumbnails($target); + $user->setCustomAvatarSourceContent($newAvatarContent); } private function updateUserName(\Szurubooru\Entities\User $user, $newName) diff --git a/src/Upgrades/Upgrade04.php b/src/Upgrades/Upgrade04.php index 80c36357..e7cd9164 100644 --- a/src/Upgrades/Upgrade04.php +++ b/src/Upgrades/Upgrade04.php @@ -5,20 +5,27 @@ class Upgrade04 implements IUpgrade { private $postService; private $fileService; + private $thumbnailService; public function __construct( \Szurubooru\Services\PostService $postService, - \Szurubooru\Services\FileService $fileService) + \Szurubooru\Services\FileService $fileService, + \Szurubooru\Services\ThumbnailService $thumbnailService) { $this->postService = $postService; $this->fileService = $fileService; + $this->thumbnailService = $thumbnailService; } public function run(\Szurubooru\DatabaseConnection $databaseConnection) { $databaseConnection->getPDO()->exec('ALTER TABLE "posts" ADD COLUMN contentMimeType TEXT DEFAULT NULL'); - $postDao = new \Szurubooru\Dao\PostDao($databaseConnection); + $postDao = new \Szurubooru\Dao\PostDao( + $databaseConnection, + $this->fileService, + $this->thumbnailService); + $posts = $postDao->findAll(); foreach ($posts as $post) { diff --git a/tests/Dao/PostDaoTest.php b/tests/Dao/PostDaoTest.php index 8b176817..160252f6 100644 --- a/tests/Dao/PostDaoTest.php +++ b/tests/Dao/PostDaoTest.php @@ -3,6 +3,16 @@ namespace Szurubooru\Tests\Dao; final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { + private $fileServiceMock; + private $thumbnailServiceMock; + + public function setUp() + { + parent::setUp(); + $this->fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class); + $this->thumbnailServiceMock = $this->mock(\Szurubooru\Services\ThumbnailService::class); + } + public function testCreating() { $postDao = $this->getPostDao(); @@ -116,9 +126,81 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $this->assertEquals(2, count($tagDao->findAll())); } + public function testNotLoadingContentForNewPosts() + { + $postDao = $this->getPostDao(); + $newlyCreatedPost = $this->getPost(); + $this->assertNull($newlyCreatedPost->getContent()); + } + + public function testLoadingContentPostsForExistingPosts() + { + $postDao = $this->getPostDao(); + $post = $this->getPost(); + $postDao->save($post); + + $post = $postDao->findById($post->getId()); + + $this->fileServiceMock + ->expects($this->once()) + ->method('load') + ->with($post->getContentPath()) + ->willReturn('whatever'); + + $this->assertEquals('whatever', $post->getContent()); + } + + public function testSavingContent() + { + $postDao = $this->getPostDao(); + $post = $this->getPost(); + $post->setContent('whatever'); + + $this->thumbnailServiceMock + ->expects($this->exactly(2)) + ->method('deleteUsedThumbnails') + ->withConsecutive( + [$post->getContentPath()], + [$post->getThumbnailSourceContentPath()]); + + $this->fileServiceMock + ->expects($this->once()) + ->method('save') + ->with($post->getContentPath(), 'whatever'); + + $postDao->save($post); + } + public function testSavingContentAndThumbnail() + { + $postDao = $this->getPostDao(); + $post = $this->getPost(); + $post->setContent('whatever'); + $post->setThumbnailSourceContent('an image of sharks'); + + $this->thumbnailServiceMock + ->expects($this->exactly(2)) + ->method('deleteUsedThumbnails') + ->withConsecutive( + [$post->getContentPath()], + [$post->getThumbnailSourceContentPath()]); + + $this->fileServiceMock + ->expects($this->exactly(2)) + ->method('save') + ->withConsecutive( + [$post->getContentPath(), 'whatever'], + [$post->getThumbnailSourceContentPath(), 'an image of sharks']); + + $postDao->save($post); + } + + private function getPostDao() { - return new \Szurubooru\Dao\PostDao($this->databaseConnection); + return new \Szurubooru\Dao\PostDao( + $this->databaseConnection, + $this->fileServiceMock, + $this->thumbnailServiceMock); } private function getTagDao() diff --git a/tests/Dao/Services/UserSearchServiceTest.php b/tests/Dao/Services/UserSearchServiceTest.php index be0e9b4e..e479e360 100644 --- a/tests/Dao/Services/UserSearchServiceTest.php +++ b/tests/Dao/Services/UserSearchServiceTest.php @@ -8,7 +8,13 @@ class UserSearchServiceTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function setUp() { parent::setUp(); - $this->userDao = new \Szurubooru\Dao\UserDao($this->databaseConnection); + + $fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class); + $thumbnailServiceMock = $this->mock(\Szurubooru\Services\ThumbnailService::class); + $this->userDao = new \Szurubooru\Dao\UserDao( + $this->databaseConnection, + $fileServiceMock, + $thumbnailServiceMock); } public function testNothing() @@ -21,40 +27,59 @@ class UserSearchServiceTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $this->assertEquals($expected, $actual); } - public function testSorting() + public function testDefaultOrder() { - $user1 = $this->getTestUser('reginald'); - $user2 = $this->getTestUser('beartato'); + list ($user1, $user2) = $this->prepareUsers(); + $this->doTestSorting(null, [$user2]); + } + + public function testOrderByNameAscending() + { + list ($user1, $user2) = $this->prepareUsers(); + $this->doTestSorting('name,asc', [$user1]); + } + + public function testOrderByNameDescending() + { + list ($user1, $user2) = $this->prepareUsers(); + $this->doTestSorting('name,desc', [$user2]); + } + + public function testOrderByRegistrationTimeAscending() + { + list ($user1, $user2) = $this->prepareUsers(); + $this->doTestSorting('registrationTime,asc', [$user2]); + } + + public function testOrderByRegistrationTimeDescending() + { + list ($user1, $user2) = $this->prepareUsers(); + $this->doTestSorting('registrationTime,desc', [$user1]); + } + + private function prepareUsers() + { + $user1 = $this->getTestUser('beartato'); + $user2 = $this->getTestUser('reginald'); $user1->setRegistrationTime(date('c', mktime(3, 2, 1))); $user2->setRegistrationTime(date('c', mktime(1, 2, 3))); $this->userDao->save($user1); $this->userDao->save($user2); + return [$user1, $user2]; + } + private function doTestSorting($order, $expectedUsers) + { $userSearchService = $this->getUserSearchService(); $searchFilter = new \Szurubooru\Dao\SearchFilter(1); - $expected = new \Szurubooru\Dao\SearchResult($searchFilter, [$user2], 2); - $actual = $userSearchService->getFiltered($searchFilter); - $this->assertEquals($expected, $actual); + if ($order !== null) + $searchFilter->order = $order; - $searchFilter->order = 'name,asc'; - $expected = new \Szurubooru\Dao\SearchResult($searchFilter, [$user2], 2); - $actual = $userSearchService->getFiltered($searchFilter); - $this->assertEquals($expected, $actual); - - $searchFilter->order = 'name,desc'; - $expected = new \Szurubooru\Dao\SearchResult($searchFilter, [$user1], 2); - $actual = $userSearchService->getFiltered($searchFilter); - $this->assertEquals($expected, $actual); - - $searchFilter->order = 'registrationTime,desc'; - $expected = new \Szurubooru\Dao\SearchResult($searchFilter, [$user1], 2); - $actual = $userSearchService->getFiltered($searchFilter); - $this->assertEquals($expected, $actual); - - $searchFilter->order = 'registrationTime'; - $expected = new \Szurubooru\Dao\SearchResult($searchFilter, [$user2], 2); + $expected = new \Szurubooru\Dao\SearchResult($searchFilter, $expectedUsers, 2); $actual = $userSearchService->getFiltered($searchFilter); + foreach ($actual->entities as $entity) + $entity->resetLazyLoaders(); $this->assertEquals($expected, $actual); } diff --git a/tests/Dao/UserDaoTest.php b/tests/Dao/UserDaoTest.php index ed4a1e70..17a161ac 100644 --- a/tests/Dao/UserDaoTest.php +++ b/tests/Dao/UserDaoTest.php @@ -3,6 +3,17 @@ namespace Szurubooru\Tests\Dao; final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { + private $fileServiceMock; + private $thumbnailServiceMock; + + public function setUp() + { + parent::setUp(); + + $this->fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class); + $this->thumbnailServiceMock = $this->mock(\Szurubooru\Services\ThumbnailService::class); + } + public function testRetrievingByValidName() { $userDao = $this->getUserDao(); @@ -12,6 +23,7 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $expected = $user; $actual = $userDao->findByName($user->getName()); + $actual->resetLazyLoaders(); $this->assertEquals($actual, $expected); } @@ -34,9 +46,68 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $this->assertTrue($userDao->hasAnyUsers()); } + public function testNotLoadingAvatarContentForNewUsers() + { + $userDao = $this->getUserDao(); + $user = $this->getTestUser(); + $user->setAvatarStyle(\Szurubooru\Entities\User::AVATAR_STYLE_MANUAL); + $userDao->save($user); + + $this->assertNull($user->getCustomAvatarSourceContent()); + } + + public function testLoadingContentUsersForExistingUsers() + { + $userDao = $this->getUserDao(); + $user = $this->getTestUser(); + $user->setAvatarStyle(\Szurubooru\Entities\User::AVATAR_STYLE_MANUAL); + $userDao->save($user); + + $user = $userDao->findById($user->getId()); + + $this->fileServiceMock + ->expects($this->once()) + ->method('load') + ->with($user->getCustomAvatarSourceContentPath())->willReturn('whatever'); + + $this->assertEquals('whatever', $user->getCustomAvatarSourceContent()); + } + + public function testSavingContent() + { + $userDao = $this->getUserDao(); + $user = $this->getTestUser(); + $user->setAvatarStyle(\Szurubooru\Entities\User::AVATAR_STYLE_MANUAL); + $user->setCustomAvatarSourceContent('whatever'); + + $this->thumbnailServiceMock + ->expects($this->once()) + ->method('deleteUsedThumbnails') + ->with($this->callback( + function($subject) use ($user) + { + return $subject == $user->getCustomAvatarSourceContentPath(); + })); + + $this->fileServiceMock + ->expects($this->once()) + ->method('save') + ->with($this->callback( + function($subject) use ($user) + { + //callback is used because ->save() will create id, which is going to be used by the function below + return $subject == $user->getCustomAvatarSourceContentPath(); + }), 'whatever'); + + $userDao->save($user); + } + private function getUserDao() { - return new \Szurubooru\Dao\UserDao($this->databaseConnection); + return new \Szurubooru\Dao\UserDao( + $this->databaseConnection, + $this->fileServiceMock, + $this->thumbnailServiceMock); } private function getTestUser() diff --git a/tests/Helpers/InputReaderTest.php b/tests/Helpers/InputReaderTest.php index f423b2b9..c60c9d88 100644 --- a/tests/Helpers/InputReaderTest.php +++ b/tests/Helpers/InputReaderTest.php @@ -10,4 +10,10 @@ class InputReaderTest extends \Szurubooru\Tests\AbstractTestCase $expected = 'awesome dog'; $this->assertEquals($expected, $actual); } + + public function testDecodingEmptyBase64() + { + $inputReader = new \Szurubooru\Helpers\InputReader(); + $this->assertNull($inputReader->decodeBase64($inputReader->iDontEvenExist)); + } } diff --git a/tests/Services/PostServiceTest.php b/tests/Services/PostServiceTest.php index abc7eb70..f69747ef 100644 --- a/tests/Services/PostServiceTest.php +++ b/tests/Services/PostServiceTest.php @@ -60,8 +60,6 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase $formData->contentFileName = 'blah'; $this->postDaoMock->expects($this->once())->method('save')->will($this->returnArgument(0)); - $this->fileServiceMock->expects($this->once())->method('save'); - $this->fileServiceMock->expects($this->once())->method('getFullPath')->willReturn($this->getTestFilePath('image.jpg')); $this->postService = $this->getPostService(); $savedPost = $this->postService->createPost($formData); @@ -82,8 +80,6 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase $formData->contentFileName = 'blah'; $this->postDaoMock->expects($this->once())->method('save')->will($this->returnArgument(0)); - $this->fileServiceMock->expects($this->once())->method('save'); - $this->fileServiceMock->expects($this->once())->method('getFullPath')->willReturn($this->getTestFilePath('video.mp4')); $this->postService = $this->getPostService(); $savedPost = $this->postService->createPost($formData); @@ -104,8 +100,6 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase $formData->contentFileName = 'blah'; $this->postDaoMock->expects($this->once())->method('save')->will($this->returnArgument(0)); - $this->fileServiceMock->expects($this->once())->method('save'); - $this->fileServiceMock->expects($this->once())->method('getFullPath')->willReturn($this->getTestFilePath('flash.swf')); $this->postService = $this->getPostService(); $savedPost = $this->postService->createPost($formData);