-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #72 from HackRU/update-buy-ins
Update buy ins
- Loading branch information
Showing
7 changed files
with
263 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway'; | ||
import { middyfy } from '@libs/lambda'; | ||
|
||
import schema from './schema'; | ||
|
||
import { MongoDB, validateToken } from '../../util'; | ||
import * as path from 'path'; | ||
import * as dotenv from 'dotenv'; | ||
|
||
dotenv.config({ path: path.resolve(process.cwd(), '.env') }); | ||
|
||
const updateBuyIns: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => { | ||
try { | ||
// validate auth token | ||
const validToken = validateToken(event.body.auth_token, process.env.JWT_SECRET, event.body.email); | ||
if (!validToken) { | ||
return { | ||
statusCode: 401, | ||
body: JSON.stringify({ | ||
statusCode: 401, | ||
message: 'Unauthorized', | ||
}), | ||
}; | ||
} | ||
|
||
// connect to DB | ||
const db = MongoDB.getInstance(process.env.MONGO_URI); | ||
await db.connect(); | ||
const pointCollection = db.getCollection('f24-points-syst'); | ||
const userPoints = await pointCollection.findOne({ email: event.body.email }); | ||
|
||
if (!userPoints) { | ||
return { | ||
statusCode: 404, | ||
body: JSON.stringify({ | ||
statusCode: 404, | ||
message: 'User point balance information not found', | ||
}), | ||
}; | ||
} | ||
|
||
//sort the request buy_ins array and the buy_ins array from the db | ||
const userBuyInsSorted = userPoints.buy_ins.sort((a, b) => b.prize_id.localeCompare(a.prize_id)); | ||
const requestBuyInsSorted = event.body.buy_ins.sort((a, b) => b.prize_id.localeCompare(a.prize_id)); | ||
|
||
//check if the length of both arrays are equal | ||
if (userBuyInsSorted.length !== requestBuyInsSorted.length) { | ||
return { | ||
statusCode: 400, | ||
body: JSON.stringify({ | ||
statusCode: 400, | ||
message: 'Request body prizes do not match', | ||
}), | ||
}; | ||
} | ||
|
||
let pointsUsed = 0; | ||
for (let i = 0; i < userBuyInsSorted.length; i++) { | ||
if (requestBuyInsSorted[i].prize_id !== userBuyInsSorted[i].prize_id) { | ||
return { | ||
statusCode: 400, | ||
body: JSON.stringify({ | ||
statusCode: 400, | ||
message: 'Request body prizes do not match', | ||
}), | ||
}; | ||
} | ||
pointsUsed += event.body.buy_ins[i].buy_in; | ||
} | ||
|
||
//check that the points used are within the total_points | ||
if (pointsUsed > userPoints.total_points) { | ||
return { | ||
statusCode: 403, | ||
body: JSON.stringify({ | ||
statusCode: 403, | ||
message: 'Points distributed exceed user point total.', | ||
}), | ||
}; | ||
} | ||
|
||
//update the buy_ins array | ||
await pointCollection.updateOne({ email: event.body.email }, { $set: { buy_ins: event.body.buy_ins } }); | ||
return { | ||
statusCode: 200, | ||
body: JSON.stringify({ | ||
statusCode: 200, | ||
message: 'Updated user point balance successfully', | ||
}), | ||
}; | ||
} catch (error) { | ||
console.error('Error updating', error); | ||
return { | ||
statusCode: 500, | ||
body: JSON.stringify({ | ||
statusCode: 500, | ||
message: 'Internal server error', | ||
error, | ||
}), | ||
}; | ||
} | ||
}; | ||
|
||
export const main = middyfy(updateBuyIns); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { handlerPath } from '@libs/handler-resolver'; | ||
import schema from './schema'; | ||
|
||
export default { | ||
handler: `${handlerPath(__dirname)}/handler.main`, | ||
events: [ | ||
{ | ||
http: { | ||
method: 'post', | ||
path: 'update-buy-ins', | ||
cors: true, | ||
request: { | ||
schemas: { | ||
'application/json': schema, | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export default { | ||
type: 'object', | ||
properties: { | ||
email: { type: 'string', format: 'email' }, | ||
auth_token: { type: 'string ' }, | ||
buy_ins: { | ||
type: 'array', | ||
items: { | ||
type: 'object', | ||
properties: { | ||
prize_id: { type: 'string' }, | ||
buy_in: { type: 'number' }, | ||
}, | ||
required: ['prize_id', 'buy_in'], | ||
}, | ||
}, | ||
}, | ||
required: ['email', 'auth_token', 'buy_ins'], | ||
} as const; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { main } from '../src/functions/update-buy-ins/handler'; | ||
import { createEvent, mockContext } from './helper'; | ||
import * as util from '../src/util'; | ||
|
||
jest.mock('../src/util', () => ({ | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
MongoDB: { | ||
getInstance: jest.fn().mockReturnValue({ | ||
connect: jest.fn(), | ||
disconnect: jest.fn(), | ||
getCollection: jest.fn().mockReturnValue({ | ||
findOne: jest.fn(), | ||
updateOne: jest.fn(), | ||
}), | ||
}), | ||
}, | ||
validateToken: jest.fn().mockReturnValueOnce(false).mockReturnValue(true), | ||
})); | ||
|
||
describe('Update-Buy-Ins tests', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); // Clear mocks before each test to avoid interference | ||
}); | ||
|
||
const userData = { | ||
email: 'mockEmail@mock.org', | ||
buy_ins: [ | ||
{ prize_id: 'prize1', buy_in: 10 }, | ||
{ prize_id: 'prize2', buy_in: 20 }, | ||
], | ||
}; | ||
|
||
const path = '/update-buy-ins'; | ||
const httpMethod = 'POST'; | ||
|
||
const findOneMock = util.MongoDB.getInstance('uri').getCollection('users').findOne as jest.Mock; | ||
const mockCallback = jest.fn(); | ||
|
||
// case 1 | ||
it('auth token is not valid', async () => { | ||
const mockEvent = createEvent(userData, path, httpMethod); | ||
|
||
const result = await main(mockEvent, mockContext, mockCallback); | ||
|
||
expect(result.statusCode).toBe(401); | ||
expect(JSON.parse(result.body).message).toBe('Unauthorized'); | ||
}); | ||
|
||
// case 2 | ||
it('user not found', async () => { | ||
findOneMock.mockReturnValueOnce(null); | ||
const mockEvent = createEvent(userData, path, httpMethod); | ||
|
||
const result = await main(mockEvent, mockContext, mockCallback); | ||
|
||
expect(result.statusCode).toBe(404); | ||
expect(JSON.parse(result.body).message).toBe('User point balance information not found'); | ||
}); | ||
|
||
// case 3 | ||
it('prize IDs do not match', async () => { | ||
findOneMock.mockReturnValueOnce({ | ||
email: userData.email, | ||
total_points: 100, | ||
buy_ins: [ | ||
{ prize_id: 'prize2', buy_in: 10 }, | ||
{ prize_id: 'prize2', buy_in: 20 }, | ||
], | ||
}); | ||
|
||
const mockEvent = createEvent(userData, path, httpMethod); | ||
|
||
const result = await main(mockEvent, mockContext, mockCallback); | ||
|
||
expect(result.statusCode).toBe(400); | ||
expect(JSON.parse(result.body).message).toBe('Request body prizes do not match'); | ||
}); | ||
|
||
// case 4 | ||
it('points distributed exceed user point total', async () => { | ||
findOneMock.mockReturnValueOnce({ | ||
email: userData.email, | ||
total_points: 15, | ||
buy_ins: [ | ||
{ prize_id: 'prize1', buy_in: 5 }, | ||
{ prize_id: 'prize2', buy_in: 11 }, | ||
], | ||
}); | ||
|
||
const mockEvent = createEvent(userData, path, httpMethod); | ||
|
||
const result = await main(mockEvent, mockContext, mockCallback); | ||
|
||
expect(result.statusCode).toBe(403); | ||
expect(JSON.parse(result.body).message).toBe('Points distributed exceed user point total.'); | ||
}); | ||
|
||
// case 5 | ||
it('successfully update user point balance', async () => { | ||
findOneMock.mockReturnValueOnce({ | ||
email: userData.email, | ||
total_points: 30, | ||
buy_ins: [ | ||
{ prize_id: 'prize1', buy_in: 10 }, | ||
{ prize_id: 'prize2', buy_in: 20 }, | ||
], | ||
}); | ||
|
||
const mockEvent = createEvent(userData, path, httpMethod); | ||
|
||
const result = await main(mockEvent, mockContext, mockCallback); | ||
|
||
expect(result.statusCode).toBe(200); | ||
expect(JSON.parse(result.body).message).toBe('Updated user point balance successfully'); | ||
}); | ||
}); |