Repository: hngprojects/hng_boilerplate_nestjs

Issue:
[FEAT]  Implement Endpoint for User Language Deletion
## Description
To create a DELETE API endpoint that allows users to delete a specific User language .

## Acceptance Criteria
1. The endpoint successfully deletes a specific user language and returns a successful status when Language  is deleted
2. A Bad Request status is returned if the user language cannot be deleted due to dependencies, with a detailed error message.

### Purpose
Enable users to delete a specific language from the system by its unique ID.

### Requirements	
1.  Develop the DELETE endpoint.
2.  Validate that the user language ID provided exists in the system and confirm that the requester has sufficient permissions to perform the deletion.
4. Check for any dependencies or related records before deleting the user language.
5. Confirm successful deletion, handle non-existent IDs or dependency constraints, and internal errors with appropriate error messages

### Expected Outcome

1. The endpoint will be secure, efficient, and thoroughly tested to meet the outlined requirements.
2. The DELETE endpoint will successfully delete the specified resource and return a confirmation message with an OK status.
3. Proper handling of cases where the user language ID is not found or dependencies prevent deletion, with appropriate status codes and messages.

### 


Discussion:
@Bethislearning: https://github.com/hngprojects/hng_boilerplate_csharp_web/pull/345

Solution patch:
diff --git a/.gitignore b/.gitignore
index 550356a9..0a7f13b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,11 @@ fabric.properties
 ##
 ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 
+
+# DB
+data/db/
+
+docker-compose.yml
 # User-specific files
 *.suo
 *.user
diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts
index 98bd7151..9da65d5f 100644
--- a/src/modules/auth/auth.service.ts
+++ b/src/modules/auth/auth.service.ts
@@ -42,12 +42,10 @@ export default class AuthenticationService {
         identifierType: 'email',
       });
 
-      console.log('userExists', userExists);
       if (userExists) {
         throw new CustomHttpException(SYS_MSG.USER_ACCOUNT_EXIST, HttpStatus.BAD_REQUEST);
       }
 
-      console.log('createUserDto', createUserDto);
       const user = await this.userService.createUser(createUserDto, manager);
 
       if (!user) {
diff --git a/src/modules/comments/dto/add-comment.dto.ts b/src/modules/comments/dtos/add-comment.dto.ts
similarity index 100%
rename from src/modules/comments/dto/add-comment.dto.ts
rename to src/modules/comments/dtos/add-comment.dto.ts
diff --git a/src/modules/comments/entities/comment.entity.ts b/src/modules/comments/entities/comment.entity.ts
deleted file mode 100644
index 830fd264..00000000
--- a/src/modules/comments/entities/comment.entity.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { AbstractBaseEntity } from '../../../entities/base.entity';
-import { Entity, Column, ManyToOne } from 'typeorm';
-import { User } from '../../user/entities/user.entity';
-
-@Entity()
-export class Comment extends AbstractBaseEntity {
-  @Column({ nullable: false })
-  model_id: string;
-
-  @Column()
-  model_type: string;
-
-  @Column()
-  content: string;
-
-  @ManyToOne(() => User, user => user.comments)
-  user: User;
-}
diff --git a/src/modules/comments/entities/comments.entity.ts b/src/modules/comments/entities/comments.entity.ts
index 1ecbf0ef..8d4f2261 100644
--- a/src/modules/comments/entities/comments.entity.ts
+++ b/src/modules/comments/entities/comments.entity.ts
@@ -14,9 +14,9 @@ export class Comment extends AbstractBaseEntity {
   @ManyToOne(() => User, user => user.comments, { cascade: true })
   user: User;
 
-  @Column({ nullable: false })
+  @Column({ nullable: true })
   model_id: string;
 
-  @Column()
+  @Column({ nullable: true })
   model_type: string;
 }
diff --git a/src/modules/email/email.consumer.ts b/src/modules/email/email.consumer.ts
index c6d401b0..f025a9ac 100644
--- a/src/modules/email/email.consumer.ts
+++ b/src/modules/email/email.consumer.ts
@@ -96,7 +96,7 @@ export default class EmailQueueConsumer {
     }
   }
 
-  @Process('in-app-notification')
+  @Process('login-otp')
   async sendLoginOtpEmailJob(job: Job<MailInterface>) {
     try {
       const {
@@ -113,7 +113,7 @@ export default class EmailQueueConsumer {
     }
   }
 
-  @Process('login-otp')
+  @Process('in-app-notification')
   async sendNotificationMail(job: Job<MailInterface>) {
     try {
       const {
diff --git a/src/modules/email/hng-templates/login-otp.hbs b/src/modules/email/hng-templates/login-otp.hbs
index 3dd04aea..f69e6ea2 100644
--- a/src/modules/email/hng-templates/login-otp.hbs
+++ b/src/modules/email/hng-templates/login-otp.hbs
@@ -1,20 +1,71 @@
 <html lang='en'><head><meta charset='UTF-8' /><meta
       name='viewport'
       content='width=device-width, initial-scale=1.0'
-    /><title>Email Confirmation</title><style>body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin:
-      0; padding: 0; } .container { width: 100%; max-width: 600px; margin: 0 auto; background-color: #ffffff; padding:
-      20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } .header { text-align: center; padding-bottom:
-      20px; } .header img { width: 120px; } .content { text-align: center; padding: 20px; } .content h1 { color: #333;
-      font-size: 24px; margin-bottom: 15px; } .content p { color: #666; font-size: 16px; line-height: 1.5;
-      margin-bottom: 20px; } .btn { display: inline-block; padding: 12px 25px; color: #ffffff; background-color:
-      #28a745; text-decoration: none; border-radius: 5px; font-weight: bold; font-size: 16px; margin-top: 20px; }
-      .footer { text-align: center; padding-top: 20px; font-size: 12px; color: #888888; } .footer a { color: #28a745;
-      text-decoration: none; }</style></head><body><div class='container'><div class='header'><img
+    /><title>Email Confirmation</title><style>
+      body {
+        font-family: Arial, sans-serif;
+        background-color: #f4f4f4;
+        margin: 0;
+        padding: 0;
+      }
+      .container {
+        width: 100%;
+        max-width: 600px;
+        margin: 0 auto;
+        background-color: #ffffff;
+        padding: 20px;
+        border-radius: 8px;
+        box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+      }
+      .header {
+        text-align: center;
+        padding-bottom: 20px;
+      }
+      .header img {
+        width: 120px;
+      }
+      .content {
+        text-align: center;
+        padding: 20px;
+      }
+      .content h1 {
+        color: #333;
+        font-size: 24px;
+        margin-bottom: 15px;
+      }
+      .content p {
+        color: #666;
+        font-size: 16px;
+        line-height: 1.5;
+        margin-bottom: 20px;
+      }
+      .btn {
+        display: inline-block;
+        padding: 12px 25px;
+        color: #ffffff;
+        background-color: #28a745;
+        text-decoration: none;
+        border-radius: 5px;
+        font-weight: bold;
+        font-size: 16px;
+        margin-top: 20px;
+      }
+      .footer {
+        text-align: center;
+        padding-top: 20px;
+        font-size: 12px;
+        color: #888888;
+      }
+      .footer a {
+        color: #28a745;
+        text-decoration: none;
+      }
+    </style></head><body><div class='container'><div class='header'><img
           src='https://www.shutterstock.com/image-vector/circle-line-simple-design-logo-600nw-2174926871.jpg'
           alt='Company Logo'
         /></div><div class='content'><h1>Confirm Your Email Address</h1><p>Hello {{email}}</p><p>Thank you for
           registering. Please confirm your email address by entering the otp:
-          <strong>{{otp}}</strong></p><p>If you didn't create an account with us, you can safely ignore this email.</p></div><div
+          <strong>{{token}}</strong></p><p>If you didn't create an account with us, you can safely ignore this email.</p></div><div
         class='footer'
       ><p>&copy; 2024 Company Name. All rights reserved.</p><p><a
             href='https://example.com/unsubscribe'
diff --git a/src/modules/languages/languages.controller.ts b/src/modules/languages/languages.controller.ts
index f3471d2a..63e81eec 100644
--- a/src/modules/languages/languages.controller.ts
+++ b/src/modules/languages/languages.controller.ts
@@ -1,4 +1,19 @@
-import { Controller, Post, Body, Get, Patch, Param, Res, Request, Response, UseGuards } from '@nestjs/common';
+import {
+  Controller,
+  Post,
+  Body,
+  Get,
+  Patch,
+  Param,
+  Res,
+  Request,
+  Response,
+  UseGuards,
+  BadRequestException,
+  Delete,
+  HttpCode,
+  HttpStatus,
+} from '@nestjs/common';
 import { ApiTags, ApiOperation, ApiResponse, ApiBody, ApiBearerAuth } from '@nestjs/swagger';
 import { CreateLanguageDto, UpdateLanguageDto } from './dto/create-language.dto';
 import { LanguagesService } from './languages.service';
@@ -8,71 +23,88 @@ import { NotFoundException, UnauthorizedException } from '@nestjs/common';
 @ApiTags('Languages')
 @Controller('languages')
 export class LanguagesController {
-    constructor(private readonly languagesService: LanguagesService) { }
+  constructor(private readonly languagesService: LanguagesService) {}
 
-    @Post()
-    @ApiBearerAuth()
-    @ApiOperation({ summary: 'Create a new language' })
-    @ApiBody({ type: CreateLanguageDto })
-    @ApiResponse({ status: 201, description: 'Language successfully created.' })
-    @ApiResponse({ status: 409, description: 'Language already exists.' })
-    @ApiResponse({ status: 401, description: 'Unauthorized' })
-    async createLanguage(@Body() createLanguageDto: CreateLanguageDto) {
-        return this.languagesService.createLanguage(createLanguageDto);
-    }
+  @Post()
+  @ApiBearerAuth()
+  @ApiOperation({ summary: 'Create a new language' })
+  @ApiBody({ type: CreateLanguageDto })
+  @ApiResponse({ status: 201, description: 'Language successfully created.' })
+  @ApiResponse({ status: 409, description: 'Language already exists.' })
+  @ApiResponse({ status: 401, description: 'Unauthorized' })
+  async createLanguage(@Body() createLanguageDto: CreateLanguageDto) {
+    return this.languagesService.createLanguage(createLanguageDto);
+  }
 
-    @Get()
-    @ApiBearerAuth()
-    @ApiOperation({ summary: 'Get all supported languages' })
-    @ApiResponse({ status: 200, description: 'List of supported languages.' })
-    @ApiResponse({ status: 401, description: 'Unauthorized' })
-    async getLanguages(@Response() response): Promise<any> {
-        const result = await this.languagesService.getSupportedLanguages();
-        return response.status(result.status_code).json(result);
-    }
+  @Get()
+  @ApiBearerAuth()
+  @ApiOperation({ summary: 'Get all supported languages' })
+  @ApiResponse({ status: 200, description: 'List of supported languages.' })
+  @ApiResponse({ status: 401, description: 'Unauthorized' })
+  async getLanguages(@Response() response): Promise<any> {
+    const result = await this.languagesService.getSupportedLanguages();
+    return response.status(result.status_code).json(result);
+  }
 
-    @Get(':id')
-    @ApiBearerAuth()
-    @ApiOperation({ summary: 'Get all languages from a userId' })
-    @ApiResponse({ status: 200, description: 'List of  languages by the user.' })
-    @ApiResponse({ status: 401, description: 'Unauthorized' })
-    @ApiResponse({ status: 400, description: 'Invalid UserId' })
-    @ApiResponse({ status: 404, description: 'No languages found for the specified user' })
-    @ApiResponse({ status: 500, description: 'Internal server error' })
-    async getLanguagesByUserId(@Param('id') id: string, @Request() req, @Response() response): Promise<any> {
-        const result = await this.languagesService.getLanguagesById(id, req.user);
-        return response.status(result.status_code).json(result);
-    }
+  @Get(':id')
+  @ApiBearerAuth()
+  @ApiOperation({ summary: 'Get all languages from a userId' })
+  @ApiResponse({ status: 200, description: 'List of  languages by the user.' })
+  @ApiResponse({ status: 401, description: 'Unauthorized' })
+  @ApiResponse({ status: 400, description: 'Invalid UserId' })
+  @ApiResponse({ status: 404, description: 'No languages found for the specified user' })
+  @ApiResponse({ status: 500, description: 'Internal server error' })
+  async getLanguagesByUserId(@Param('id') id: string, @Request() req, @Response() response): Promise<any> {
+    const result = await this.languagesService.getLanguagesById(id, req.user);
+    return response.status(result.status_code).json(result);
+  }
 
-    @Patch(':id')
-    @ApiBearerAuth()
-    @ApiOperation({ summary: 'Update a language' })
-    @ApiBody({ type: UpdateLanguageDto })
-    @ApiResponse({ status: 200, description: 'Language successfully updated.' })
-    @ApiResponse({ status: 404, description: 'Language not found.' })
-    @ApiResponse({ status: 401, description: 'Unauthorized' })
-    async updateLanguage(@Param('id') id: string, @Body() updateLanguageDto: UpdateLanguageDto) {
-        return this.languagesService.updateLanguage(id, updateLanguageDto);
+  @Patch(':id')
+  @ApiBearerAuth()
+  @ApiOperation({ summary: 'Update a language' })
+  @ApiBody({ type: UpdateLanguageDto })
+  @ApiResponse({ status: 200, description: 'Language successfully updated.' })
+  @ApiResponse({ status: 404, description: 'Language not found.' })
+  @ApiResponse({ status: 401, description: 'Unauthorized' })
+  async updateLanguage(@Param('id') id: string, @Body() updateLanguageDto: UpdateLanguageDto) {
+    return this.languagesService.updateLanguage(id, updateLanguageDto);
+  }
+
+  @ApiBearerAuth()
+  @UseGuards(AuthGuard)
+  @ApiOperation({ summary: 'Get all languages by User ID' })
+  @ApiResponse({ status: 200, description: 'Returns languages associated with user.' })
+  @ApiResponse({ status: 404, description: 'User not found or no languages available.' })
+  @ApiResponse({ status: 401, description: 'Unauthorized access.' })
+  @Get(':id/languages')
+  async getUserLanguages(@Param('id') userId: string) {
+    if (!userId) {
+      throw new UnauthorizedException('Invalid user ID.');
     }
 
-    @ApiBearerAuth()
-    @UseGuards(AuthGuard)
-    @ApiOperation({ summary: 'Get all languages by User ID' })
-    @ApiResponse({ status: 200, description: 'Returns languages associated with user.' })
-    @ApiResponse({ status: 404, description: 'User not found or no languages available.' })
-    @ApiResponse({ status: 401, description: 'Unauthorized access.' })
-    @Get(':id/languages')
-    async getUserLanguages(@Param('id') userId: string) {
-        if (!userId) {
-            throw new UnauthorizedException('Invalid user ID.');
-        }
+    const languages = await this.languagesService.getUserLanguages(userId);
 
-        const languages = await this.languagesService.getUserLanguages(userId);
+    if (!languages.length) {
+      throw new NotFoundException('No languages found for this user.');
+    }
 
-        if (!languages.length) {
-            throw new NotFoundException('No languages found for this user.');
-        }
+    return { status: 200, data: languages };
+  }
 
-        return { status: 200, data: languages };
+  @Delete(':id')
+  @ApiBearerAuth()
+  @UseGuards(AuthGuard)
+  @ApiOperation({ summary: 'Delete a specific user language by ID' })
+  @ApiResponse({ status: 200, description: 'Language successfully deleted for the user.' })
+  @ApiResponse({ status: 400, description: 'Cannot delete language due to dependencies.' })
+  @ApiResponse({ status: 404, description: 'Language not found for the user.' })
+  @ApiResponse({ status: 401, description: 'Unauthorized access.' })
+  @ApiResponse({ status: 403, description: 'User not authorized to delete this language.' })
+  @HttpCode(HttpStatus.OK)
+  async deleteUserLanguage(@Param('id') languageId: string, @Request() req) {
+    if (!languageId) {
+      throw new BadRequestException('Invalid language ID provided.');
     }
+    return await this.languagesService.deleteUserLanguage(languageId, req.user.id);
+  }
 }
diff --git a/src/modules/languages/languages.service.ts b/src/modules/languages/languages.service.ts
index 0155f2aa..c126dac9 100644
--- a/src/modules/languages/languages.service.ts
+++ b/src/modules/languages/languages.service.ts
@@ -1,12 +1,12 @@
 import {
-    BadRequestException,
-    ConflictException,
-    ForbiddenException,
-    HttpStatus,
-    Injectable,
-    InternalServerErrorException,
-    Logger,
-    NotFoundException,
+  BadRequestException,
+  ConflictException,
+  ForbiddenException,
+  HttpStatus,
+  Injectable,
+  InternalServerErrorException,
+  Logger,
+  NotFoundException,
 } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
@@ -17,153 +17,172 @@ import { isUUID } from 'class-validator';
 
 @Injectable()
 export class LanguagesService {
-    constructor(
-        @InjectRepository(Language)
-        private readonly languageRepository: Repository<Language>,
-        @InjectRepository(User)
-        private readonly userRepository: Repository<User>,
-    ) { }
-
-    async createLanguage(createLanguageDto: CreateLanguageDto): Promise<any> {
-        try {
-            const languageExists = await this.languageRepository.findOne({
-                where: { language: createLanguageDto.language },
-            });
-
-            if (languageExists) {
-                throw new ConflictException({
-                    status_code: HttpStatus.CONFLICT,
-                    message: 'Language already exists',
-                });
-            }
-
-            const newLanguage = this.languageRepository.create(createLanguageDto);
-            await this.languageRepository.save(newLanguage);
-
-            return {
-                status_code: HttpStatus.CREATED,
-                message: 'Language Created Successfully',
-                language: newLanguage,
-            };
-        } catch (error) {
-            if (error instanceof ConflictException) {
-                throw error;
-            }
-            Logger.error('LanguagesServiceError ~ createLanguage ~', error);
-            throw new InternalServerErrorException({
-                message: 'An error occurred',
-                status_code: HttpStatus.INTERNAL_SERVER_ERROR,
-            });
-        }
+  constructor(
+    @InjectRepository(Language)
+    private readonly languageRepository: Repository<Language>,
+    @InjectRepository(User)
+    private readonly userRepository: Repository<User>
+  ) {}
+
+  async createLanguage(createLanguageDto: CreateLanguageDto): Promise<any> {
+    try {
+      const languageExists = await this.languageRepository.findOne({
+        where: { language: createLanguageDto.language },
+      });
+
+      if (languageExists) {
+        throw new ConflictException({
+          status_code: HttpStatus.CONFLICT,
+          message: 'Language already exists',
+        });
+      }
+
+      const newLanguage = this.languageRepository.create(createLanguageDto);
+      await this.languageRepository.save(newLanguage);
+
+      return {
+        status_code: HttpStatus.CREATED,
+        message: 'Language Created Successfully',
+        language: newLanguage,
+      };
+    } catch (error) {
+      if (error instanceof ConflictException) {
+        throw error;
+      }
+      Logger.error('LanguagesServiceError ~ createLanguage ~', error);
+      throw new InternalServerErrorException({
+        message: 'An error occurred',
+        status_code: HttpStatus.INTERNAL_SERVER_ERROR,
+      });
+    }
+  }
+
+  async getSupportedLanguages(): Promise<any> {
+    try {
+      const languages = await this.languageRepository.find();
+      const formattedLanguages = languages.map(language => ({
+        language: `${language.language} (${language.description})`,
+      }));
+      return {
+        status_code: HttpStatus.OK,
+        message: 'Languages fetched successfully',
+        languages: formattedLanguages,
+      };
+    } catch (error) {
+      Logger.error('LanguagesServiceError ~ fetchLanguages ~', error);
+      throw new InternalServerErrorException({
+        message: 'An error occurred',
+        status_code: HttpStatus.INTERNAL_SERVER_ERROR,
+      });
+    }
+  }
+
+  async updateLanguage(id: string, updateLanguageDto: UpdateLanguageDto): Promise<any> {
+    try {
+      const language = await this.languageRepository.findOne({ where: { id } });
+      if (!language) {
+        throw new NotFoundException({
+          status_code: HttpStatus.NOT_FOUND,
+          message: 'Language not found',
+        });
+      }
+
+      Object.assign(language, updateLanguageDto);
+      await this.languageRepository.save(language);
+
+      return {
+        status_code: HttpStatus.OK,
+        message: 'Language successfully updated',
+        language,
+      };
+    } catch (error) {
+      if (error instanceof NotFoundException) {
+        throw error;
+      }
+      Logger.error('LanguagesServiceError ~ updateLanguage ~', error);
+      throw new InternalServerErrorException({
+        message: 'An error occurred',
+        status_code: HttpStatus.INTERNAL_SERVER_ERROR,
+      });
     }
+  }
+  async getLanguagesById(userId: string, user: User): Promise<any> {
+    try {
+      if (!isUUID(userId)) {
+        throw new BadRequestException('Invalid user Id');
+      }
+
+      if (user.id !== userId) {
+        throw new ForbiddenException({
+          status_code: HttpStatus.FORBIDDEN,
+          message: 'You are not authorized to access this resource',
+        });
+      }
+
+      const languages = await this.languageRepository
+        .createQueryBuilder('language')
+        .innerJoin('language.users', 'user')
+        .where('user.id = :userId', { userId })
+        .getMany();
 
-    async getSupportedLanguages(): Promise<any> {
-        try {
-            const languages = await this.languageRepository.find();
-            const formattedLanguages = languages.map(language => ({
-                language: `${language.language} (${language.description})`,
-            }));
-            return {
-                status_code: HttpStatus.OK,
-                message: 'Languages fetched successfully',
-                languages: formattedLanguages,
-            };
-        } catch (error) {
-            Logger.error('LanguagesServiceError ~ fetchLanguages ~', error);
-            throw new InternalServerErrorException({
-                message: 'An error occurred',
-                status_code: HttpStatus.INTERNAL_SERVER_ERROR,
-            });
-        }
+      if (!languages || languages.length === 0) {
+        throw new NotFoundException({
+          status_code: HttpStatus.NOT_FOUND,
+          message: 'Languages associated with this user not found',
+        });
+      }
+
+      const formattedLanguages = languages.map(language => ({
+        id: language.id,
+        language: language.language,
+        description: language.description,
+        code: language.code,
+      }));
+
+      return {
+        status: 'OK',
+        status_code: HttpStatus.OK,
+        message: 'Languages fetched successfully',
+        data: formattedLanguages,
+      };
+    } catch (error) {
+      if (
+        error instanceof NotFoundException ||
+        error instanceof ForbiddenException ||
+        error instanceof BadRequestException
+      ) {
+        throw error;
+      }
+      Logger.error('LanguagesServiceError ~ getLanguagesById ~', error);
+      throw new InternalServerErrorException({
+        message: 'An error occurred',
+        status_code: HttpStatus.INTERNAL_SERVER_ERROR,
+      });
     }
+  }
 
-    async updateLanguage(id: string, updateLanguageDto: UpdateLanguageDto): Promise<any> {
-        try {
-            const language = await this.languageRepository.findOne({ where: { id } });
-            if (!language) {
-                throw new NotFoundException({
-                    status_code: HttpStatus.NOT_FOUND,
-                    message: 'Language not found',
-                });
-            }
-
-            Object.assign(language, updateLanguageDto);
-            await this.languageRepository.save(language);
-
-            return {
-                status_code: HttpStatus.OK,
-                message: 'Language successfully updated',
-                language,
-            };
-        } catch (error) {
-            if (error instanceof NotFoundException) {
-                throw error;
-            }
-            Logger.error('LanguagesServiceError ~ updateLanguage ~', error);
-            throw new InternalServerErrorException({
-                message: 'An error occurred',
-                status_code: HttpStatus.INTERNAL_SERVER_ERROR,
-            });
-        }
+  async getUserLanguages(userId: string): Promise<Language[]> {
+    const user = await this.userRepository.findOne({ where: { id: userId }, relations: ['languages'] });
+    if (!user) throw new NotFoundException('User not found.');
+    return user.languages || [];
+  }
+
+  async deleteUserLanguage(languageId: string, userId: string): Promise<{ message: string }> {
+    const user = await this.userRepository.findOne({ where: { id: userId }, relations: ['languages'] });
+    if (!user) {
+      throw new NotFoundException('User not found.');
     }
-    async getLanguagesById(userId: string, user: User): Promise<any> {
-        try {
-            if (!isUUID(userId)) {
-                throw new BadRequestException('Invalid user Id');
-            }
-
-            if (user.id !== userId) {
-                throw new ForbiddenException({
-                    status_code: HttpStatus.FORBIDDEN,
-                    message: 'You are not authorized to access this resource',
-                });
-            }
-
-            const languages = await this.languageRepository
-                .createQueryBuilder('language')
-                .innerJoin('language.users', 'user')
-                .where('user.id = :userId', { userId })
-                .getMany();
-
-            if (!languages || languages.length === 0) {
-                throw new NotFoundException({
-                    status_code: HttpStatus.NOT_FOUND,
-                    message: 'Languages associated with this user not found',
-                });
-            }
-
-            const formattedLanguages = languages.map(language => ({
-                id: language.id,
-                language: language.language,
-                description: language.description,
-                code: language.code,
-            }));
-
-            return {
-                status: 'OK',
-                status_code: HttpStatus.OK,
-                message: 'Languages fetched successfully',
-                data: formattedLanguages,
-            };
-        } catch (error) {
-            if (
-                error instanceof NotFoundException ||
-                error instanceof ForbiddenException ||
-                error instanceof BadRequestException
-            ) {
-                throw error;
-            }
-            Logger.error('LanguagesServiceError ~ getLanguagesById ~', error);
-            throw new InternalServerErrorException({
-                message: 'An error occurred',
-                status_code: HttpStatus.INTERNAL_SERVER_ERROR,
-            });
-        }
+
+    const language = user.languages.find(lang => lang.id === languageId);
+    if (!language) {
+      throw new NotFoundException('Language not found for this user.');
     }
 
-    async getUserLanguages(userId: string): Promise<Language[]> {
-        const user = await this.userRepository.findOne({ where: { id: userId }, relations: ['languages'] });
-        if (!user) throw new NotFoundException('User not found.');
-        return user.languages || [];
+    try {
+      await this.languageRepository.remove(language);
+      return { message: 'Language successfully deleted for the user.' };
+    } catch (error) {
+      throw new BadRequestException('Cannot delete language due to dependencies.');
     }
+  }
 }
diff --git a/src/modules/newsletter-subscription/dto/resubscribe-newsletter.dto.ts b/src/modules/newsletter-subscription/dto/resubscribe-newsletter.dto.ts
new file mode 100644
index 00000000..5ea5620a
--- /dev/null
+++ b/src/modules/newsletter-subscription/dto/resubscribe-newsletter.dto.ts
@@ -0,0 +1,18 @@
+import { IsEmail, IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
+
+export class ResubscribeNewsletterDto {
+  @IsOptional()
+  @IsUUID()
+  id?: string;
+
+  @IsOptional()
+  @IsEmail()
+  email?: string;
+
+  @IsNotEmpty()
+  validateUserIdentifier?() {
+    if (!this.id && !this.email) {
+      throw new Error('Either userId or email must be provided.');
+    }
+  }
+}
diff --git a/src/modules/newsletter-subscription/entities/newsletter-subscription.entity.ts b/src/modules/newsletter-subscription/entities/newsletter-subscription.entity.ts
index 3ec042d9..4c07fa55 100644
--- a/src/modules/newsletter-subscription/entities/newsletter-subscription.entity.ts
+++ b/src/modules/newsletter-subscription/entities/newsletter-subscription.entity.ts
@@ -1,14 +1,26 @@
 import { AbstractBaseEntity } from '../../../entities/base.entity';
-import { Column, DeleteDateColumn, Entity } from 'typeorm';
+import { Column, DeleteDateColumn, Entity, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
 
 @Entity('newsletters')
 export class NewsletterSubscription extends AbstractBaseEntity {
-  @Column({ unique: true })
+  @PrimaryGeneratedColumn('uuid')
+  id: string;
+
+  @Column({ unique: true, nullable: true })
   email: string;
 
+  @CreateDateColumn()
+  createdAt: Date;
+
+  @UpdateDateColumn()
+  updatedAt: Date;
+
   @DeleteDateColumn()
   deletedAt: Date;
 
   @Column({ default: false })
   isUnsubscribed: boolean;
+
+  @Column({ default: 'inactive' }) // inactive, active, unsubscribed
+  status: string;
 }
diff --git a/src/modules/newsletter-subscription/newsletter-subscription.controller.ts b/src/modules/newsletter-subscription/newsletter-subscription.controller.ts
index 2cfcb2f6..5a56ceba 100644
--- a/src/modules/newsletter-subscription/newsletter-subscription.controller.ts
+++ b/src/modules/newsletter-subscription/newsletter-subscription.controller.ts
@@ -6,6 +6,7 @@ import { ApiBearerAuth, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@ne
 import { NewsletterSubscriptionResponseDto } from './dto/newsletter-subscription.response.dto';
 import { SuperAdminGuard } from '@guards/super-admin.guard';
 import { UnsubscribeNewsletterDto } from './dto/unsubscribe-newsletter.dto';
+import { ResubscribeNewsletterDto } from './dto/resubscribe-newsletter.dto';
 
 @ApiTags('Newsletter Subscription')
 @Controller('newsletter-subscription')
@@ -21,6 +22,17 @@ export class NewsletterSubscriptionController {
     return this.newsletterSubscriptionService.newsletterSubscription(createNewsletterDto);
   }
 
+  @skipAuth()
+  @Post('resubscribe')
+  @HttpCode(HttpStatus.OK)
+  @ApiOperation({ summary: 'Resubscribe to the newsletter' })
+  @ApiResponse({ status: 200, description: 'User successfully resubscribed.' })
+  @ApiResponse({ status: 400, description: 'User is already subscribed.' })
+  @ApiResponse({ status: 404, description: 'User not found or not unsubscribed.' })
+  resubscribe(@Body() resubscribeDto: ResubscribeNewsletterDto) {
+    return this.newsletterSubscriptionService.resubscribe(resubscribeDto);
+  }
+
   @ApiBearerAuth()
   @UseGuards(SuperAdminGuard)
   @Get()
diff --git a/src/modules/newsletter-subscription/newsletter-subscription.service.ts b/src/modules/newsletter-subscription/newsletter-subscription.service.ts
index cf44397d..982486da 100644
--- a/src/modules/newsletter-subscription/newsletter-subscription.service.ts
+++ b/src/modules/newsletter-subscription/newsletter-subscription.service.ts
@@ -1,9 +1,10 @@
-import { Injectable, NotFoundException } from '@nestjs/common';
+import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { IsNull, Not, Repository } from 'typeorm';
 import { CreateNewsletterSubscriptionDto } from './dto/create-newsletter-subscription.dto';
 import { NewsletterSubscriptionResponseDto } from './dto/newsletter-subscription.response.dto';
 import { NewsletterSubscription } from './entities/newsletter-subscription.entity';
+import { ResubscribeNewsletterDto } from './dto/resubscribe-newsletter.dto';
 
 @Injectable()
 export class NewsletterSubscriptionService {
@@ -85,4 +86,27 @@ export class NewsletterSubscriptionService {
 
     return { message: `Email ${email} has been unsubscribed successfully` };
   }
+  async resubscribe(dto: ResubscribeNewsletterDto): Promise<{ message: string }> {
+    const { id, email } = dto;
+
+    // Find the subscription record
+    const userSubscription = await this.newsletterSubscriptionRepository.findOne({
+      where: [{ id }, { email }],
+    });
+
+    if (!userSubscription) {
+      throw new NotFoundException('User not found or not unsubscribed.');
+    }
+
+    // Check if the user is already subscribed
+    if (userSubscription.status === 'active') {
+      throw new BadRequestException('User is already subscribed.');
+    }
+
+    // Update the subscription status
+    userSubscription.status = 'active';
+    await this.newsletterSubscriptionRepository.save(userSubscription);
+
+    return { message: 'Successfully resubscribed to the newsletter.' };
+  }
 }
diff --git a/src/modules/products/entities/product.entity.ts b/src/modules/products/entities/product.entity.ts
index ded381c1..addef46c 100644
--- a/src/modules/products/entities/product.entity.ts
+++ b/src/modules/products/entities/product.entity.ts
@@ -1,7 +1,7 @@
-import { AbstractBaseEntity } from '../../../entities/base.entity';
+import { AbstractBaseEntity } from '@entities/base.entity';
 import { Column, DeleteDateColumn, Entity, ManyToOne, OneToMany } from 'typeorm';
-import { Comment } from '../../../modules/comments/entities/comments.entity';
-import { Organisation } from '../../../modules/organisations/entities/organisations.entity';
+import { Comment } from '@modules/comments/entities/comments.entity';
+import { Organisation } from '@modules/organisations/entities/organisations.entity';
 import { Cart } from '../../dashboard/entities/cart.entity';
 import { OrderItem } from '../../dashboard/entities/order-items.entity';
 import { Review } from './review.entity';
@@ -64,7 +64,7 @@ export class Product extends AbstractBaseEntity {
   @DeleteDateColumn()
   deletedAt?: Date;
 
-  @OneToMany(() => Comment, comment => comment.product)
+  @OneToMany(() => Comment, comment => comment.product, { onUpdate: 'CASCADE' })
   comments?: Comment[];
 
   @OneToMany(() => OrderItem, orderItem => orderItem.product)
diff --git a/src/modules/products/products.controller.ts b/src/modules/products/products.controller.ts
index 9b7ad01d..f03ce781 100644
--- a/src/modules/products/products.controller.ts
+++ b/src/modules/products/products.controller.ts
@@ -24,7 +24,7 @@ import { AuthGuard } from '../../guards/auth.guard';
 import { CurrentUser } from './current-user.decorator';
 import { skipAuth } from '@shared/helpers/skipAuth';
 import { OwnershipGuard } from '@guards/authorization.guard';
-import { AddCommentDto } from '@modules/comments/dto/add-comment.dto';
+import { AddCommentDto } from '@modules/comments/dtos/add-comment.dto';
 import { INVALID_ORG_ID, INVALID_PRODUCT_ID } from '@shared/constants/SystemMessages';
 import { CustomHttpException } from '@shared/helpers/custom-http-filter';
 import { SuperAdminGuard } from '@guards/super-admin.guard';
@@ -157,10 +157,9 @@ export class ProductsController {
 
   @ApiBearerAuth()
   @UseGuards(OwnershipGuard)
-  @Post('organisations/:productId/comments')
-  @ApiBearerAuth()
+  @Post('organisations/:orgId/products/:productId/comments')
   @ApiOperation({ summary: 'Creates a comment for a product' })
-  @ApiParam({ name: 'id', description: 'organisation ID', example: '870ccb14-d6b0-4a50-b459-9895af803i89' })
+  @ApiParam({ name: 'orgId', description: 'organisation ID', example: '870ccb14-d6b0-4a50-b459-9895af803i89' })
   @ApiParam({ name: 'productId', description: 'product ID', example: '126ccb14-d6b0-4a50-b459-9895af803h6y' })
   @ApiBody({ type: AddCommentDto, description: 'Comment to be added' })
   @ApiResponse({ status: 201, description: 'Comment added successfully' })
@@ -174,9 +173,9 @@ export class ProductsController {
 
   @ApiBearerAuth()
   @UseGuards(OwnershipGuard)
-  @Get('organisations/:productId/stock')
+  @Get('organisations/:orgId/products/:productId/stock')
   @ApiOperation({ summary: 'Gets a product stock details by id' })
-  @ApiParam({ name: 'id', description: 'Organization ID', example: '12345' })
+  @ApiParam({ name: 'orgId', description: 'Organization ID', example: '12345' })
   @ApiParam({ name: 'productId', description: 'Product ID' })
   @ApiResponse({ status: 200, description: 'Product stock retrieved successfully' })
   @ApiResponse({ status: 404, description: 'Product not found' })
diff --git a/src/modules/products/products.service.ts b/src/modules/products/products.service.ts
index b760c771..0482ea83 100644
--- a/src/modules/products/products.service.ts
+++ b/src/modules/products/products.service.ts
@@ -1,16 +1,8 @@
-import {
-  ForbiddenException,
-  HttpStatus,
-  Injectable,
-  InternalServerErrorException,
-  Logger,
-  NotFoundException,
-} from '@nestjs/common';
+import { HttpStatus, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { endOfMonth, startOfMonth, subMonths } from 'date-fns';
 import { Repository } from 'typeorm';
 import * as SYS_MSG from '@shared/constants/SystemMessages';
-import { AddCommentDto } from '../comments/dto/add-comment.dto';
 import { Comment } from '../comments/entities/comments.entity';
 import { Organisation } from '../organisations/entities/organisations.entity';
 import { User } from '../user/entities/user.entity';
@@ -21,7 +13,7 @@ import { CreateReviewDto } from './dto/create-review.dto';
 import { Review } from './entities/review.entity';
 import { ProductResponseDto } from './dto/product-response.dto';
 import { CustomHttpException } from '@shared/helpers/custom-http-filter';
-
+import { AddCommentDto } from '../comments/dtos/add-comment.dto';
 
 interface SearchCriteria {
   name?: string;
@@ -63,32 +55,18 @@ export class ProductsService {
     newProduct.org = org;
     const statusCal = await this.calculateProductStatus(dto.quantity);
     newProduct.stock_status = statusCal;
-    newProduct.cost_price = 0.2 * dto.price - dto.price;
+    newProduct.cost_price = dto.price - 0.2 * dto.price;
     const product = await this.productRepository.save(newProduct);
-    if (!product || !newProduct)
-      throw new InternalServerErrorException({
-        status_code: 500,
-        status: 'Internal server error',
-        message: 'An unexpected error occurred. Please try again later.',
-      });
 
+    delete product.org;
     return {
-      status: 'success',
+      status_code: HttpStatus.CREATED,
       message: 'Product created successfully',
-      data: {
-        id: product.id,
-        name: product.name,
-        description: product.description,
-        price: product.price,
-        status: product.stock_status,
-        quantity: product.quantity,
-        created_at: product.created_at,
-        updated_at: product.updated_at,
-      },
+      data: product,
     };
   }
 
-  async getAllProducts({ page = 1, pageSize = 2 }: { page: number; pageSize: number }) {
+  async getAllProducts({ page = 1, pageSize = 10 }: { page: number; pageSize: number }) {
     const skip = (page - 1) * pageSize;
     const allProucts = await this.productRepository.find({ skip, take: pageSize });
     const totalProducts = await this.productRepository.count();
@@ -140,13 +118,12 @@ export class ProductsService {
         status_code: 422,
       });
 
-    const { name, category, minPrice, maxPrice } = criteria;
+    const { name, minPrice, maxPrice } = criteria;
     const query = this.productRepository.createQueryBuilder('product').where('product.orgId = :orgId', { orgId });
 
     if (name) {
       query.andWhere('product.name ILIKE :name', { name: `%${name}%` });
     }
-
     if (minPrice) {
       query.andWhere('product.price >= :minPrice', { minPrice });
     }
@@ -167,7 +144,7 @@ export class ProductsService {
     return {
       success: true,
       statusCode: 200,
-      products,
+      data: products,
     };
   }
 
@@ -177,26 +154,9 @@ export class ProductsService {
   }
 
   async updateProduct(id: string, productId: string, updateProductDto: UpdateProductDTO) {
-    const org = await this.organisationRepository.findOne({ where: { id } });
-    if (!org)
-      throw new InternalServerErrorException({
-        status: 'Unprocessable entity exception',
-        message: 'Invalid organisation credentials',
-        status_code: 422,
-      });
-    const product = await this.productRepository.findOne({ where: { id: productId }, relations: ['org'] });
+    const product = await this.productRepository.findOne({ where: { id: productId, org: { id } } });
     if (!product) {
-      throw new NotFoundException({
-        error: 'Product not found',
-        status_code: HttpStatus.NOT_FOUND,
-      });
-    }
-
-    if (product.org.id !== org.id) {
-      throw new ForbiddenException({
-        status: 'fail',
-        message: 'Not allowed to perform this action',
-      });
+      throw new NotFoundException('Invalid products credentials');
     }
 
     try {
@@ -254,7 +214,6 @@ export class ProductsService {
   }
 
   async addCommentToProduct(productId: string, commentDto: AddCommentDto, userId: string) {
-    const { comment } = commentDto;
     const product = await this.productRepository.findOne({ where: { id: productId } });
     const user = await this.userRepository.findOne({ where: { id: userId } });
 
@@ -262,7 +221,7 @@ export class ProductsService {
       throw new CustomHttpException(SYS_MSG.PRODUCT_NOT_FOUND, HttpStatus.NOT_FOUND);
     }
 
-    const productComment = this.commentRepository.create({ comment, product, user });
+    const productComment = this.commentRepository.create({ ...commentDto, product, user });
 
     const saveComment = await this.commentRepository.save(productComment);
 
@@ -322,10 +281,9 @@ export class ProductsService {
     const lastMonthEnds = this.getDateRange(lastMonth).monthEnds;
 
     const totalProductsThisMonth = await this.getTotalProductsForDateRange(monthStarts, monthEnds);
-
     const totalProductsLastMonth = await this.getTotalProductsForDateRange(lastMonthStarts, lastMonthEnds);
 
-    let percentageChange;
+    let percentageChange: string;
 
     if (totalProductsLastMonth === totalProductsThisMonth) {
       percentageChange =
diff --git a/src/modules/timezones/timezones.controller.ts b/src/modules/timezones/timezones.controller.ts
index a988738f..f739fb34 100644
--- a/src/modules/timezones/timezones.controller.ts
+++ b/src/modules/timezones/timezones.controller.ts
@@ -25,9 +25,8 @@ export class TimezonesController {
   @ApiOperation({ summary: 'Get all supported timezones' })
   @ApiResponse({ status: 200, description: 'List of supported timezones.' })
   @ApiResponse({ status: 401, description: 'Unauthorized' })
-  async getTimezones(@Res() response: Response): Promise<any> {
-    const result = await this.timezonesService.getSupportedTimezones();
-    return response.status(result.status_code).json(result);
+  async getTimezones() {
+    return this.timezonesService.getSupportedTimezones();
   }
 
   @Patch(':id')
diff --git a/src/modules/timezones/timezones.service.ts b/src/modules/timezones/timezones.service.ts
index 97b5429e..4c6b4548 100644
--- a/src/modules/timezones/timezones.service.ts
+++ b/src/modules/timezones/timezones.service.ts
@@ -21,6 +21,7 @@ import {
 
 @Injectable()
 export class TimezonesService {
+  private logger = new Logger(TimezonesService.name);
   constructor(
     @InjectRepository(Timezone)
     private readonly timezoneRepository: Repository<Timezone>
@@ -48,7 +49,7 @@ export class TimezonesService {
         timezone: newTimezone,
       };
     } catch (error) {
-      console.log('TimezonesServiceError ~ createTimezone ~', error);
+      this.logger.error('TimezonesServiceError ~ createTimezone ~', error);
       throw new InternalServerErrorException({
         message: ERROR_OCCURED,
         status_code: HttpStatus.INTERNAL_SERVER_ERROR,
@@ -60,15 +61,18 @@ export class TimezonesService {
     try {
       const timezones = await this.timezoneRepository.find();
       const formattedTimezones = timezones.map(tz => ({
-        timezone: `(UTC ${tz.gmtOffset}) ${tz.description}`,
+        timezone: tz.timezone,
+        gmtOffset: tz.gmtOffset,
+        description: tz.description,
       }));
+
       return {
         status_code: HttpStatus.OK,
         message: SUCCESS,
         timezones: formattedTimezones,
       };
     } catch (error) {
-      Logger.error('TimezonesServiceError ~ fetchTimezones ~', error);
+      this.logger.error('TimezonesServiceError ~ fetchTimezones ~', error);
       throw new InternalServerErrorException({
         message: FETCH_TIMEZONE_FAILURE,
         status_code: HttpStatus.INTERNAL_SERVER_ERROR,
@@ -95,7 +99,7 @@ export class TimezonesService {
         timezone,
       };
     } catch (error) {
-      Logger.error('TimezonesServiceError ~ updateTimezone ~', error);
+      this.logger.error('TimezonesServiceError ~ updateTimezone ~', error);
       throw new InternalServerErrorException({
         message: ERROR_OCCURED,
         status_code: HttpStatus.INTERNAL_SERVER_ERROR,



Test patch:
diff --git a/src/modules/languages/tests/languages.service.spec.ts b/src/modules/languages/tests/languages.service.spec.ts
index 67175a41..34f9dbce 100644
--- a/src/modules/languages/tests/languages.service.spec.ts
+++ b/src/modules/languages/tests/languages.service.spec.ts
@@ -20,6 +20,7 @@ const mockLanguageRepository = {
   create: jest.fn(),
   save: jest.fn(),
   find: jest.fn(),
+  remove: jest.fn(),
   createQueryBuilder: jest.fn(() => ({
     innerJoin: jest.fn().mockReturnThis(),
     where: jest.fn().mockReturnThis(),
@@ -53,6 +54,7 @@ describe('LanguagesService', () => {
 
     service = module.get<LanguagesService>(LanguagesService);
     repository = module.get<Repository<Language>>(getRepositoryToken(Language));
+    userRepository = module.get<Repository<User>>(getRepositoryToken(User));
   });
 
   it('should be defined', () => {
@@ -163,4 +165,35 @@ describe('LanguagesService', () => {
       await expect(languageService.getUserLanguages('123')).rejects.toThrow(NotFoundException);
     });
   });
+
+  describe('deleteUserLanguage', () => {
+    it('should delete a user-specific language successfully', async () => {
+      const mockUser = { id: 'user123', languages: [{ id: 'lang123', language: 'English' }] } as User;
+      jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser);
+      jest.spyOn(repository, 'remove').mockResolvedValue(null);
+
+      await expect(service.deleteUserLanguage('lang123', 'user123')).resolves.toEqual({
+        message: 'Language successfully deleted for the user.',
+      });
+    });
+
+    it('should throw NotFoundException if user does not exist', async () => {
+      jest.spyOn(userRepository, 'findOne').mockResolvedValue(null);
+      await expect(service.deleteUserLanguage('lang123', 'user123')).rejects.toThrow(NotFoundException);
+    });
+
+    it('should throw NotFoundException if language is not found for user', async () => {
+      const mockUser = { id: 'user123', languages: [] } as User;
+      jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser);
+      await expect(service.deleteUserLanguage('lang123', 'user123')).rejects.toThrow(NotFoundException);
+    });
+
+    it('should throw BadRequestException if language has dependencies', async () => {
+      const mockUser = { id: 'user123', languages: [{ id: 'lang123', language: 'English' }] } as User;
+      jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser);
+      jest.spyOn(repository, 'remove').mockRejectedValue(new Error('Cannot delete'));
+
+      await expect(service.deleteUserLanguage('lang123', 'user123')).rejects.toThrow(BadRequestException);
+    });
+  });
 });
diff --git a/src/modules/newsletter-subscription/tests/newsletter-subscription.service.spec.ts b/src/modules/newsletter-subscription/tests/newsletter-subscription.service.spec.ts
index f7e05a74..87a11818 100644
--- a/src/modules/newsletter-subscription/tests/newsletter-subscription.service.spec.ts
+++ b/src/modules/newsletter-subscription/tests/newsletter-subscription.service.spec.ts
@@ -189,4 +189,38 @@ describe('NewsletterService', () => {
       expect(repository.findOne).toHaveBeenCalledWith({ where: { email } });
     });
   });
+
+  describe('resubscribe', () => {
+    it('should successfully resubscribe a user who was unsubscribed', async () => {
+      const dto = { email: 'test@example.com' };
+      const mockSubscription = {
+        id: '1',
+        email: 'test@example.com',
+        status: 'unsubscribed',
+      } as NewsletterSubscription;
+
+      jest.spyOn(repository, 'findOne').mockResolvedValue(mockSubscription);
+      jest.spyOn(repository, 'save').mockResolvedValue({ ...mockSubscription, status: 'active' });
+
+      const result = await service.resubscribe(dto);
+
+      expect(result).toEqual({ message: 'Successfully resubscribed to the newsletter.' });
+      expect(repository.findOne).toHaveBeenCalledWith(
+        expect.objectContaining({
+          where: expect.arrayContaining([expect.objectContaining({ email: dto.email })]),
+        })
+      );
+      expect(repository.save).toHaveBeenCalledWith(expect.objectContaining({ status: 'active' }));
+    });
+
+    it('should throw NotFoundException if user is not found or was never subscribed', async () => {
+      const dto = { email: 'notfound@example.com' };
+
+      jest.spyOn(repository, 'findOne').mockResolvedValue(null);
+
+      await expect(service.resubscribe(dto)).rejects.toThrow(
+        new NotFoundException('User not found or not unsubscribed.')
+      );
+    });
+  });
 });
diff --git a/src/modules/products/tests/products.service.spec.ts b/src/modules/products/tests/products.service.spec.ts
index 375a3517..6c842caf 100644
--- a/src/modules/products/tests/products.service.spec.ts
+++ b/src/modules/products/tests/products.service.spec.ts
@@ -1,23 +1,22 @@
 import { InternalServerErrorException, NotFoundException } from '@nestjs/common';
 import { Test, TestingModule } from '@nestjs/testing';
 import { getRepositoryToken } from '@nestjs/typeorm';
-import { DeleteResult, Repository, UpdateResult } from 'typeorm';
+import { Repository, UpdateResult } from 'typeorm';
 import { CustomHttpException } from '@shared/helpers/custom-http-filter';
-import { Comment } from '../../../modules/comments/entities/comments.entity';
-import { Organisation } from '../../../modules/organisations/entities/organisations.entity';
-import { orgMock } from '../../../modules/organisations/tests/mocks/organisation.mock';
-import { User } from '../../../modules/user/entities/user.entity';
-import { mockUser } from '../../../modules/user/tests/mocks/user.mock';
-import { AddCommentDto } from '../../comments/dto/add-comment.dto';
+import { Comment } from '@modules/comments/entities/comments.entity';
+import { Organisation } from '@modules/organisations/entities/organisations.entity';
+import { orgMock } from '@modules/organisations/tests/mocks/organisation.mock';
+import { User } from '@modules/user/entities/user.entity';
+import { mockUser } from '@modules/user/tests/mocks/user.mock';
 import { UpdateProductDTO } from '../dto/update-product.dto';
 import { ProductVariant } from '../entities/product-variant.entity';
 import { Product } from '../entities/product.entity';
 import { ProductsService } from '../products.service';
 import { mockComment } from './mocks/comment.mock';
-import { deletedProductMock } from './mocks/deleted-product.mock';
 import { createProductRequestDtoMock } from './mocks/product-request-dto.mock';
 import { productMock } from './mocks/product.mock';
 import { Review } from '../entities/review.entity';
+import { AddCommentDto } from '../../comments/dtos/add-comment.dto';
 
 describe('ProductsService', () => {
   let service: ProductsService;
@@ -91,7 +90,6 @@ describe('ProductsService', () => {
     const createdProduct = await service.createProduct(orgMock.id, createProductRequestDtoMock);
 
     expect(createdProduct.message).toEqual('Product created successfully');
-    expect(createdProduct.status).toEqual('success');
   });
 
   describe('searchProducts', () => {
@@ -108,7 +106,7 @@ describe('ProductsService', () => {
 
       const products = await service.searchProducts(orgMock.id, searchCriteria);
 
-      expect(products).toEqual({ success: true, statusCode: 200, products: [productMock] });
+      expect(products).toEqual({ success: true, statusCode: 200, data: [productMock] });
     });
 
     it('should return products based on price range search', async () => {
@@ -124,7 +122,7 @@ describe('ProductsService', () => {
 
       const products = await service.searchProducts(orgMock.id, searchCriteria);
 
-      expect(products).toEqual({ success: true, statusCode: 200, products: [productMock] });
+      expect(products).toEqual({ success: true, statusCode: 200, data: [productMock] });
     });
 
     it('should return products based on combined search criteria', async () => {
@@ -140,7 +138,7 @@ describe('ProductsService', () => {
 
       const products = await service.searchProducts(orgMock.id, searchCriteria);
 
-      expect(products).toEqual({ success: true, statusCode: 200, products: [productMock] });
+      expect(products).toEqual({ success: true, statusCode: 200, data: [productMock] });
     });
 
     it('should throw NotFoundException if no products match search criteria', async () => {
@@ -228,7 +226,7 @@ describe('ProductsService', () => {
     });
 
     it('should throw an error', async () => {
-      const addCommentDto: AddCommentDto = {
+      const AddCommentDto: AddCommentDto = {
         comment: 'New Comment',
       };
 
@@ -237,7 +235,7 @@ describe('ProductsService', () => {
       jest.spyOn(commentRepository, 'create').mockReturnValue(mockComment);
       jest.spyOn(commentRepository, 'save').mockResolvedValue(mockComment);
 
-      await expect(service.addCommentToProduct(productMock.id, addCommentDto, mockUser.id)).rejects.toThrow(
+      await expect(service.addCommentToProduct(productMock.id, AddCommentDto, mockUser.id)).rejects.toThrow(
         CustomHttpException
       );
     });