diff --git a/tests/RepositoryTest.php b/tests/RepositoryTest.php index a020e8a..147c217 100644 --- a/tests/RepositoryTest.php +++ b/tests/RepositoryTest.php @@ -3,7 +3,13 @@ namespace Salehhashemi\Repository\Tests; use Illuminate\Contracts\Pagination\Paginator; +use Illuminate\Database\Eloquent\Collection as EloquentCollection; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Support\Facades\DB; +use InvalidArgumentException; +use Salehhashemi\Repository\Tests\TestSupport\Models\Category; use Salehhashemi\Repository\Tests\TestSupport\Models\Post; +use Salehhashemi\Repository\Tests\TestSupport\Repositories\Criterias\FeaturedPostCriteria; use Salehhashemi\Repository\Tests\TestSupport\Repositories\PostRepository; use Salehhashemi\Repository\Tests\TestSupport\Repositories\PostRepositoryInterface; @@ -31,6 +37,106 @@ public function testFindOneReturnsModelInstance() $this->assertEquals($post->id, $foundPost->id); } + public function testFindOneReturnsNullIfNotFound() + { + $postRepository = new PostRepository(); + $nonExistentId = 99999; // This ID does not exist in the database + + $foundPost = $postRepository->findOne($nonExistentId); + + $this->assertNull($foundPost); + } + + public function testFindOneOrFailReturnsModelInstance() + { + $post = Post::factory()->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $foundPost = $postRepo->findOneOrFail($post->id); + + $this->assertInstanceOf(Post::class, $foundPost); + $this->assertEquals($post->id, $foundPost->id); + } + + public function testFindOneOrFailThrowsExceptionIfNotFound() + { + $this->expectException(ModelNotFoundException::class); + + $postRepository = new PostRepository(); + $nonExistentId = 99999; // This ID does not exist in the database + + $postRepository->findOneOrFail($nonExistentId); + } + + public function testFindAllRetrievesAllRecords() + { + Post::factory()->count(5)->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $posts = $postRepo->findAll(); + + $this->assertCount(5, $posts); + $this->assertInstanceOf(EloquentCollection::class, $posts); + } + + public function testFindAllWithLimitOption() + { + Post::factory()->count(10)->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $posts = $postRepo->findAll(['limit' => 5]); + + $this->assertCount(5, $posts); + } + + public function testFindAllWithOffsetAndLimitOptions() + { + Post::factory()->count(10)->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $posts = $postRepo->findAll(['offset' => 5, 'limit' => 4]); + + $this->assertCount(4, $posts); + + $expectedId = 5; // This depends on how IDs are assigned in your test database + $firstPostAfterOffset = $posts->first(); + $this->assertEquals($expectedId, $firstPostAfterOffset->id); + } + + public function testFindListRetrievesDefaultKeyValuePairs() + { + Post::factory()->count(3)->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $list = $postRepo->findList(); + + $this->assertCount(3, $list); + foreach ($list as $key => $value) { + $this->assertIsNumeric($key); // Default key is 'id' + $this->assertIsNumeric($value); // Default value is from 'getDisplayField' + } + } + + public function testFindListWithCustomKeyValuePairs() + { + Post::factory()->count(3)->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $list = $postRepo->findList('id', 'title'); + + $this->assertCount(3, $list); + foreach ($list as $key => $title) { + $this->assertIsNumeric($key); + $this->assertIsString($title); + } + } + public function testPaginateWithSpecifiedPerPage() { Post::factory()->count(15)->create(); @@ -58,4 +164,171 @@ public function testPaginateWithDefaultPerPage() $this->assertInstanceOf(Paginator::class, $paginatedResults); $this->assertCount(min(10, $defaultPerPage), $paginatedResults->items()); } + + public function testInvalidPageSizeThrowsException() + { + $this->expectException(InvalidArgumentException::class); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $postRepo->paginate(0); + } + + public function testGetFilteredPosts() + { + $category = Category::factory()->create(); + + $post = Post::factory()->create([ + 'title' => 'test', + 'status' => 'published', + 'created_at' => now()->subDay(), + ]); + + $post->categories()->attach($category->id); + + Post::factory()->create(['status' => 'draft', 'created_at' => now()->subHour()]); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + // Define filter options + $filterOptions = [ + 'title' => 'test', + 'status' => 'published', + 'orderBy' => 'created_at', + 'orderDirection' => 'ASC', + 'category_id' => $category->id, + ]; + + $postRepo->orderBy('created_at'); + + /** @var \Illuminate\Database\Eloquent\Collection $filteredPosts */ + $filteredPosts = $postRepo->search($filterOptions); + + $this->assertCount(1, $filteredPosts); + $this->assertEquals('published', $filteredPosts->first()->status); + $this->assertEquals( + now()->subDay()->format('Y-m-d H:i:s'), + $filteredPosts->first()->created_at->format('Y-m-d H:i:s') + ); + } + + public function testGetCriteriaPosts() + { + Post::factory()->create(['is_featured' => 1]); + Post::factory()->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $postRepo->addCriteria(new FeaturedPostCriteria()); + + $filteredPosts = $postRepo->findAll(); + + $this->assertCount(1, $filteredPosts); + } + + public function testFindAllFeatured() + { + Post::factory()->count(3)->create(['is_featured' => 1]); + Post::factory()->count(2)->create(['is_featured' => 0]); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + $featuredPosts = $postRepo->findAllFeatured(); + + $this->assertCount(3, $featuredPosts); + foreach ($featuredPosts as $post) { + $this->assertEquals(1, $post->is_featured); + } + } + + public function testSearchVisible() + { + Post::factory()->count(6)->create(['status' => 'draft']); + Post::factory()->count(4)->create(['status' => 'published']); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + $queryParams = ['status' => 'draft']; + $paginatedPosts = $postRepo->searchVisible($queryParams, 5); + + $this->assertInstanceOf(Paginator::class, $paginatedPosts); + $this->assertCount(5, $paginatedPosts->items()); + } + + public function testFindOnePublishedOrFail() + { + $publishedPost = Post::factory()->create(['is_published' => 1]); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + + $foundPost = $postRepo->findOnePublishedOrFail($publishedPost->id); + $this->assertInstanceOf(Post::class, $foundPost); + $this->assertEquals(1, $foundPost->is_published); + + $this->expectException(ModelNotFoundException::class); + $postRepo->findOnePublishedOrFail(999); // Non-existent ID + } + + public function testWithComments() + { + $post = Post::factory()->hasComments(3)->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + $postRepo->withComments(); + $foundPost = $postRepo->findOne($post->id); + + $this->assertCount(3, $foundPost->comments); + } + + public function testWithCategories() + { + // Create posts and associate them with categories + $post = Post::factory()->hasCategories(3)->create(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + $postRepo->withCategories(); + + $retrievedPost = $postRepo->findOne($post->id); + + $this->assertTrue($retrievedPost->relationLoaded('categories')); + $this->assertCount(3, $retrievedPost->categories); + + foreach ($retrievedPost->categories as $category) { + $this->assertNotEmpty($category->name); + } + } + + public function testLockForUpdateUsage() + { + $post = Post::factory()->create(); + + DB::beginTransaction(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + $postRepo->lockForUpdate(); + + $lockedPost = $postRepo->findOne($post->id); + $lockedPost->title = 'Updated Title'; + $lockedPost->save(); + + DB::commit(); + + $updatedPost = Post::query()->find($post->id); + $this->assertEquals('Updated Title', $updatedPost->title); + } + + public function testSharedLockUsage() + { + $post = Post::factory()->create(); + + DB::beginTransaction(); + + $postRepo = $this->app->make(PostRepositoryInterface::class); + $postRepo->sharedLock(); + + $lockedPost = $postRepo->findOne($post->id); + + DB::commit(); + + $this->assertInstanceOf(Post::class, $lockedPost); + $this->assertEquals($post->id, $lockedPost->id); + } } diff --git a/tests/TestSupport/Models/Category.php b/tests/TestSupport/Models/Category.php index 6e569ba..8e9b1b2 100644 --- a/tests/TestSupport/Models/Category.php +++ b/tests/TestSupport/Models/Category.php @@ -11,6 +11,8 @@ * @property int $id * @property string $name * @property \Illuminate\Database\Eloquent\Collection|\Salehhashemi\Repository\Tests\TestSupport\Models\Post[] $posts + * + * @method static CategoryFactory factory($count = null, $state = []) */ class Category extends Model { diff --git a/tests/TestSupport/Models/Comment.php b/tests/TestSupport/Models/Comment.php index 621831c..a0cd771 100644 --- a/tests/TestSupport/Models/Comment.php +++ b/tests/TestSupport/Models/Comment.php @@ -14,6 +14,8 @@ * @property string $content * @property int $post_id * @property \Salehhashemi\Repository\Tests\TestSupport\Models\Post $post + * + * @method static CommentFactory factory($count = null, $state = []) */ class Comment extends Model { diff --git a/tests/TestSupport/Models/Post.php b/tests/TestSupport/Models/Post.php index d041252..daf8356 100644 --- a/tests/TestSupport/Models/Post.php +++ b/tests/TestSupport/Models/Post.php @@ -14,18 +14,20 @@ * @property string $content * @property string $status * @property bool $is_published + * @property bool $is_featured * @property \Illuminate\Database\Eloquent\Collection|\Salehhashemi\Repository\Tests\TestSupport\Models\Comment[] $comments * @property \Illuminate\Database\Eloquent\Collection| \Salehhashemi\Repository\Tests\TestSupport\Models\Category[] $categories * * @method static \Illuminate\Database\Eloquent\Builder|Post newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Post newQuery() * @method static \Illuminate\Database\Eloquent\Builder|Post query() + * @method static PostFactory factory($count = null, $state = []) */ class Post extends Model { use HasFactory; - protected $fillable = ['title', 'content', 'status', 'is_published']; + protected $fillable = ['title', 'content', 'status', 'is_published', 'is_featured']; /** * {@inheritDoc} diff --git a/tests/TestSupport/Repositories/PostRepository.php b/tests/TestSupport/Repositories/PostRepository.php index bc42b58..0e0fa0f 100644 --- a/tests/TestSupport/Repositories/PostRepository.php +++ b/tests/TestSupport/Repositories/PostRepository.php @@ -27,7 +27,7 @@ protected function getFilterManager(): PostFilter return $filterManager; } - public function findAllFeatured(array $queryParams): EloquentCollection + public function findAllFeatured(): EloquentCollection { $this->addCriteria(new FeaturedPostCriteria()); @@ -36,7 +36,7 @@ public function findAllFeatured(array $queryParams): EloquentCollection public function searchVisible(array $queryParams, int $perPage): Paginator { - $this->orderBy('sort'); + $this->orderBy('id'); $this->withCategories(); return $this->search($queryParams, $perPage); diff --git a/tests/TestSupport/Repositories/PostRepositoryInterface.php b/tests/TestSupport/Repositories/PostRepositoryInterface.php index 26d86db..84fdc16 100644 --- a/tests/TestSupport/Repositories/PostRepositoryInterface.php +++ b/tests/TestSupport/Repositories/PostRepositoryInterface.php @@ -14,7 +14,7 @@ */ interface PostRepositoryInterface extends RepositoryInterface, SearchableRepositoryInterface { - public function findAllFeatured(array $queryParams): EloquentCollection; + public function findAllFeatured(): EloquentCollection; public function searchVisible(array $queryParams, int $perPage): Paginator; diff --git a/tests/TestSupport/database/factories/PostFactory.php b/tests/TestSupport/database/factories/PostFactory.php index a971833..e112e07 100644 --- a/tests/TestSupport/database/factories/PostFactory.php +++ b/tests/TestSupport/database/factories/PostFactory.php @@ -3,6 +3,8 @@ namespace Salehhashemi\Repository\Tests\TestSupport\database\factories; use Illuminate\Database\Eloquent\Factories\Factory; +use Salehhashemi\Repository\Tests\TestSupport\Models\Category; +use Salehhashemi\Repository\Tests\TestSupport\Models\Comment; use Salehhashemi\Repository\Tests\TestSupport\Models\Post; class PostFactory extends Factory @@ -18,7 +20,21 @@ public function definition(): array 'title' => $this->faker->sentence, 'content' => $this->faker->paragraph, 'status' => $this->faker->randomElement(['draft', 'published']), - 'is_published' => $this->faker->boolean, + 'is_published' => false, + 'is_featured' => false, ]; } + + public function hasComments(int $count): Factory + { + return $this->has(Comment::factory()->count($count), 'comments'); + } + + public function hasCategories(int $count): Factory + { + return $this->afterCreating(function (Post $post) use ($count) { + $categories = Category::factory()->count($count)->create(); + $post->categories()->attach($categories); + }); + } } diff --git a/tests/TestSupport/database/migrations/2023_12_15_134252_create_posts_table.php b/tests/TestSupport/database/migrations/2023_12_15_134252_create_posts_table.php index 61c6b01..9d2117d 100644 --- a/tests/TestSupport/database/migrations/2023_12_15_134252_create_posts_table.php +++ b/tests/TestSupport/database/migrations/2023_12_15_134252_create_posts_table.php @@ -17,6 +17,7 @@ public function up(): void $table->text('content'); $table->string('status')->default('draft'); $table->boolean('is_published')->default(false); + $table->boolean('is_featured')->default(false); $table->timestamps(); });