From 2d1a476c8732bb91c1c6976cd52252f183c37959 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Tue, 17 Dec 2024 15:56:36 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=20=E2=99=BB=EF=B8=8F=20auth=20mod?= =?UTF-8?q?ule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/auth/application/src/index.ts | 2 +- .../application/src/lib/auth.service.spec.ts | 64 ----------------- .../lib/use-cases/sign-in.use-case.spec.ts | 70 +++++++++++++++++++ .../sign-in.use-case.ts} | 20 +++--- .../interface-adapters/src/lib/auth.module.ts | 6 +- .../src/lib/resolver/auth.resolver.spec.ts | 61 ++++++++++------ .../src/lib/resolver/auth.resolver.ts | 6 +- 7 files changed, 126 insertions(+), 103 deletions(-) delete mode 100644 libs/auth/application/src/lib/auth.service.spec.ts create mode 100644 libs/auth/application/src/lib/use-cases/sign-in.use-case.spec.ts rename libs/auth/application/src/lib/{auth.service.ts => use-cases/sign-in.use-case.ts} (58%) diff --git a/libs/auth/application/src/index.ts b/libs/auth/application/src/index.ts index 9497466..80a7c81 100644 --- a/libs/auth/application/src/index.ts +++ b/libs/auth/application/src/index.ts @@ -1 +1 @@ -export * from './lib/auth.service'; +export * from './lib/use-cases/sign-in.use-case'; diff --git a/libs/auth/application/src/lib/auth.service.spec.ts b/libs/auth/application/src/lib/auth.service.spec.ts deleted file mode 100644 index 844c893..0000000 --- a/libs/auth/application/src/lib/auth.service.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { UnauthorizedException } from '@nestjs/common'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AwsCognitoService } from '@shared/infrastructure-aws-cognito'; -import { JwtService } from '@nestjs/jwt'; - -import { AuthService } from './auth.service'; - -describe('AuthService', () => { - let service: AuthService; - let awsCognitoService: jest.Mocked; - let jwtService: jest.Mocked; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - AuthService, - { - provide: AwsCognitoService, - useValue: { - signIn: jest.fn(), - }, - }, - { - provide: JwtService, - useValue: { - signAsync: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(AuthService); - awsCognitoService = module.get(AwsCognitoService); - jwtService = module.get(JwtService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('signIn', () => { - const email = 'test@example.com'; - const password = 'password123'; - - // it('should throw UnauthorizedException when AWS Cognito sign in fails', async () => { - // const error = new Error('Invalid credentials'); - // awsCognitoService.signIn.mockRejectedValue(error); - - // await expect(service.signIn(email, password)).rejects.toThrow( - // UnauthorizedException, - // ); - // }); - - it('should throw UnauthorizedException when JWT signing fails', async () => { - const error = new Error('JWT signing failed'); - awsCognitoService.signIn.mockResolvedValue(undefined); - jwtService.signAsync.mockRejectedValue(error); - - await expect(service.signIn(email, password)).rejects.toThrow( - UnauthorizedException, - ); - }); - }); -}); diff --git a/libs/auth/application/src/lib/use-cases/sign-in.use-case.spec.ts b/libs/auth/application/src/lib/use-cases/sign-in.use-case.spec.ts new file mode 100644 index 0000000..31351b2 --- /dev/null +++ b/libs/auth/application/src/lib/use-cases/sign-in.use-case.spec.ts @@ -0,0 +1,70 @@ +import { Test } from '@nestjs/testing'; +import { JwtService } from '@nestjs/jwt'; +import { UnauthorizedException } from '@nestjs/common'; +import { AwsCognitoService } from '@shared/infrastructure-aws-cognito'; +import { SignInUseCase } from './sign-in.use-case'; + +describe('SignInUseCase', () => { + let signInUseCase: SignInUseCase; + let awsCognitoService: AwsCognitoService; + let jwtService: JwtService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + SignInUseCase, + { + provide: AwsCognitoService, + useValue: { + signIn: jest.fn(), + }, + }, + { + provide: JwtService, + useValue: { + signAsync: jest.fn(), + }, + }, + ], + }).compile(); + + signInUseCase = moduleRef.get(SignInUseCase); + awsCognitoService = moduleRef.get(AwsCognitoService); + jwtService = moduleRef.get(JwtService); + }); + + describe('execute', () => { + const email = 'test@example.com'; + const password = 'password123'; + const mockAccessToken = 'mock.access.token'; + + it('should successfully sign in and return access token', async () => { + jest.spyOn(awsCognitoService, 'signIn').mockResolvedValue(undefined); + jest.spyOn(jwtService, 'signAsync').mockResolvedValue(mockAccessToken); + + const result = await signInUseCase.execute(email, password); + + expect(awsCognitoService.signIn).toHaveBeenCalledWith(email, password); + expect(jwtService.signAsync).toHaveBeenCalledWith({ email }); + expect(result).toEqual({ accessToken: mockAccessToken }); + }); + + it('should throw UnauthorizedException when AWS Cognito sign in fails', async () => { + const error = new Error('Invalid credentials'); + jest.spyOn(awsCognitoService, 'signIn').mockRejectedValue(error); + + await expect(signInUseCase.execute(email, password)).rejects.toThrow( + UnauthorizedException, + ); + }); + + it('should throw UnauthorizedException when JWT signing fails', async () => { + jest.spyOn(awsCognitoService, 'signIn').mockResolvedValue(undefined); + jest.spyOn(jwtService, 'signAsync').mockRejectedValue(new Error()); + + await expect(signInUseCase.execute(email, password)).rejects.toThrow( + UnauthorizedException, + ); + }); + }); +}); diff --git a/libs/auth/application/src/lib/auth.service.ts b/libs/auth/application/src/lib/use-cases/sign-in.use-case.ts similarity index 58% rename from libs/auth/application/src/lib/auth.service.ts rename to libs/auth/application/src/lib/use-cases/sign-in.use-case.ts index 20890c4..c4b50e3 100644 --- a/libs/auth/application/src/lib/auth.service.ts +++ b/libs/auth/application/src/lib/use-cases/sign-in.use-case.ts @@ -1,29 +1,26 @@ -import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; +import { Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { AwsCognitoService } from '@shared/infrastructure-aws-cognito'; -// Implement the authentication logic @Injectable() -export class AuthService { - private readonly logger = new Logger(AuthService.name); +export class SignInUseCase { constructor( private awsCognitoService: AwsCognitoService, private jwtService: JwtService, ) {} // Retrieving a user and verifying the password - async signIn( + async execute( email: string, pass: string, ): Promise<{ accessToken: string; }> { - // try { - // await this.awsCognitoService.signIn(email, pass); - // } catch (error) { - // this.logger.error('SignIn error:', error); - // throw new UnauthorizedException(error); // TODO: return error code - // } + try { + await this.awsCognitoService.signIn(email, pass); + } catch (error) { + throw new UnauthorizedException(error); // TODO: return error code + } try { const accessToken = await this.jwtService.signAsync({ @@ -35,7 +32,6 @@ export class AuthService { accessToken, }; } catch (error) { - this.logger.error('SignIn error:', error); throw new UnauthorizedException('Invalid credentials'); // TODO: return error code } } diff --git a/libs/auth/interface-adapters/src/lib/auth.module.ts b/libs/auth/interface-adapters/src/lib/auth.module.ts index 3f8b4c1..b59a311 100644 --- a/libs/auth/interface-adapters/src/lib/auth.module.ts +++ b/libs/auth/interface-adapters/src/lib/auth.module.ts @@ -1,4 +1,4 @@ -import { AuthService } from '@auth/application'; +import { SignInUseCase } from '@auth/application'; import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { JwtModule } from '@nestjs/jwt'; @@ -7,7 +7,7 @@ import { AwsCognitoService } from '@shared/infrastructure-aws-cognito'; import { AuthResolver } from './resolver/auth.resolver'; @Module({ - providers: [AuthResolver, AuthService, AwsCognitoService], + providers: [AuthResolver, SignInUseCase, AwsCognitoService], imports: [ JwtModule.registerAsync({ imports: [ConfigModule], @@ -22,6 +22,6 @@ import { AuthResolver } from './resolver/auth.resolver'; }, }), ], - exports: [AuthService], + exports: [], }) export class AuthModule {} diff --git a/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.spec.ts b/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.spec.ts index d6fdab2..db4ba65 100644 --- a/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.spec.ts +++ b/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.spec.ts @@ -1,50 +1,71 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { AuthService } from '@auth/application'; +import { SignInUseCase } from '@auth/application'; +import { UnauthorizedException } from '@nestjs/common'; + import { AuthResolver } from './auth.resolver'; import { SignInInputDto } from '../dto/sign-in-input.dto'; describe('AuthResolver', () => { let resolver: AuthResolver; - let authService: AuthService; + let signInUseCase: SignInUseCase; + + const mockSignInUseCase = { + execute: jest.fn(), + }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ AuthResolver, { - provide: AuthService, - useValue: { - signIn: jest.fn(), - }, + provide: SignInUseCase, + useValue: mockSignInUseCase, }, ], }).compile(); resolver = module.get(AuthResolver); - authService = module.get(AuthService); + signInUseCase = module.get(SignInUseCase); }); - it('should be defined', () => { - expect(resolver).toBeDefined(); + afterEach(() => { + jest.clearAllMocks(); }); describe('signIn', () => { - it('should call authService.signIn with correct parameters', async () => { - const signInInput: SignInInputDto = { - email: 'test@example.com', - password: 'password123', - }; - const expectedResult = { accessToken: 'testToken' }; - - jest.spyOn(authService, 'signIn').mockResolvedValue(expectedResult); + const signInInput: SignInInputDto = { + email: 'test@example.com', + password: 'password123', + }; + + const mockResponse = { + accessToken: 'mock-access-token', + }; + + it('should successfully sign in a user', async () => { + mockSignInUseCase.execute.mockResolvedValue(mockResponse); const result = await resolver.signIn(signInInput); - expect(authService.signIn).toHaveBeenCalledWith( + expect(result).toEqual(mockResponse); + expect(signInUseCase.execute).toHaveBeenCalledWith( + signInInput.email, + signInInput.password, + ); + }); + + it('should throw UnauthorizedException when sign in fails', async () => { + mockSignInUseCase.execute.mockRejectedValue( + new UnauthorizedException('Invalid credentials'), + ); + + await expect(resolver.signIn(signInInput)).rejects.toThrow( + UnauthorizedException, + ); + expect(signInUseCase.execute).toHaveBeenCalledWith( signInInput.email, - signInInput.password + signInInput.password, ); - expect(result).toEqual(expectedResult); }); }); }); diff --git a/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.ts b/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.ts index 2b58926..2a79cd2 100644 --- a/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.ts +++ b/libs/auth/interface-adapters/src/lib/resolver/auth.resolver.ts @@ -1,4 +1,4 @@ -import { AuthService } from '@auth/application'; +import { SignInUseCase } from '@auth/application'; import { Resolver, Mutation, Args, Field, ObjectType } from '@nestjs/graphql'; import { SignInInputDto } from '../dto/sign-in-input.dto'; @@ -12,13 +12,13 @@ export class Response { // Expose the authentication endpoints @Resolver() export class AuthResolver { - constructor(private authService: AuthService) {} + constructor(private signInUseCase: SignInUseCase) {} @Mutation(() => Response) async signIn( @Args({ name: 'signInInput', type: () => SignInInputDto }) signInInput: SignInInputDto, ): Promise { - return this.authService.signIn(signInInput.email, signInInput.password); + return this.signInUseCase.execute(signInInput.email, signInInput.password); } } From 18531a966ae848d3adfb994deec9178af55cad96 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Tue, 17 Dec 2024 16:02:21 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remove=20logger=20in?= =?UTF-8?q?=20AwsCognitoService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aws-cognito/src/lib/aws-cognito.service.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts b/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts index b1b38c4..fed7c3c 100644 --- a/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts +++ b/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts @@ -1,11 +1,9 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { awsConfig } from '@shared/config'; import * as AWS from 'aws-sdk'; @Injectable() export class AwsCognitoService { - private readonly logger = new Logger(AwsCognitoService.name); // private readonly configService: ConfigService; private cognito: AWS.CognitoIdentityServiceProvider; @@ -38,7 +36,6 @@ export class AwsCognitoService { try { return await this.cognito.signUp(params).promise(); } catch (error) { - this.logger.error('signUp', error); throw error; } } @@ -60,7 +57,6 @@ export class AwsCognitoService { try { return await this.cognito.initiateAuth(params).promise(); } catch (error) { - this.logger.error('signIn', error); throw error; } } @@ -80,7 +76,6 @@ export class AwsCognitoService { try { return await this.cognito.initiateAuth(params).promise(); } catch (error) { - this.logger.error('refreshToken', error); throw error; } } @@ -96,7 +91,6 @@ export class AwsCognitoService { try { await this.cognito.confirmSignUp(params).promise(); } catch (error) { - this.logger.error('confirmSignUp', error); throw error; } } From da14c23e25d0391606973cff42e1becff6582db6 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Tue, 17 Dec 2024 16:07:29 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=F0=9F=90=9B=20lint=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/aws-cognito.service.ts | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts b/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts index fed7c3c..7506882 100644 --- a/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts +++ b/libs/shared/infrastructure/aws-cognito/src/lib/aws-cognito.service.ts @@ -33,11 +33,7 @@ export class AwsCognitoService { ], }; - try { - return await this.cognito.signUp(params).promise(); - } catch (error) { - throw error; - } + return await this.cognito.signUp(params).promise(); } async signIn( @@ -54,11 +50,7 @@ export class AwsCognitoService { }, }; - try { - return await this.cognito.initiateAuth(params).promise(); - } catch (error) { - throw error; - } + return await this.cognito.initiateAuth(params).promise(); } async refreshToken( @@ -73,11 +65,7 @@ export class AwsCognitoService { }, }; - try { - return await this.cognito.initiateAuth(params).promise(); - } catch (error) { - throw error; - } + return await this.cognito.initiateAuth(params).promise(); } async confirmSignUp(email: string, confirmationCode: string): Promise { @@ -88,10 +76,6 @@ export class AwsCognitoService { ConfirmationCode: confirmationCode, }; - try { - await this.cognito.confirmSignUp(params).promise(); - } catch (error) { - throw error; - } + await this.cognito.confirmSignUp(params).promise(); } }