From 2a00f41ef8dbc9a84bb69a547f71a176e7a428db Mon Sep 17 00:00:00 2001 From: Balu Babu Date: Wed, 1 Feb 2023 17:52:33 +0530 Subject: [PATCH] test: refactored all test cases with new user type change --- .../src/auth/auth.controller.ts | 8 ++-- .../src/auth/auth.service.spec.ts | 43 +++++++++++++------ .../src/auth/auth.service.ts | 6 +-- .../user-settings.service.spec.ts | 11 ++++- .../user-settings/user-settings.service.ts | 5 ++- .../src/user/user.resolver.ts | 3 +- .../src/user/user.service.spec.ts | 31 +++++++------ .../src/user/user.service.ts | 2 +- 8 files changed, 72 insertions(+), 37 deletions(-) diff --git a/packages/hoppscotch-backend/src/auth/auth.controller.ts b/packages/hoppscotch-backend/src/auth/auth.controller.ts index 964a0e534..f28c5ff27 100644 --- a/packages/hoppscotch-backend/src/auth/auth.controller.ts +++ b/packages/hoppscotch-backend/src/auth/auth.controller.ts @@ -10,8 +10,8 @@ import { UseGuards, } from '@nestjs/common'; import { AuthService } from './auth.service'; -import { signInMagicDto } from './dto/signin-magic.dto'; -import { verifyMagicDto } from './dto/verify-magic.dto'; +import { SignInMagicDto } from './dto/signin-magic.dto'; +import { VerifyMagicDto } from './dto/verify-magic.dto'; import { Response } from 'express'; import * as E from 'fp-ts/Either'; import { authCookieHandler, throwHTTPErr } from 'src/utils'; @@ -29,7 +29,7 @@ export class AuthController { ** Route to initiate magic-link auth for a users email */ @Post('signin') - async signInMagicLink(@Body() authData: signInMagicDto) { + async signInMagicLink(@Body() authData: SignInMagicDto) { const deviceIdToken = await this.authService.signInMagicLink( authData.email, ); @@ -41,7 +41,7 @@ export class AuthController { ** Route to verify and sign in a valid user via magic-link */ @Post('verify') - async verify(@Body() data: verifyMagicDto, @Res() res: Response) { + async verify(@Body() data: VerifyMagicDto, @Res() res: Response) { const authTokens = await this.authService.verifyMagicLinkTokens(data); if (E.isLeft(authTokens)) throwHTTPErr(authTokens.left); authCookieHandler(res, authTokens.right, false); diff --git a/packages/hoppscotch-backend/src/auth/auth.service.spec.ts b/packages/hoppscotch-backend/src/auth/auth.service.spec.ts index aca5f166f..0e3a995f8 100644 --- a/packages/hoppscotch-backend/src/auth/auth.service.spec.ts +++ b/packages/hoppscotch-backend/src/auth/auth.service.spec.ts @@ -16,9 +16,10 @@ import { AuthUser } from 'src/types/AuthUser'; import { UserService } from 'src/user/user.service'; import { AuthService } from './auth.service'; import * as O from 'fp-ts/Option'; -import { verifyMagicDto } from './dto/verify-magic.dto'; +import { VerifyMagicDto } from './dto/verify-magic.dto'; import { DateTime } from 'luxon'; import * as argon2 from 'argon2'; +import * as E from 'fp-ts/Either'; const mockPrisma = mockDeep(); const mockUser = mockDeep(); @@ -50,7 +51,7 @@ const passwordlessData: VerificationToken = { expiresOn: new Date(), }; -const magicLinkVerify: verifyMagicDto = { +const magicLinkVerify: VerifyMagicDto = { deviceIdentifier: 'Dscdc', token: 'SDcsdc', }; @@ -154,7 +155,8 @@ describe('verifyMagicLinkTokens', () => { // mockPrisma.account.findUnique.mockResolvedValueOnce(null); // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); - mockPrisma.user.update.mockResolvedValueOnce(user); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); // deletePasswordlessVerificationToken mockPrisma.verificationToken.delete.mockResolvedValueOnce(passwordlessData); @@ -178,7 +180,8 @@ describe('verifyMagicLinkTokens', () => { mockUser.createUserSSO.mockResolvedValueOnce(user); // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); - mockPrisma.user.update.mockResolvedValueOnce(user); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); // deletePasswordlessVerificationToken mockPrisma.verificationToken.delete.mockResolvedValueOnce(passwordlessData); @@ -219,7 +222,10 @@ describe('verifyMagicLinkTokens', () => { // mockPrisma.account.findUnique.mockResolvedValueOnce(null); // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); - mockPrisma.user.update.mockRejectedValueOnce('RecordNotFound'); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + E.left(USER_NOT_FOUND), + ); const result = await authService.verifyMagicLinkTokens(magicLinkVerify); expect(result).toEqualLeft({ @@ -241,7 +247,8 @@ describe('verifyMagicLinkTokens', () => { // mockPrisma.account.findUnique.mockResolvedValueOnce(null); // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); - mockPrisma.user.update.mockResolvedValueOnce(user); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); // deletePasswordlessVerificationToken mockPrisma.verificationToken.delete.mockRejectedValueOnce('RecordNotFound'); @@ -256,7 +263,8 @@ describe('verifyMagicLinkTokens', () => { describe('generateAuthTokens', () => { test('should successfully generate tokens with valid inputs', async () => { mockJWT.sign.mockReturnValue(user.refreshToken); - mockPrisma.user.update.mockResolvedValueOnce(user); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); const result = await authService.generateAuthTokens(user.uid); expect(result).toEqualRight({ @@ -267,7 +275,10 @@ describe('generateAuthTokens', () => { test('should throw USER_NOT_FOUND when updating refresh tokens fails', async () => { mockJWT.sign.mockReturnValue(user.refreshToken); - mockPrisma.user.update.mockRejectedValueOnce('RecordNotFound'); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + E.left(USER_NOT_FOUND), + ); const result = await authService.generateAuthTokens(user.uid); expect(result).toEqualLeft({ @@ -291,7 +302,10 @@ describe('refreshAuthTokens', () => { test('should throw USER_NOT_FOUND when updating refresh tokens fails', async () => { // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); - mockPrisma.user.update.mockRejectedValueOnce('RecordNotFound'); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + E.left(USER_NOT_FOUND), + ); const result = await authService.refreshAuthTokens( '$argon2id$v=19$m=65536,t=3,p=4$MvVOam2clCOLtJFGEE26ZA$czvA5ez9hz+A/LML8QRgqgaFuWa5JcbwkH6r+imTQbs', @@ -317,10 +331,13 @@ describe('refreshAuthTokens', () => { test('should successfully refresh the tokens and generate a new auth token pair', async () => { // generateAuthTokens mockJWT.sign.mockReturnValue('sdhjcbjsdhcbshjdcb'); - mockPrisma.user.update.mockResolvedValueOnce({ - ...user, - refreshToken: 'sdhjcbjsdhcbshjdcb', - }); + // UpdateUserRefreshToken + mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + E.right({ + ...user, + refreshToken: 'sdhjcbjsdhcbshjdcb', + }), + ); const result = await authService.refreshAuthTokens( '$argon2id$v=19$m=65536,t=3,p=4$MvVOam2clCOLtJFGEE26ZA$czvA5ez9hz+A/LML8QRgqgaFuWa5JcbwkH6r+imTQbs', diff --git a/packages/hoppscotch-backend/src/auth/auth.service.ts b/packages/hoppscotch-backend/src/auth/auth.service.ts index 61e448eb9..44b3e6396 100644 --- a/packages/hoppscotch-backend/src/auth/auth.service.ts +++ b/packages/hoppscotch-backend/src/auth/auth.service.ts @@ -3,7 +3,7 @@ import { MailerService } from 'src/mailer/mailer.service'; import { PrismaService } from 'src/prisma/prisma.service'; import { User } from 'src/user/user.model'; import { UserService } from 'src/user/user.service'; -import { verifyMagicDto } from './dto/verify-magic.dto'; +import { VerifyMagicDto } from './dto/verify-magic.dto'; import { DateTime } from 'luxon'; import * as argon2 from 'argon2'; import * as bcrypt from 'bcrypt'; @@ -70,7 +70,7 @@ export class AuthService { * @param magicLinkTokens Object containing deviceIdentifier and token * @returns Option of VerificationToken token */ - private async validatePasswordlessTokens(magicLinkTokens: verifyMagicDto) { + private async validatePasswordlessTokens(magicLinkTokens: VerifyMagicDto) { try { const tokens = await this.prismaService.verificationToken.findUniqueOrThrow({ @@ -234,7 +234,7 @@ export class AuthService { * @returns Either of generated AuthTokens */ async verifyMagicLinkTokens( - magicLinkIDTokens: verifyMagicDto, + magicLinkIDTokens: VerifyMagicDto, ): Promise | E.Left> { const passwordlessTokens = await this.validatePasswordlessTokens( magicLinkIDTokens, diff --git a/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts b/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts index c8bc9705e..37d0eb781 100644 --- a/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts +++ b/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts @@ -5,6 +5,7 @@ import { UserSettingsService } from './user-settings.service'; import { JSON_INVALID, USER_SETTINGS_NULL_SETTINGS } from 'src/errors'; import { UserSettings } from './user-settings.model'; import { User } from 'src/user/user.model'; +import { AuthUser } from 'src/types/AuthUser'; const mockPrisma = mockDeep(); const mockPubSub = mockDeep(); @@ -16,12 +17,20 @@ const userSettingsService = new UserSettingsService( mockPubSub as any, ); -const user: User = { +const currentTime = new Date(); + +const user: AuthUser = { uid: 'aabb22ccdd', displayName: 'user-display-name', email: 'user-email', photoURL: 'user-photo-url', + isAdmin: false, + refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + currentGQLSession: {}, + currentRESTSession: {}, + createdOn: currentTime, }; + const settings: UserSettings = { id: '1', userUid: user.uid, diff --git a/packages/hoppscotch-backend/src/user-settings/user-settings.service.ts b/packages/hoppscotch-backend/src/user-settings/user-settings.service.ts index 40b630297..ed1be3ceb 100644 --- a/packages/hoppscotch-backend/src/user-settings/user-settings.service.ts +++ b/packages/hoppscotch-backend/src/user-settings/user-settings.service.ts @@ -10,6 +10,7 @@ import { USER_SETTINGS_NULL_SETTINGS, USER_SETTINGS_NOT_FOUND, } from 'src/errors'; +import { AuthUser } from 'src/types/AuthUser'; @Injectable() export class UserSettingsService { @@ -46,7 +47,7 @@ export class UserSettingsService { * @param properties stringified user settings properties * @returns an Either of `UserSettings` or error */ - async createUserSettings(user: User, properties: string) { + async createUserSettings(user: AuthUser, properties: string) { if (!properties) return E.left(USER_SETTINGS_NULL_SETTINGS); const jsonProperties = stringToJson(properties); @@ -80,7 +81,7 @@ export class UserSettingsService { * @param properties stringified user settings * @returns Promise of an Either of `UserSettings` or error */ - async updateUserSettings(user: User, properties: string) { + async updateUserSettings(user: AuthUser, properties: string) { if (!properties) return E.left(USER_SETTINGS_NULL_SETTINGS); const jsonProperties = stringToJson(properties); diff --git a/packages/hoppscotch-backend/src/user/user.resolver.ts b/packages/hoppscotch-backend/src/user/user.resolver.ts index 3c5ab1080..803377d41 100644 --- a/packages/hoppscotch-backend/src/user/user.resolver.ts +++ b/packages/hoppscotch-backend/src/user/user.resolver.ts @@ -7,6 +7,7 @@ import { UserService } from './user.service'; import { throwErr } from 'src/utils'; import * as E from 'fp-ts/lib/Either'; import { PubSubService } from 'src/pubsub/pubsub.service'; +import { AuthUser } from 'src/types/AuthUser'; @Resolver(() => User) export class UserResolver { @@ -31,7 +32,7 @@ export class UserResolver { }) @UseGuards(GqlAuthGuard) async updateUserSessions( - @GqlUser() user: User, + @GqlUser() user: AuthUser, @Args({ name: 'currentSession', description: 'JSON string of the saved REST/GQL session', diff --git a/packages/hoppscotch-backend/src/user/user.service.spec.ts b/packages/hoppscotch-backend/src/user/user.service.spec.ts index ddadc6402..972faddd7 100644 --- a/packages/hoppscotch-backend/src/user/user.service.spec.ts +++ b/packages/hoppscotch-backend/src/user/user.service.spec.ts @@ -21,8 +21,8 @@ const user: AuthUser = { displayName: 'Dwight Schrute', photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute', isAdmin: false, - currentRESTSession: JSON.stringify({}), - currentGQLSession: JSON.stringify({}), + currentRESTSession: {}, + currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', createdOn: currentTime, }; @@ -227,21 +227,22 @@ describe('createProviderAccount', () => { describe('updateUserSessions', () => { test('Should resolve right and update users GQL session', async () => { const sessionData = user.currentGQLSession; + mockPrisma.user.update.mockResolvedValue({ ...user, - currentGQLSession: JSON.parse(sessionData), + currentGQLSession: sessionData, currentRESTSession: null, }); const result = await userService.updateUserSessions( user, - sessionData, + JSON.stringify(sessionData), 'GQL', ); expect(result).toEqualRight({ ...user, - currentGQLSession: sessionData, + currentGQLSession: JSON.stringify(sessionData), currentRESTSession: null, }); }); @@ -250,19 +251,19 @@ describe('updateUserSessions', () => { mockPrisma.user.update.mockResolvedValue({ ...user, currentGQLSession: null, - currentRESTSession: JSON.parse(sessionData), + currentRESTSession: sessionData, }); const result = await userService.updateUserSessions( user, - sessionData, + JSON.stringify(sessionData), 'REST', ); expect(result).toEqualRight({ ...user, currentGQLSession: null, - currentRESTSession: sessionData, + currentRESTSession: JSON.stringify(sessionData), }); }); test('Should reject left and update user for invalid GQL session', async () => { @@ -291,16 +292,22 @@ describe('updateUserSessions', () => { test('Should publish pubsub message on user update sessions', async () => { mockPrisma.user.update.mockResolvedValue({ ...user, - currentGQLSession: JSON.parse(user.currentGQLSession), - currentRESTSession: JSON.parse(user.currentRESTSession), }); - await userService.updateUserSessions(user, user.currentGQLSession, 'GQL'); + await userService.updateUserSessions( + user, + JSON.stringify(user.currentGQLSession), + 'GQL', + ); expect(mockPubSub.publish).toHaveBeenCalledTimes(1); expect(mockPubSub.publish).toHaveBeenCalledWith( `user/${user.uid}/updated`, - user, + { + ...user, + currentGQLSession: JSON.stringify(user.currentGQLSession), + currentRESTSession: JSON.stringify(user.currentRESTSession), + }, ); }); }); diff --git a/packages/hoppscotch-backend/src/user/user.service.ts b/packages/hoppscotch-backend/src/user/user.service.ts index a88f88c0d..a712f3ac4 100644 --- a/packages/hoppscotch-backend/src/user/user.service.ts +++ b/packages/hoppscotch-backend/src/user/user.service.ts @@ -199,7 +199,7 @@ export class UserService { * @returns a Either of User or error */ async updateUserSessions( - user: User, + user: AuthUser, currentSession: string, sessionType: string, ): Promise | E.Left> {