Skip to content

Commit

Permalink
improved more coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdip-b committed Feb 20, 2024
1 parent 8eec8dd commit 4608118
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 34 deletions.
110 changes: 88 additions & 22 deletions apps/api/src/api-key/api-key.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,32 @@ describe('Api Key Role Controller Tests', () => {
apiKeyValue = response.json().value
})

it('should be able to update the api key', async () => {
it('should not have any authorities if none are provided', async () => {
const response = await app.inject({
method: 'POST',
url: '/api-key',
payload: {
name: 'Test Key 2',
expiresAfter: '24'
},
headers: {
'x-e2e-user-email': user.email
}
})

expect(response.statusCode).toBe(201)
expect(response.json()).toEqual({
id: expect.any(String),
name: 'Test Key 2',
value: expect.stringMatching(/^ks_*/),
authorities: [],
expiresAt: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String)
})
})

it('should be able to update the api key without without changing the authorities', async () => {
const response = await app.inject({
method: 'PUT',
url: `/api-key/${apiKey.id}`,
Expand All @@ -107,6 +132,31 @@ describe('Api Key Role Controller Tests', () => {
apiKey = response.json()
})

it('should be able to update the api key with changing the expiry', async () => {
const response = await app.inject({
method: 'PUT',
url: `/api-key/${apiKey.id}`,
payload: {
name: 'Updated Test Key',
expiresAfter: '24',
authorities: ['READ_API_KEY', 'CREATE_ENVIRONMENT']
},
headers: {
'x-e2e-user-email': user.email
}
})

expect(response.statusCode).toBe(200)
expect(response.json()).toEqual({
id: apiKey.id,
name: 'Updated Test Key',
authorities: ['READ_API_KEY', 'CREATE_ENVIRONMENT'],
expiresAt: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String)
})
})

it('should be able to get the api key', async () => {
const response = await app.inject({
method: 'GET',
Expand All @@ -120,13 +170,25 @@ describe('Api Key Role Controller Tests', () => {
expect(response.json()).toEqual({
id: apiKey.id,
name: 'Updated Test Key',
authorities: ['READ_API_KEY'],
authorities: ['READ_API_KEY', 'CREATE_ENVIRONMENT'],
expiresAt: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String)
})
})

it('should not be able to get the API key if not exists', async () => {
const response = await app.inject({
method: 'GET',
url: `/api-key/ks_1234567890`,
headers: {
'x-e2e-user-email': user.email
}
})

expect(response.statusCode).toBe(404)
})

it('should be able to get all the api keys of the user', async () => {
const response = await app.inject({
method: 'GET',
Expand All @@ -137,16 +199,18 @@ describe('Api Key Role Controller Tests', () => {
})

expect(response.statusCode).toBe(200)
expect(response.json()).toEqual([
{
id: apiKey.id,
name: 'Updated Test Key',
authorities: ['READ_API_KEY'],
expiresAt: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String)
}
])
expect(response.json()).toEqual(
expect.arrayContaining([
{
id: apiKey.id,
name: 'Updated Test Key',
authorities: ['READ_API_KEY', 'CREATE_ENVIRONMENT'],
expiresAt: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String)
}
])
)
})

it('should be able to get all api keys using the API key', async () => {
Expand All @@ -159,16 +223,18 @@ describe('Api Key Role Controller Tests', () => {
})

expect(response.statusCode).toBe(200)
expect(response.json()).toEqual([
{
id: apiKey.id,
name: 'Updated Test Key',
authorities: ['READ_API_KEY'],
expiresAt: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String)
}
])
expect(response.json()).toEqual(
expect.arrayContaining([
{
id: apiKey.id,
name: 'Updated Test Key',
authorities: ['READ_API_KEY', 'CREATE_ENVIRONMENT'],
expiresAt: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String)
}
])
)
})

it('should not be able to create api key with invalid authorities of API key', async () => {
Expand Down
4 changes: 1 addition & 3 deletions apps/api/src/api-key/service/api-key.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,7 @@ export class ApiKeyService {
})

if (!apiKey) {
throw new NotFoundException(
`User ${user.id} is not authorized to access API key ${apiKeyId}`
)
throw new NotFoundException(`API key with id ${apiKeyId} not found`)
}

return apiKey
Expand Down
125 changes: 125 additions & 0 deletions apps/api/src/auth/auth.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {
FastifyAdapter,
NestFastifyApplication
} from '@nestjs/platform-fastify'
import { PrismaService } from '../prisma/prisma.service'
import { Test } from '@nestjs/testing'
import { AuthModule } from './auth.module'
import { MAIL_SERVICE } from '../mail/services/interface.service'
import { MockMailService } from '../mail/services/mock.service'
import { AppModule } from '../app/app.module'
import { Otp } from '@prisma/client'

describe('Auth Controller Tests', () => {
let app: NestFastifyApplication
let prisma: PrismaService

let otp: Otp

beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule, AuthModule]
})
.overrideProvider(MAIL_SERVICE)
.useClass(MockMailService)
.compile()

app = moduleRef.createNestApplication<NestFastifyApplication>(
new FastifyAdapter()
)
prisma = moduleRef.get(PrismaService)

await app.init()
await app.getHttpAdapter().getInstance().ready()
})

it('should be defined', async () => {
expect(app).toBeDefined()
expect(prisma).toBeDefined()
})

it('should not send otp if email is blank', async () => {
const response = await app.inject({
method: 'POST',
url: '/auth/send-otp/'
})

expect(response.statusCode).toBe(400)
expect(response.json().message).toBe('Please enter a valid email address')
})

it('should not send otp if email is invalid', async () => {
const response = await app.inject({
method: 'POST',
url: '/auth/send-otp/abcdef'
})

expect(response.statusCode).toBe(400)
expect(response.json().message).toBe('Please enter a valid email address')
})

it('should send otp if email is valid', async () => {
const response = await app.inject({
method: 'POST',
url: '/auth/send-otp/[email protected]'
})

expect(response.statusCode).toBe(201)
})

it('should have generated an otp', async () => {
otp = await prisma.otp.findFirst({
where: {
user: {
email: '[email protected]'
}
}
})

expect(otp).toBeDefined()
expect(otp.code).toBeDefined()
expect(otp.expiresAt).toBeDefined()
expect(otp.code.length).toBe(6)
})

it('should upsert otp if regenerated', async () => {
await app.inject({
method: 'POST',
url: '/auth/send-otp/[email protected]'
})

const regenerated = await prisma.otp.findFirst({
where: {
user: {
email: '[email protected]'
}
}
})

expect(regenerated).toBeDefined()
expect(regenerated.code).toBeDefined()
expect(regenerated.expiresAt).toBeDefined()
expect(regenerated.code.length).toBe(6)
expect(regenerated.code).not.toBe(otp.code)

otp = regenerated
})

it('should not be able to validate otp with invalid email', async () => {
const response = await app.inject({
method: 'POST',
url: '/auth/validate-otp?email=abcdef&otp=123456'
})

expect(response.statusCode).toBe(404)
})

it('should not be able to validate otp with invalid otp', async () => {
const response = await app.inject({
method: 'POST',
url: '/auth/[email protected]&otp=123456'
})

expect(response.statusCode).toBe(401)
})
})
3 changes: 3 additions & 0 deletions apps/api/src/auth/controller/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class AuthController {
await this.authService.sendOtp(email)
}

/* istanbul ignore next */
@Public()
@Post('validate-otp')
@ApiOperation({
Expand Down Expand Up @@ -88,6 +89,7 @@ export class AuthController {
return await this.authService.validateOtp(email, otp)
}

/* istanbul ignore next */
@Public()
@Get('github')
@ApiOperation({
Expand All @@ -106,6 +108,7 @@ export class AuthController {
res.status(302).redirect('/api/auth/github/callback')
}

/* istanbul ignore next */
@Public()
@Get('github/callback')
@UseGuards(AuthGuard('github'))
Expand Down
19 changes: 10 additions & 9 deletions apps/api/src/auth/service/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
HttpException,
HttpStatus,
BadRequestException,
Inject,
Injectable,
Logger,
LoggerService
LoggerService,
NotFoundException,
UnauthorizedException
} from '@nestjs/common'
import { randomUUID } from 'crypto'
import { JwtService } from '@nestjs/jwt'
Expand Down Expand Up @@ -33,10 +34,7 @@ export class AuthService {
async sendOtp(email: string): Promise<void> {
if (!email || !email.includes('@')) {
this.logger.error(`Invalid email address: ${email}`)
throw new HttpException(
'Please enter a valid email address',
HttpStatus.BAD_REQUEST
)
throw new BadRequestException('Please enter a valid email address')
}

const user = await this.createUserIfNotExists(email)
Expand Down Expand Up @@ -64,14 +62,15 @@ export class AuthService {
this.logger.log(`Login code sent to ${email}`)
}

/* istanbul ignore next */
async validateOtp(
email: string,
otp: string
): Promise<UserAuthenticatedResponse> {
const user = await this.findUserByEmail(email)
if (!user) {
this.logger.error(`User not found: ${email}`)
throw new HttpException('User not found', HttpStatus.NOT_FOUND)
throw new NotFoundException('User not found')
}

const isOtpValid =
Expand All @@ -89,7 +88,7 @@ export class AuthService {

if (!isOtpValid) {
this.logger.error(`Invalid login code for ${email}: ${otp}`)
throw new HttpException('Invalid login code', HttpStatus.UNAUTHORIZED)
throw new UnauthorizedException('Invalid login code')
}

await this.prisma.otp.delete({
Expand All @@ -111,6 +110,7 @@ export class AuthService {
}
}

/* istanbul ignore next */
async handleGithubOAuth(
email: string,
name: string,
Expand All @@ -131,6 +131,7 @@ export class AuthService {
}
}

/* istanbul ignore next */
@Cron(CronExpression.EVERY_HOUR)
async cleanUpExpiredOtps() {
try {
Expand Down

0 comments on commit 4608118

Please sign in to comment.