diff --git a/src/modules/blogs/blogs.controller.ts b/src/modules/blogs/blogs.controller.ts index db0a8a36d..f7f8ebc0f 100644 --- a/src/modules/blogs/blogs.controller.ts +++ b/src/modules/blogs/blogs.controller.ts @@ -80,7 +80,14 @@ export class BlogController { @ApiResponse({ status: 200, description: 'Blog fetched successfully.', type: BlogDto }) @ApiResponse({ status: 404, description: 'Blog not found.' }) @ApiResponse({ status: 500, description: 'Internal server error.' }) - async getSingleBlog(@Param('id', new ParseUUIDPipe()) id: string, @Request() req): Promise { + async getSingleBlog( + @Param('id', new ParseUUIDPipe()) id: string, + @Request() req + ): Promise<{ + status_code: number; + message: string; + data: BlogDto; + }> { return await this.blogService.getSingleBlog(id, req.user); } diff --git a/src/modules/blogs/blogs.service.ts b/src/modules/blogs/blogs.service.ts index c5ee2f6d6..e7c8e33c9 100644 --- a/src/modules/blogs/blogs.service.ts +++ b/src/modules/blogs/blogs.service.ts @@ -1,7 +1,7 @@ import * as SYS_MSG from '@shared/constants/SystemMessages'; import { HttpStatus, Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, Like, MoreThanOrEqual, FindOptionsWhere } from 'typeorm'; +import { Repository, Like, MoreThanOrEqual, FindOptionsWhere, Not, IsNull } from 'typeorm'; import { Blog } from './entities/blog.entity'; import { CreateBlogDto } from './dtos/create-blog.dto'; import { UpdateBlogDto } from './dtos/update-blog.dto'; @@ -52,8 +52,15 @@ export class BlogService { }; } - async getSingleBlog(blogId: string, user: User): Promise { - const singleBlog = await this.blogRepository.findOneBy({ id: blogId }); + async getSingleBlog( + blogId: string, + user: User + ): Promise<{ + status_code: number; + message: string; + data: BlogResponseDto; + }> { + const singleBlog = await this.blogRepository.findOneBy({ id: blogId, deletedAt: null }); const fullName = await this.fetchUserById(user.id); if (!singleBlog) { @@ -66,7 +73,7 @@ export class BlogService { return { status_code: 200, message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, - data: { blog_id: id, ...rest, author, published_date: created_at }, + data: { blog_id: id, ...rest, author, published_date: created_at, created_at }, }; } @@ -96,46 +103,56 @@ export class BlogService { created_at: updatedBlog.created_at, }; } + async deleteBlogPost(id: string): Promise { const blog = await this.blogRepository.findOne({ where: { id } }); if (!blog) { throw new CustomHttpException('Blog post with this id does not exist.', HttpStatus.NOT_FOUND); - } else await this.blogRepository.remove(blog); + } else await this.blogRepository.softRemove(blog); } async getAllBlogs( page: number, - pageSize: number + pageSize: number, + includeDeleted: boolean = false ): Promise<{ status_code: number; message: string; data: { currentPage: number; totalPages: number; totalResults: number; blogs: BlogResponseDto[]; meta: any }; }> { - const skip = (page - 1) * pageSize; - - const [result, total] = await this.blogRepository.findAndCount({ - skip, - take: pageSize, - relations: ['author'], - }); - - const data = this.mapBlogResults(result); - const totalPages = Math.ceil(total / pageSize); + const result = await this.getBlogs(page, pageSize, includeDeleted ? {} : { deletedAt: null }); + return { + status_code: result.status_code, + message: result.message, + data: { + ...result.data, + blogs: includeDeleted + ? result.data.blogs + : result.data.blogs + .filter(blog => !blog.deletedAt) + .map(blog => { + const { deletedAt, ...rest } = blog; + return rest; + }), + }, + }; + } + async getDeletedBlogs( + page: number, + pageSize: number + ): Promise<{ + status_code: number; + message: string; + data: { currentPage: number; totalPages: number; totalResults: number; blogs: BlogResponseDto[]; meta: any }; + }> { + const result = await this.getBlogs(page, pageSize, { deletedAt: Not(IsNull()) }); return { - status_code: HttpStatus.OK, - message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + status_code: result.status_code, + message: result.message, data: { - currentPage: page, - totalPages, - totalResults: total, - blogs: data, - meta: { - hasNext: page < totalPages, - total, - nextPage: page < totalPages ? page + 1 : null, - prevPage: page > 1 ? page - 1 : null, - }, + ...result.data, + blogs: result.data.blogs, }, }; } @@ -188,7 +205,12 @@ export class BlogService { current_page: page, total_pages: totalPages, total_results: total, - blogs: data, + blogs: data + .filter(blog => !blog.deletedAt) + .map(blog => { + const { deletedAt, ...rest } = blog; + return rest; + }), meta: { has_next: page < totalPages, total, @@ -199,6 +221,45 @@ export class BlogService { }; } + private async getBlogs( + page: number, + pageSize: number, + whereCondition: FindOptionsWhere + ): Promise<{ + status_code: number; + message: string; + data: { currentPage: number; totalPages: number; totalResults: number; blogs: BlogResponseDto[]; meta: any }; + }> { + const skip = (page - 1) * pageSize; + + const [result, total] = await this.blogRepository.findAndCount({ + where: whereCondition, + skip, + take: pageSize, + relations: ['author'], + }); + + const data = this.mapBlogResults(result); + const totalPages = Math.ceil(total / pageSize); + + return { + status_code: HttpStatus.OK, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: page, + totalPages, + totalResults: total, + blogs: data, + meta: { + hasNext: page < totalPages, + total, + nextPage: page < totalPages ? page + 1 : null, + prevPage: page > 1 ? page - 1 : null, + }, + }, + }; + } + private buildWhereClause(query: any): FindOptionsWhere { const where: FindOptionsWhere = {}; @@ -249,6 +310,7 @@ export class BlogService { image_urls: blog.image_urls, author: author_name, created_at: blog.created_at, + deletedAt: blog.deletedAt, }; }); } diff --git a/src/modules/blogs/dtos/blog-response.dto.ts b/src/modules/blogs/dtos/blog-response.dto.ts index 04814b643..4e17f23a0 100644 --- a/src/modules/blogs/dtos/blog-response.dto.ts +++ b/src/modules/blogs/dtos/blog-response.dto.ts @@ -21,4 +21,10 @@ export class BlogResponseDto { @ApiProperty({ description: 'The creation date of the blog' }) created_at: Date; + + @ApiProperty({ description: 'The published date of the blog' }) + published_date?: Date; + + @ApiProperty({ description: 'The deletion date of the blog', required: false }) + deletedAt?: Date; } diff --git a/src/modules/blogs/dtos/blog.dto.ts b/src/modules/blogs/dtos/blog.dto.ts index 8a7d914af..02950433c 100644 --- a/src/modules/blogs/dtos/blog.dto.ts +++ b/src/modules/blogs/dtos/blog.dto.ts @@ -20,5 +20,5 @@ export class BlogDto { author: string; @ApiProperty({ description: 'The creation date of the blog' }) - published_date: Date; + published_date?: Date; } diff --git a/src/modules/blogs/entities/blog.entity.ts b/src/modules/blogs/entities/blog.entity.ts index 354292ece..7afee40b5 100644 --- a/src/modules/blogs/entities/blog.entity.ts +++ b/src/modules/blogs/entities/blog.entity.ts @@ -1,4 +1,4 @@ -import { Entity, Column, ManyToOne } from 'typeorm'; +import { Entity, Column, ManyToOne, DeleteDateColumn, BeforeUpdate } from 'typeorm'; import { AbstractBaseEntity } from '../../../entities/base.entity'; import { User } from '../../user/entities/user.entity'; @@ -16,6 +16,12 @@ export class Blog extends AbstractBaseEntity { @Column('simple-array', { nullable: true }) image_urls?: string[]; + @DeleteDateColumn({ nullable: true, default: null }) + deletedAt?: Date; + + @Column({ nullable: true, default: null }) + published_date?: Date; + @ManyToOne(() => User, user => user.blogs) author: User; } diff --git a/src/modules/blogs/tests/blogs.service.spec.ts b/src/modules/blogs/tests/blogs.service.spec.ts index c11bf770a..5505c564f 100644 --- a/src/modules/blogs/tests/blogs.service.spec.ts +++ b/src/modules/blogs/tests/blogs.service.spec.ts @@ -3,7 +3,7 @@ import 'reflect-metadata'; import * as SYS_MSG from '@shared/constants/SystemMessages'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository, Like, MoreThanOrEqual } from 'typeorm'; +import { Repository, Like, MoreThanOrEqual, Not, IsNull } from 'typeorm'; import { Blog } from '../entities/blog.entity'; import { User } from '@modules/user/entities/user.entity'; import { BlogService } from '../blogs.service'; @@ -24,6 +24,7 @@ describe('BlogService', () => { findOneBy: jest.fn(), findOne: jest.fn(), remove: jest.fn(), + softRemove: jest.fn(), }); beforeEach(async () => { @@ -153,6 +154,7 @@ describe('BlogService', () => { image_urls: ['http://example.com/image.jpg'], author: 'John Doe', created_at: new Date('2023-01-01'), + deletedAt: undefined, }, ], meta: { @@ -243,7 +245,7 @@ describe('BlogService', () => { blog.tags = ['test']; blog.image_urls = ['http://example.com/image.jpg']; blog.created_at = new Date(); - blog.updated_at = new Date(); + blog.deletedAt = null; jest.spyOn(blogRepository, 'findOneBy').mockResolvedValue(blog); jest.spyOn(userRepository, 'findOne').mockResolvedValue(user); @@ -261,9 +263,11 @@ describe('BlogService', () => { image_urls: blog.image_urls, published_date: blog.created_at, author: 'John Doe', + created_at: blog.created_at, + deletedAt: blog.deletedAt, }, }); - expect(blogRepository.findOneBy).toHaveBeenCalledWith({ id: blogId }); + expect(blogRepository.findOneBy).toHaveBeenCalledWith({ id: blogId, deletedAt: null }); expect(userRepository.findOne).toHaveBeenCalledWith({ where: { id: user.id }, select: ['first_name', 'last_name'], @@ -290,12 +294,12 @@ describe('BlogService', () => { blog.id = 'blog-id'; jest.spyOn(blogRepository, 'findOne').mockResolvedValue(blog); - jest.spyOn(blogRepository, 'remove').mockResolvedValue(undefined); + jest.spyOn(blogRepository, 'softRemove').mockResolvedValue(undefined); await service.deleteBlogPost('blog-id'); expect(blogRepository.findOne).toHaveBeenCalledWith({ where: { id: 'blog-id' } }); - expect(blogRepository.remove).toHaveBeenCalledWith(blog); + expect(blogRepository.softRemove).toHaveBeenCalledWith(blog); }); it('should throw a 404 error if blog not found', async () => { @@ -304,4 +308,552 @@ describe('BlogService', () => { await expect(service.deleteBlogPost('blog-id')).rejects.toThrow('Blog post with this id does not exist'); }); }); + + describe('getAllBlogs', () => { + it('should return all blogs excluding deleted', async () => { + const blog = new Blog(); + blog.id = 'blog-id'; + blog.title = 'Test Blog'; + blog.content = 'Test Content'; + blog.tags = ['test']; + blog.image_urls = ['http://example.com/image.jpg']; + blog.author = new User(); + blog.author.first_name = 'John'; + blog.author.last_name = 'Doe'; + blog.created_at = new Date(); + blog.updated_at = new Date(); + + const expectedResponse = { + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 1, + totalResults: 1, + blogs: [ + { + blog_id: 'blog-id', + title: 'Test Blog', + content: 'Test Content', + tags: ['test'], + image_urls: ['http://example.com/image.jpg'], + author: 'John Doe', + created_at: blog.created_at, + deletedAt: undefined, + }, + ], + meta: { + hasNext: false, + total: 1, + nextPage: null, + prevPage: null, + }, + }, + }; + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog], 1]); + + const result = await service.getAllBlogs(1, 10); + + expect(result).toEqual(expectedResponse); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: null }, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + + it('should return all blogs including deleted', async () => { + const blog = new Blog(); + blog.id = 'blog-id'; + blog.title = 'Test Blog'; + blog.content = 'Test Content'; + blog.tags = ['test']; + blog.image_urls = ['http://example.com/image.jpg']; + blog.author = new User(); + blog.author.first_name = 'John'; + blog.author.last_name = 'Doe'; + blog.created_at = new Date(); + blog.updated_at = new Date(); + blog.deletedAt = new Date(); + + const expectedResponse = { + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 1, + totalResults: 1, + blogs: [ + { + blog_id: 'blog-id', + title: 'Test Blog', + content: 'Test Content', + tags: ['test'], + image_urls: ['http://example.com/image.jpg'], + author: 'John Doe', + created_at: blog.created_at, + deletedAt: blog.deletedAt, + }, + ], + meta: { + hasNext: false, + total: 1, + nextPage: null, + prevPage: null, + }, + }, + }; + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog], 1]); + + const result = await service.getAllBlogs(1, 10, true); + + expect(result).toEqual(expectedResponse); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: {}, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + + it('should return an empty list if no blogs are found', async () => { + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[], 0]); + + const result = await service.getAllBlogs(1, 10); + + expect(result).toEqual({ + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 0, + totalResults: 0, + blogs: [], + meta: { + hasNext: false, + total: 0, + nextPage: null, + prevPage: null, + }, + }, + }); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: null }, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + + it('should handle pagination correctly', async () => { + const blog = new Blog(); + blog.id = 'blog-id'; + blog.title = 'Test Blog'; + blog.content = 'Test Content'; + blog.tags = ['test']; + blog.image_urls = ['http://example.com/image.jpg']; + blog.author = new User(); + blog.author.first_name = 'John'; + blog.author.last_name = 'Doe'; + blog.created_at = new Date(); + blog.updated_at = new Date(); + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog], 1]); + + const result = await service.getAllBlogs(2, 1); + + expect(result).toEqual({ + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 2, + totalPages: 1, + totalResults: 1, + blogs: [ + { + blog_id: 'blog-id', + title: 'Test Blog', + content: 'Test Content', + tags: ['test'], + image_urls: ['http://example.com/image.jpg'], + author: 'John Doe', + created_at: blog.created_at, + deletedAt: undefined, + }, + ], + meta: { + hasNext: false, + total: 1, + nextPage: null, + prevPage: 1, + }, + }, + }); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: null }, + skip: 1, + take: 1, + relations: ['author'], + }); + }); + + it('should return all blogs excluding deleted with multiple mock data', async () => { + const blog1 = new Blog(); + blog1.id = 'blog-id-1'; + blog1.title = 'Test Blog 1'; + blog1.content = 'Test Content 1'; + blog1.tags = ['test1']; + blog1.image_urls = ['http://example.com/image1.jpg']; + blog1.author = new User(); + blog1.author.first_name = 'John'; + blog1.author.last_name = 'Doe'; + blog1.created_at = new Date(); + blog1.updated_at = new Date(); + blog1.deletedAt = null; + + const blog2 = new Blog(); + blog2.id = 'blog-id-2'; + blog2.title = 'Test Blog 2'; + blog2.content = 'Test Content 2'; + blog2.tags = ['test2']; + blog2.image_urls = ['http://example.com/image2.jpg']; + blog2.author = new User(); + blog2.author.first_name = 'Jane'; + blog2.author.last_name = 'Doe'; + blog2.created_at = new Date(); + blog2.updated_at = new Date(); + blog2.deletedAt = new Date(); + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog1, blog2], 2]); + + const result = await service.getAllBlogs(1, 10); + + expect(result).toEqual({ + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 1, + totalResults: 2, + blogs: [ + { + blog_id: 'blog-id-1', + title: 'Test Blog 1', + content: 'Test Content 1', + tags: ['test1'], + image_urls: ['http://example.com/image1.jpg'], + author: 'John Doe', + created_at: blog1.created_at, + }, + ], + meta: { + hasNext: false, + total: 2, + nextPage: null, + prevPage: null, + }, + }, + }); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: null }, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + + it('should return all blogs including deleted with multiple mock data', async () => { + const blog1 = new Blog(); + blog1.id = 'blog-id-1'; + blog1.title = 'Test Blog 1'; + blog1.content = 'Test Content 1'; + blog1.tags = ['test1']; + blog1.image_urls = ['http://example.com/image1.jpg']; + blog1.author = new User(); + blog1.author.first_name = 'John'; + blog1.author.last_name = 'Doe'; + blog1.created_at = new Date(); + blog1.updated_at = new Date(); + blog1.deletedAt = null; + + const blog2 = new Blog(); + blog2.id = 'blog-id-2'; + blog2.title = 'Test Blog 2'; + blog2.content = 'Test Content 2'; + blog2.tags = ['test2']; + blog2.image_urls = ['http://example.com/image2.jpg']; + blog2.author = new User(); + blog2.author.first_name = 'Jane'; + blog2.author.last_name = 'Doe'; + blog2.created_at = new Date(); + blog2.updated_at = new Date(); + blog2.deletedAt = new Date(); + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog1, blog2], 2]); + + const result = await service.getAllBlogs(1, 10, true); + + expect(result).toEqual({ + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 1, + totalResults: 2, + blogs: [ + { + blog_id: 'blog-id-1', + title: 'Test Blog 1', + content: 'Test Content 1', + tags: ['test1'], + image_urls: ['http://example.com/image1.jpg'], + author: 'John Doe', + created_at: blog1.created_at, + deletedAt: null, + }, + { + blog_id: 'blog-id-2', + title: 'Test Blog 2', + content: 'Test Content 2', + tags: ['test2'], + image_urls: ['http://example.com/image2.jpg'], + author: 'Jane Doe', + created_at: blog2.created_at, + deletedAt: blog2.deletedAt, + }, + ], + meta: { + hasNext: false, + total: 2, + nextPage: null, + prevPage: null, + }, + }, + }); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: {}, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + }); + + describe('getDeletedBlogs', () => { + it('should return all deleted blogs', async () => { + const blog = new Blog(); + blog.id = 'blog-id'; + blog.title = 'Test Blog'; + blog.content = 'Test Content'; + blog.tags = ['test']; + blog.image_urls = ['http://example.com/image.jpg']; + blog.author = new User(); + blog.author.first_name = 'John'; + blog.author.last_name = 'Doe'; + blog.created_at = new Date(); + blog.updated_at = new Date(); + blog.deletedAt = new Date(); + + const expectedResponse = { + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 1, + totalResults: 1, + blogs: [ + { + blog_id: 'blog-id', + title: 'Test Blog', + content: 'Test Content', + tags: ['test'], + image_urls: ['http://example.com/image.jpg'], + author: 'John Doe', + created_at: blog.created_at, + deletedAt: blog.deletedAt, + }, + ], + meta: { + hasNext: false, + total: 1, + nextPage: null, + prevPage: null, + }, + }, + }; + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog], 1]); + + const result = await service.getDeletedBlogs(1, 10); + + expect(result).toEqual(expectedResponse); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: Not(IsNull()) }, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + + it('should return an empty list if no deleted blogs are found', async () => { + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[], 0]); + + const result = await service.getDeletedBlogs(1, 10); + + expect(result).toEqual({ + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 0, + totalResults: 0, + blogs: [], + meta: { + hasNext: false, + total: 0, + nextPage: null, + prevPage: null, + }, + }, + }); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: Not(IsNull()) }, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + + it('should handle pagination correctly for deleted blogs', async () => { + const blog = new Blog(); + blog.id = 'blog-id'; + blog.title = 'Test Blog'; + blog.content = 'Test Content'; + blog.tags = ['test']; + blog.image_urls = ['http://example.com/image.jpg']; + blog.author = new User(); + blog.author.first_name = 'John'; + blog.author.last_name = 'Doe'; + blog.created_at = new Date(); + blog.updated_at = new Date(); + blog.deletedAt = new Date(); + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog], 1]); + + const result = await service.getDeletedBlogs(2, 1); + + expect(result).toEqual({ + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 2, + totalPages: 1, + totalResults: 1, + blogs: [ + { + blog_id: 'blog-id', + title: 'Test Blog', + content: 'Test Content', + tags: ['test'], + image_urls: ['http://example.com/image.jpg'], + author: 'John Doe', + created_at: blog.created_at, + deletedAt: blog.deletedAt, + }, + ], + meta: { + hasNext: false, + total: 1, + nextPage: null, + prevPage: 1, + }, + }, + }); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: Not(IsNull()) }, + skip: 1, + take: 1, + relations: ['author'], + }); + }); + + it('should return all deleted blogs with multiple mock data', async () => { + const blog1 = new Blog(); + blog1.id = 'blog-id-1'; + blog1.title = 'Test Blog 1'; + blog1.content = 'Test Content 1'; + blog1.tags = ['test1']; + blog1.image_urls = ['http://example.com/image1.jpg']; + blog1.author = new User(); + blog1.author.first_name = 'John'; + blog1.author.last_name = 'Doe'; + blog1.created_at = new Date(); + blog1.updated_at = new Date(); + blog1.deletedAt = new Date(); + + const blog2 = new Blog(); + blog2.id = 'blog-id-2'; + blog2.title = 'Test Blog 2'; + blog2.content = 'Test Content 2'; + blog2.tags = ['test2']; + blog2.image_urls = ['http://example.com/image2.jpg']; + blog2.author = new User(); + blog2.author.first_name = 'Jane'; + blog2.author.last_name = 'Doe'; + blog2.created_at = new Date(); + blog2.updated_at = new Date(); + blog2.deletedAt = new Date(); + + jest.spyOn(blogRepository, 'findAndCount').mockResolvedValue([[blog1, blog2], 2]); + + const result = await service.getDeletedBlogs(1, 10); + + expect(result).toEqual({ + status_code: 200, + message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL, + data: { + currentPage: 1, + totalPages: 1, + totalResults: 2, + blogs: [ + { + blog_id: 'blog-id-1', + title: 'Test Blog 1', + content: 'Test Content 1', + tags: ['test1'], + image_urls: ['http://example.com/image1.jpg'], + author: 'John Doe', + created_at: blog1.created_at, + deletedAt: blog1.deletedAt, + }, + { + blog_id: 'blog-id-2', + title: 'Test Blog 2', + content: 'Test Content 2', + tags: ['test2'], + image_urls: ['http://example.com/image2.jpg'], + author: 'Jane Doe', + created_at: blog2.created_at, + deletedAt: blog2.deletedAt, + }, + ], + meta: { + hasNext: false, + total: 2, + nextPage: null, + prevPage: null, + }, + }, + }); + expect(blogRepository.findAndCount).toHaveBeenCalledWith({ + where: { deletedAt: Not(IsNull()) }, + skip: 0, + take: 10, + relations: ['author'], + }); + }); + }); });