Changed file mgmt to use entities' lazy getters

This commit is contained in:
Marcin Kurczewski 2014-09-20 12:45:56 +02:00
parent a2587fb0d8
commit a3f9382671
18 changed files with 426 additions and 96 deletions

13
TODO
View file

@ -8,6 +8,10 @@ everything related to posts:
- comment count - comment count
- fix broken thumbnails if no external software is installed - fix broken thumbnails if no external software is installed
- uploading post
- remove hard dependency on gd2 in PostService::setContentFromString
(getimagesizefromstring)
- single post view - single post view
- post content - post content
- post tags - post tags
@ -124,17 +128,10 @@ refactors:
separate PostTagDao for this) separate PostTagDao for this)
- post view proxy should retrieve full tags and full user - post view proxy should retrieve full tags and full user
- centralize markdown prefix decorators - 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 ViewProxy and make presenters use it instead of duplicating the code
- after implementing Post::getPostContentPath, make paths include file - after implementing Post::getPostContentPath, make paths include file
extensions so that apache can guess mime types when it serves the files 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 - add enum validation in IValidatables (needs refactors of enums and
possible disposal of EnumHelper in favor of something more subtle) possible disposal of EnumHelper in favor of something more subtle)
- (idea) keep denormalized data in separate tables, i.e. tag usages in - (idea) keep denormalized data in separate tables, i.e. tag usages in

View file

@ -29,16 +29,16 @@ final class PostContentController extends AbstractController
public function getPostContent($postName) public function getPostContent($postName)
{ {
$post = $this->postService->getByName($postName); $post = $this->postService->getByName($postName);
$source = $this->postService->getPostContentPath($post); $source = $post->getContentPath();
$this->fileService->serve($source); $this->fileService->serve($source);
} }
public function getPostThumbnail($postName, $size) public function getPostThumbnail($postName, $size)
{ {
$post = $this->postService->getByName($postName); $post = $this->postService->getByName($postName);
$source = $this->postService->getPostThumbnailSourcePath($post); $source = $post->getThumbnailSourceContentPath();
if (!$this->fileService->exists($source)) if (!$this->fileService->exists($source))
$source = $this->postService->getPostContentPath($post); $source = $post->getContentPath();
$sizedSource = $this->thumbnailService->getOrGenerate($source, $size, $size); $sizedSource = $this->thumbnailService->getOrGenerate($source, $size, $size);
$this->fileService->serve($sizedSource); $this->fileService->serve($sizedSource);

View file

@ -38,15 +38,15 @@ final class UserAvatarController extends AbstractController
break; break;
case \Szurubooru\Entities\User::AVATAR_STYLE_BLANK: case \Szurubooru\Entities\User::AVATAR_STYLE_BLANK:
$this->serveFromFile($this->userService->getBlankAvatarSourcePath(), $size); $this->serveFromFile($this->getBlankAvatarSourcePath(), $size);
break; break;
case \Szurubooru\Entities\User::AVATAR_STYLE_MANUAL: case \Szurubooru\Entities\User::AVATAR_STYLE_MANUAL:
$this->serveFromFile($this->userService->getCustomAvatarSourcePath($user), $size); $this->serveFromFile($user->getCustomAvatarSourceContentPath(), $size);
break; break;
default: default:
$this->serveFromFile($this->userService->getBlankAvatarSourcePath(), $size); $this->serveFromFile($this->getBlankAvatarSourcePath(), $size);
break; break;
} }
} }
@ -59,9 +59,14 @@ final class UserAvatarController extends AbstractController
private function serveFromFile($file, $size) private function serveFromFile($file, $size)
{ {
if (!$this->fileService->exists($file)) if (!$this->fileService->exists($file))
$file = $this->userService->getBlankAvatarSourcePath(); $file = $this->getBlankAvatarSourcePath();
$sizedFile = $this->thumbnailService->getOrGenerate($file, $size, $size); $sizedFile = $this->thumbnailService->getOrGenerate($file, $size, $size);
$this->fileService->serve($sizedFile); $this->fileService->serve($sizedFile);
} }
private function getBlankAvatarSourcePath()
{
return 'avatars' . DIRECTORY_SEPARATOR . 'blank.png';
}
} }

View file

@ -66,6 +66,10 @@ abstract class AbstractDao implements ICrudDao
public function deleteAll() public function deleteAll()
{ {
foreach ($this->findAll() as $entity)
{
$this->beforeDelete($entity);
}
$this->fpdo->deleteFrom($this->tableName)->execute(); $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; 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) protected function findOneBy($columnName, $value)
{ {
$arrayEntity = iterator_to_array($this->fpdo->from($this->tableName)->where($columnName, $value)); $arrayEntities = $this->findBy($columnName, $value);
if (!$arrayEntity) if (!$arrayEntities)
return null; return null;
return array_shift($arrayEntities);
return $this->entityConverter->toEntity($arrayEntity[0]);
} }
protected function deleteBy($columnName, $value) protected function deleteBy($columnName, $value)
{ {
foreach ($this->findBy($columnName, $value) as $entity)
{
$this->beforeDelete($entity);
}
$this->fpdo->deleteFrom($this->tableName)->where($columnName, $value)->execute(); $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 afterSave(\Szurubooru\Entities\Entity $entity)
{ {
} }
protected function beforeDelete(\Szurubooru\Entities\Entity $entity)
{
}
} }

View file

@ -3,12 +3,21 @@ namespace Szurubooru\Dao;
class PostDao extends AbstractDao implements ICrudDao 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( parent::__construct(
$databaseConnection, $databaseConnection,
'posts', 'posts',
new \Szurubooru\Dao\EntityConverters\PostEntityConverter()); new \Szurubooru\Dao\EntityConverters\PostEntityConverter());
$this->fileService = $fileService;
$this->thumbnailService = $thumbnailService;
} }
public function findByName($name) public function findByName($name)
@ -21,17 +30,35 @@ class PostDao extends AbstractDao implements ICrudDao
return $this->findOneBy('contentChecksum', $checksum); 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); 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) private function getTags(\Szurubooru\Entities\Post $post)
@ -44,6 +71,28 @@ class PostDao extends AbstractDao implements ICrudDao
return $result; 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) private function syncTags($postId, array $tags)
{ {
$existingTags = array_map( $existingTags = array_map(

View file

@ -3,12 +3,21 @@ namespace Szurubooru\Dao;
class UserDao extends AbstractDao implements ICrudDao 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( parent::__construct(
$databaseConnection, $databaseConnection,
'users', 'users',
new \Szurubooru\Dao\EntityConverters\UserEntityConverter()); new \Szurubooru\Dao\EntityConverters\UserEntityConverter());
$this->fileService = $fileService;
$this->thumbnailService = $thumbnailService;
} }
public function findByName($userName) public function findByName($userName)
@ -36,4 +45,33 @@ class UserDao extends AbstractDao implements ICrudDao
$this->deleteBy('name', $userName); $this->deleteBy('name', $userName);
$this->fpdo->deleteFrom('tokens')->where('additionalData', $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);
}
} }

View file

@ -12,6 +12,10 @@ final class Post extends Entity
const POST_TYPE_VIDEO = 3; const POST_TYPE_VIDEO = 3;
const POST_TYPE_YOUTUBE = 4; 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 $name;
protected $userId; protected $userId;
protected $uploadTime; protected $uploadTime;
@ -175,11 +179,41 @@ final class Post extends Entity
public function getTags() public function getTags()
{ {
return $this->lazyLoad('tags', []); return $this->lazyLoad(self::LAZY_LOADER_TAGS, []);
} }
public function setTags(array $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';
} }
} }

View file

@ -14,6 +14,8 @@ final class User extends Entity
const AVATAR_STYLE_MANUAL = 2; const AVATAR_STYLE_MANUAL = 2;
const AVATAR_STYLE_BLANK = 3; const AVATAR_STYLE_BLANK = 3;
const LAZY_LOADER_CUSTOM_AVATAR_SOURCE_CONTENT = 'customAvatarContent';
protected $name; protected $name;
protected $email; protected $email;
protected $emailUnconfirmed; protected $emailUnconfirmed;
@ -124,4 +126,19 @@ final class User extends Entity
{ {
$this->browsingSettings = $browsingSettings; $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();
}
} }

View file

@ -27,6 +27,8 @@ final class InputReader extends \ArrayObject
public function decodeBase64($base64string) public function decodeBase64($base64string)
{ {
if ($base64string === null)
return null;
$commaPosition = strpos($base64string, ','); $commaPosition = strpos($base64string, ',');
if ($commaPosition !== null) if ($commaPosition !== null)
$base64string = substr($base64string, $commaPosition + 1); $base64string = substr($base64string, $commaPosition + 1);

View file

@ -79,6 +79,14 @@ class FileService
unlink($fullPath); 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) public function save($destination, $data)
{ {
$this->createFolders($destination); $this->createFolders($destination);

View file

@ -8,9 +8,9 @@ class PostService
private $transactionManager; private $transactionManager;
private $postDao; private $postDao;
private $postSearchService; private $postSearchService;
private $fileService;
private $timeService; private $timeService;
private $authService; private $authService;
private $fileService;
public function __construct( public function __construct(
\Szurubooru\Config $config, \Szurubooru\Config $config,
@ -27,9 +27,9 @@ class PostService
$this->transactionManager = $transactionManager; $this->transactionManager = $transactionManager;
$this->postDao = $postDao; $this->postDao = $postDao;
$this->postSearchService = $postSearchService; $this->postSearchService = $postSearchService;
$this->fileService = $fileService;
$this->timeService = $timeService; $this->timeService = $timeService;
$this->authService = $authService; $this->authService = $authService;
$this->fileService = $fileService;
} }
public function getByNameOrId($postNameOrId) public function getByNameOrId($postNameOrId)
@ -92,16 +92,6 @@ class PostService
return $this->transactionManager->commit($transactionFunc); 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) private function updatePostSafety(\Szurubooru\Entities\Post $post, $newSafety)
{ {
$post->setSafety($newSafety); $post->setSafety($newSafety);
@ -145,15 +135,13 @@ class PostService
$post->setContentChecksum(sha1($content)); $post->setContentChecksum(sha1($content));
$this->assertNoPostWithThisContentChecksum($post); $this->assertNoPostWithThisContentChecksum($post);
$target = $this->getPostContentPath($post); $post->setContent($content);
$this->fileService->save($target, $content);
$fullPath = $this->fileService->getFullPath($target);
list ($imageWidth, $imageHeight) = getimagesize($fullPath); list ($imageWidth, $imageHeight) = getimagesizefromstring($content);
$post->setImageWidth($imageWidth); $post->setImageWidth($imageWidth);
$post->setImageHeight($imageHeight); $post->setImageHeight($imageHeight);
$post->setOriginalFileSize(filesize($fullPath)); $post->setOriginalFileSize(strlen($content));
} }
private function updatePostContentFromUrl(\Szurubooru\Entities\Post $post, $url) private function updatePostContentFromUrl(\Szurubooru\Entities\Post $post, $url)
@ -178,7 +166,7 @@ class PostService
$this->assertNoPostWithThisContentChecksum($post); $this->assertNoPostWithThisContentChecksum($post);
$youtubeThumbnailUrl = 'http://img.youtube.com/vi/' . $youtubeId . '/mqdefault.jpg'; $youtubeThumbnailUrl = 'http://img.youtube.com/vi/' . $youtubeId . '/mqdefault.jpg';
$youtubeThumbnail = $this->fileService->download($youtubeThumbnailUrl); $youtubeThumbnail = $this->fileService->download($youtubeThumbnailUrl);
$this->fileService->save($this->getPostThumbnailSourcePath($post), $youtubeThumbnail); $post->setThumbnailSourceContent($youtubeThumbnail);
} }
else else
{ {

View file

@ -152,10 +152,6 @@ class UserService
$transactionFunc = function() use ($user) $transactionFunc = function() use ($user)
{ {
$this->userDao->deleteById($user->getId()); $this->userDao->deleteById($user->getId());
$avatarSource = $this->getCustomAvatarSourcePath($user);
$this->fileService->delete($avatarSource);
$this->thumbnailService->deleteUsedThumbnails($avatarSource);
}; };
$this->transactionManager->commit($transactionFunc); $this->transactionManager->commit($transactionFunc);
} }
@ -212,16 +208,6 @@ class UserService
$this->transactionManager->commit($transactionFunc); $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) private function updateUserAvatarStyle(\Szurubooru\Entities\User $user, $newAvatarStyle)
{ {
$user->setAvatarStyle($newAvatarStyle); $user->setAvatarStyle($newAvatarStyle);
@ -229,9 +215,7 @@ class UserService
private function updateUserAvatarContent(\Szurubooru\Entities\User $user, $newAvatarContent) private function updateUserAvatarContent(\Szurubooru\Entities\User $user, $newAvatarContent)
{ {
$target = $this->getCustomAvatarSourcePath($user); $user->setCustomAvatarSourceContent($newAvatarContent);
$this->fileService->save($target, $newAvatarContent);
$this->thumbnailService->deleteUsedThumbnails($target);
} }
private function updateUserName(\Szurubooru\Entities\User $user, $newName) private function updateUserName(\Szurubooru\Entities\User $user, $newName)

View file

@ -5,20 +5,27 @@ class Upgrade04 implements IUpgrade
{ {
private $postService; private $postService;
private $fileService; private $fileService;
private $thumbnailService;
public function __construct( public function __construct(
\Szurubooru\Services\PostService $postService, \Szurubooru\Services\PostService $postService,
\Szurubooru\Services\FileService $fileService) \Szurubooru\Services\FileService $fileService,
\Szurubooru\Services\ThumbnailService $thumbnailService)
{ {
$this->postService = $postService; $this->postService = $postService;
$this->fileService = $fileService; $this->fileService = $fileService;
$this->thumbnailService = $thumbnailService;
} }
public function run(\Szurubooru\DatabaseConnection $databaseConnection) public function run(\Szurubooru\DatabaseConnection $databaseConnection)
{ {
$databaseConnection->getPDO()->exec('ALTER TABLE "posts" ADD COLUMN contentMimeType TEXT DEFAULT NULL'); $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(); $posts = $postDao->findAll();
foreach ($posts as $post) foreach ($posts as $post)
{ {

View file

@ -3,6 +3,16 @@ namespace Szurubooru\Tests\Dao;
final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase 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() public function testCreating()
{ {
$postDao = $this->getPostDao(); $postDao = $this->getPostDao();
@ -116,9 +126,81 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase
$this->assertEquals(2, count($tagDao->findAll())); $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() private function getPostDao()
{ {
return new \Szurubooru\Dao\PostDao($this->databaseConnection); return new \Szurubooru\Dao\PostDao(
$this->databaseConnection,
$this->fileServiceMock,
$this->thumbnailServiceMock);
} }
private function getTagDao() private function getTagDao()

View file

@ -8,7 +8,13 @@ class UserSearchServiceTest extends \Szurubooru\Tests\AbstractDatabaseTestCase
public function setUp() public function setUp()
{ {
parent::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() public function testNothing()
@ -21,40 +27,59 @@ class UserSearchServiceTest extends \Szurubooru\Tests\AbstractDatabaseTestCase
$this->assertEquals($expected, $actual); $this->assertEquals($expected, $actual);
} }
public function testSorting() public function testDefaultOrder()
{ {
$user1 = $this->getTestUser('reginald'); list ($user1, $user2) = $this->prepareUsers();
$user2 = $this->getTestUser('beartato'); $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))); $user1->setRegistrationTime(date('c', mktime(3, 2, 1)));
$user2->setRegistrationTime(date('c', mktime(1, 2, 3))); $user2->setRegistrationTime(date('c', mktime(1, 2, 3)));
$this->userDao->save($user1); $this->userDao->save($user1);
$this->userDao->save($user2); $this->userDao->save($user2);
return [$user1, $user2];
}
private function doTestSorting($order, $expectedUsers)
{
$userSearchService = $this->getUserSearchService(); $userSearchService = $this->getUserSearchService();
$searchFilter = new \Szurubooru\Dao\SearchFilter(1); $searchFilter = new \Szurubooru\Dao\SearchFilter(1);
$expected = new \Szurubooru\Dao\SearchResult($searchFilter, [$user2], 2); if ($order !== null)
$actual = $userSearchService->getFiltered($searchFilter); $searchFilter->order = $order;
$this->assertEquals($expected, $actual);
$searchFilter->order = 'name,asc'; $expected = new \Szurubooru\Dao\SearchResult($searchFilter, $expectedUsers, 2);
$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);
$actual = $userSearchService->getFiltered($searchFilter); $actual = $userSearchService->getFiltered($searchFilter);
foreach ($actual->entities as $entity)
$entity->resetLazyLoaders();
$this->assertEquals($expected, $actual); $this->assertEquals($expected, $actual);
} }

View file

@ -3,6 +3,17 @@ namespace Szurubooru\Tests\Dao;
final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase 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() public function testRetrievingByValidName()
{ {
$userDao = $this->getUserDao(); $userDao = $this->getUserDao();
@ -12,6 +23,7 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase
$expected = $user; $expected = $user;
$actual = $userDao->findByName($user->getName()); $actual = $userDao->findByName($user->getName());
$actual->resetLazyLoaders();
$this->assertEquals($actual, $expected); $this->assertEquals($actual, $expected);
} }
@ -34,9 +46,68 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase
$this->assertTrue($userDao->hasAnyUsers()); $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() private function getUserDao()
{ {
return new \Szurubooru\Dao\UserDao($this->databaseConnection); return new \Szurubooru\Dao\UserDao(
$this->databaseConnection,
$this->fileServiceMock,
$this->thumbnailServiceMock);
} }
private function getTestUser() private function getTestUser()

View file

@ -10,4 +10,10 @@ class InputReaderTest extends \Szurubooru\Tests\AbstractTestCase
$expected = 'awesome dog'; $expected = 'awesome dog';
$this->assertEquals($expected, $actual); $this->assertEquals($expected, $actual);
} }
public function testDecodingEmptyBase64()
{
$inputReader = new \Szurubooru\Helpers\InputReader();
$this->assertNull($inputReader->decodeBase64($inputReader->iDontEvenExist));
}
} }

View file

@ -60,8 +60,6 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase
$formData->contentFileName = 'blah'; $formData->contentFileName = 'blah';
$this->postDaoMock->expects($this->once())->method('save')->will($this->returnArgument(0)); $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(); $this->postService = $this->getPostService();
$savedPost = $this->postService->createPost($formData); $savedPost = $this->postService->createPost($formData);
@ -82,8 +80,6 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase
$formData->contentFileName = 'blah'; $formData->contentFileName = 'blah';
$this->postDaoMock->expects($this->once())->method('save')->will($this->returnArgument(0)); $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(); $this->postService = $this->getPostService();
$savedPost = $this->postService->createPost($formData); $savedPost = $this->postService->createPost($formData);
@ -104,8 +100,6 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase
$formData->contentFileName = 'blah'; $formData->contentFileName = 'blah';
$this->postDaoMock->expects($this->once())->method('save')->will($this->returnArgument(0)); $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(); $this->postService = $this->getPostService();
$savedPost = $this->postService->createPost($formData); $savedPost = $this->postService->createPost($formData);