Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions challenge/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.serverless
69 changes: 69 additions & 0 deletions challenge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!--
title: 'AWS Simple HTTP Endpoint example in NodeJS'
description: 'This template demonstrates how to make a simple HTTP API with Node.js running on AWS Lambda and API Gateway using the Serverless Framework.'
layout: Doc
framework: v4
platform: AWS
language: nodeJS
authorLink: 'https://github.com/serverless'
authorName: 'Serverless, Inc.'
authorAvatar: 'https://avatars1.githubusercontent.com/u/13742415?s=200&v=4'
-->

# Serverless Framework Node HTTP API on AWS

This template demonstrates how to make a simple HTTP API with Node.js running on AWS Lambda and API Gateway using the Serverless Framework.

This template does not include any kind of persistence (database). For more advanced examples, check out the [serverless/examples repository](https://github.com/serverless/examples/) which includes Typescript, Mongo, DynamoDB and other examples.

## Usage

### Deployment

In order to deploy the example, you need to run the following command:

```
serverless deploy
```

After running deploy, you should see output similar to:

```
Deploying "serverless-http-api" to stage "dev" (us-east-1)

✔ Service deployed to stack serverless-http-api-dev (91s)

endpoint: GET - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/
functions:
hello: serverless-http-api-dev-hello (1.6 kB)
```

_Note_: In current form, after deployment, your API is public and can be invoked by anyone. For production deployments, you might want to configure an authorizer. For details on how to do that, refer to [HTTP API (API Gateway V2) event docs](https://www.serverless.com/framework/docs/providers/aws/events/http-api).

### Invocation

After successful deployment, you can call the created application via HTTP:

```
curl https://xxxxxxx.execute-api.us-east-1.amazonaws.com/
```

Which should result in response similar to:

```json
{ "message": "Go Serverless v4! Your function executed successfully!" }
```

### Local development

The easiest way to develop and test your function is to use the `dev` command:

```
serverless dev
```

This will start a local emulator of AWS Lambda and tunnel your requests to and from AWS Lambda, allowing you to interact with your function as if it were running in the cloud.

Now you can invoke the function as before, but this time the function will be executed locally. Now you can develop your function locally, invoke it, and see the results immediately without having to re-deploy.

When you are done developing, don't forget to run `serverless deploy` to deploy the function to the cloud.
111 changes: 111 additions & 0 deletions challenge/__tests__/handler.exec-payments.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const { executePayments } = require('../src/handlers/exec-payments/exec-payments');
const axios = require('axios');
const dynamodb = require('../src/db/db');
const { v4: uuidv4 } = require('uuid');

jest.mock('axios');

jest.mock('../src/db/db', () => ({
put: jest.fn(),
}));

describe('executePayments', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return 201 and transactionId if payment succeeds', async () => {
const event = {
body: JSON.stringify({
userId: 'user123',
amount: 100,
}),
};
const mockResponse = {
data: {
status: 'success',
transactionId: uuidv4(),
},
};
axios.post.mockResolvedValueOnce(mockResponse);

const mockPutResponse = {};
dynamodb.put.mockReturnValueOnce({
promise: jest.fn().mockResolvedValueOnce(mockPutResponse),
});

const result = await executePayments(event);
console.log({ result: JSON.stringify(result) });

expect(result.statusCode).toBe(201);
expect(JSON.parse(result.body).message).toBe('Payment registered successfully');
expect(JSON.parse(result.body).transactionId).toBeTruthy();
expect(dynamodb.put).toHaveBeenCalled();
});

it('should return 400 if payment status is not success', async () => {
const event = {
body: JSON.stringify({
userId: 'user123',
amount: 100,
}),
};
const mockResponse = {
data: {
status: 'failure',
},
};
axios.post.mockResolvedValueOnce(mockResponse);

const result = await executePayments(event);

expect(result.statusCode).toBe(400);
expect(JSON.parse(result.body).message).toBe('Something was wrong');
expect(dynamodb.put).not.toHaveBeenCalled();
});

it('should return 500 on axios error', async () => {
const event = {
body: JSON.stringify({
userId: 'user123',
amount: 100,
}),
};
const errorMessage = 'Axios error';
axios.post.mockRejectedValueOnce(new Error(errorMessage));

const result = await executePayments(event);

expect(result.statusCode).toBe(500);
expect(JSON.parse(result.body).message).toBe('Internal Server Error');
expect(JSON.parse(result.body).error).toBe(errorMessage);
expect(dynamodb.put).not.toHaveBeenCalled();
});

it('should return 500 on DynamoDB error', async () => {
const event = {
body: JSON.stringify({
userId: 'user123',
amount: 100,
}),
};
const mockResponse = {
data: {
status: 'success',
},
};
axios.post.mockResolvedValueOnce(mockResponse);

const errorMessage = 'DynamoDB error';
dynamodb.put.mockReturnValueOnce({
promise: jest.fn().mockRejectedValueOnce(new Error(errorMessage)),
});

const result = await executePayments(event);

expect(result.statusCode).toBe(500);
expect(JSON.parse(result.body).message).toBe('Internal Server Error');
expect(JSON.parse(result.body).error).toBe(errorMessage);
expect(dynamodb.put).toHaveBeenCalled();
});
});
93 changes: 93 additions & 0 deletions challenge/__tests__/handler.get-transaction.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const { getTransaction } = require('../src/handlers/get-transaction/get-transaction'); // Ajusta la ruta según tu estructura de archivos
const dynamodb = require('../src/db/db');

jest.mock('../src/db/db', () => ({
get: jest.fn(),
}));

describe('getTransaction', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return 200 and transaction record if transaction exists', async () => {
const transactionId = 'transaction123';
const event = {
queryStringParameters: {
transaction_id: transactionId,
},
};
const mockResponse = {
Item: {
transactionId: 'transaction123',
amount: 100,
status: 'completed',
}, // Simula que la transacción existe en DynamoDB
};
dynamodb.get.mockReturnValueOnce({
promise: jest.fn().mockResolvedValueOnce(mockResponse),
});

const result = await getTransaction(event);

expect(result.statusCode).toBe(200);
expect(JSON.parse(result.body)).toEqual(mockResponse.Item);
expect(dynamodb.get).toHaveBeenCalledWith({
TableName: 'transactions',
Key: {
transactionId: 'transaction123',
},
});
});

it('should return 404 and message if transaction does not exist', async () => {
const transactionId = 'transaction456';
const event = {
queryStringParameters: {
transaction_id: transactionId,
},
};
const mockResponse = {
Item: null, // Simula que la transacción no existe en DynamoDB
};
dynamodb.get.mockReturnValueOnce({
promise: jest.fn().mockResolvedValueOnce(mockResponse),
});

const result = await getTransaction(event);

expect(result.statusCode).toBe(404);
expect(JSON.parse(result.body).message).toBe('Transacción no encontrado');
expect(dynamodb.get).toHaveBeenCalledWith({
TableName: 'transactions',
Key: {
transactionId: 'transaction456',
},
});
});

it('should return 500 and error message on DynamoDB error', async () => {
const transactionId = 'transaction789';
const event = {
queryStringParameters: {
transaction_id: transactionId,
},
};
const errorMessage = 'Database error';
dynamodb.get.mockReturnValueOnce({
promise: jest.fn().mockRejectedValueOnce(new Error(errorMessage)),
});

const result = await getTransaction(event);

expect(result.statusCode).toBe(500);
expect(JSON.parse(result.body).message).toBe('Error interno del servidor');
expect(JSON.parse(result.body).error).toBe(errorMessage);
expect(dynamodb.get).toHaveBeenCalledWith({
TableName: 'transactions',
Key: {
transactionId: 'transaction789',
},
});
});
});
71 changes: 71 additions & 0 deletions challenge/__tests__/handler.register-activity.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const { registerActivity } = require('../src/handlers/register-activity/register-activity'); // Ajusta la ruta según tu estructura de archivos
const dynamodb = require('../src/db/db');

jest.mock('../src/db/db', () => ({
put: jest.fn(),
}));

describe('registerActivity', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should register activity successfully', async () => {
const event = {
Records: [
{
dynamodb: {
NewImage: {
transactionId: { S: 'transaction123' },
userId: { S: 'user123' },
},
},
},
],
};

const mockPutResponse = {};
dynamodb.put.mockReturnValueOnce({
promise: jest.fn().mockResolvedValueOnce(mockPutResponse),
});

await registerActivity(event);

expect(dynamodb.put).toHaveBeenCalledTimes(1);
expect(dynamodb.put).toHaveBeenCalledWith({
TableName: 'activity',
Item: {
activityId: expect.any(String),
transactionId: 'transaction123',
date: expect.any(String),
detail: 'Transaction transaction123 for user user123 registered.',
},
});
});

it('should handle error when registering activity fails', async () => {
const event = {
Records: [
{
dynamodb: {
NewImage: {
transactionId: { S: 'transaction123' },
userId: { S: 'user123' },
},
},
},
],
};

const errorMessage = 'DynamoDB error';
dynamodb.put.mockReturnValueOnce({
promise: jest.fn().mockRejectedValueOnce(new Error(errorMessage)),
});

const consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {});

await registerActivity(event);

expect(consoleErrorMock).toHaveBeenCalledWith('Error registering activity:', errorMessage);
});
});
Loading