Skip to content

Commit

Permalink
Established Basis for Unit and Integration Testing (#142)
Browse files Browse the repository at this point in the history
* Established Basis for Unit and Integration Testing

* Documentation for test files.

* Documentation for test integration

* remove unused references

* update testdocs to use pnpm

* resovle pnpm lock issue

---------

Co-authored-by: Amar Cerim <[email protected]>
  • Loading branch information
Aman4017 and bambooch authored Oct 25, 2024
1 parent cd802fb commit f2a63d8
Show file tree
Hide file tree
Showing 10 changed files with 6,550 additions and 482 deletions.
4,428 changes: 3,981 additions & 447 deletions backend/package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"name": "backend",
"scripts": {
"dev": "nodemon -r dotenv/config server.js"
"dev": "nodemon -r dotenv/config server.js",
"test": "jest"
},
"dependencies": {
"ajv": "^8.17.1",
Expand All @@ -13,5 +14,9 @@
"nodemailer": "^6.9.15",
"nodemon": "^3.1.0",
"uuid": "^9.0.1"
},
"devDependencies": {
"jest": "^29.7.0",
"supertest": "^7.0.0"
}
}
49 changes: 49 additions & 0 deletions backend/test/agency.controller.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const fs = require('fs');
const path = require('path');
const AgencyController = require('../controllers/agency.controller');
const request = require('supertest');
const express = require('express');


jest.mock('fs');
jest.mock('ajv');


const app = express();
app.use(express.json());
app.use("/agencies", require("../routes/agency.routes"));

describe("AgencyController", () => {
beforeEach(() => {
jest.clearAllMocks();
});


it("should return all agencies", async () => {

fs.readFile.mockImplementation((filePath, encoding, callback) => {
callback(null, JSON.stringify([{ _id: "1", cityId: "123", address: "123 Street", website: "https://example.com", email: "[email protected]", phoneNumber: "+123456789" }]));
});

const response = await request(app).get("/agencies");

expect(response.statusCode).toBe(200);
expect(response.body).toEqual([
{ _id: "1", cityId: "123", address: "123 Street", website: "https://example.com", email: "[email protected]", phoneNumber: "+123456789" }
]);
});


it("should return an agency by id", async () => {
fs.readFile.mockImplementation((filePath, encoding, callback) => {
callback(null, JSON.stringify([{ _id: "1", cityId: "123", address: "123 Street", website: "https://example.com", email: "[email protected]", phoneNumber: "+123456789" }]));
});

const response = await request(app).get("/agencies/1");

expect(response.statusCode).toBe(200);
expect(response.body).toEqual({
_id: "1", cityId: "123", address: "123 Street", website: "https://example.com", email: "[email protected]", phoneNumber: "+123456789"
});
});
});
64 changes: 64 additions & 0 deletions backend/test/city.controller.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const request = require('supertest');
const express = require('express');
const fs = require('fs');
const cityRoutes = require('../routes/city.routes');
const app = express();

jest.mock('fs');

app.use(express.json());
app.use('/cities', cityRoutes);

describe('CityController', () => {
let mockCities;

beforeEach(() => {
mockCities = [
{
"_id": "1",
"countryId": "101",
"name": "CityOne",
"zipCode": 12345,
"coordinates": [1.2345, 2.3456]
},
{
"_id": "2",
"countryId": "102",
"name": "CityTwo",
"zipCode": 67890,
"coordinates": [3.4567, 4.5678]
}
];

fs.readFile.mockImplementation((filePath, encoding, callback) => {
callback(null, JSON.stringify(mockCities));
});

fs.writeFile.mockImplementation((filePath, data, callback) => {
callback(null);
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should return all cities', async () => {
const response = await request(app).get('/cities');
expect(response.statusCode).toBe(200);
expect(response.body.length).toBe(2);
expect(response.body).toEqual(mockCities);
});

it('should return a city by ID', async () => {
const response = await request(app).get('/cities/1');
expect(response.statusCode).toBe(200);
expect(response.body).toEqual(mockCities[0]);
});

it('should return 404 if city not found', async () => {
const response = await request(app).get('/cities/999');
expect(response.statusCode).toBe(404);
expect(response.body).toEqual({ message: 'Item not found' });
});
});
46 changes: 46 additions & 0 deletions backend/test/country.controller.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const request = require('supertest');
const express = require('express');
const fs = require('fs');
const countryRoutes = require('../routes/country.routes');
const app = express();

jest.mock('fs');

app.use(express.json());

app.use('/countries', countryRoutes);

describe('CountryController', () => {
const mockCountries = [
{ _id: '1', name: 'Country1', code: '001', acronym: 'C1' },
{ _id: '2', name: 'Country2', code: '002', acronym: 'C2' },
];

beforeEach(() => {
fs.readFile.mockImplementation((filePath, encoding, callback) => {
callback(null, JSON.stringify(mockCountries));
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should return all countries', async () => {
const response = await request(app).get('/countries');
expect(response.statusCode).toBe(200);
expect(response.body).toEqual(mockCountries);
});

it('should return a country by id', async () => {
const response = await request(app).get('/countries/1');
expect(response.statusCode).toBe(200);
expect(response.body).toEqual(mockCountries[0]);
});

it('should return 404 if country not found', async () => {
const response = await request(app).get('/countries/999');
expect(response.statusCode).toBe(404);
expect(response.body).toEqual({ message: 'Item not found' });
});
});
154 changes: 154 additions & 0 deletions backend/test/email.controller.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
const request = require("supertest");
const express = require("express");
const EmailController = require("../controllers/email.controller");
const nodemailer = require("nodemailer");

jest.mock("nodemailer");

const app = express();
app.use(express.json());

app.post("/email", EmailController.sendEmail);

describe("EmailController", () => {
let transporterMock;

beforeAll(() => {
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
});

afterAll(() => {
console.log.mockRestore();
console.error.mockRestore();
});

beforeEach(() => {
transporterMock = {
sendMail: jest.fn().mockResolvedValue({
response: "250 OK: message queued",
}),
};
nodemailer.createTransport.mockReturnValue(transporterMock);
});

afterEach(() => {
jest.clearAllMocks();
});

it("should send an email successfully with valid input", async () => {
const validPayload = {
subject: "Test Subject",
text: "This is a test email body.",
senderName: "John Doe",
senderEmail: "[email protected]",
};

const response = await request(app)
.post("/email")
.send(validPayload);

expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty("success", "Email sent successfully.");
expect(transporterMock.sendMail).toHaveBeenCalledTimes(1);
expect(transporterMock.sendMail).toHaveBeenCalledWith({
from: "John Doe <[email protected]>",
to: `${process.env.DEFAULT_RECEIVER_NAME} <${process.env.DEFAULT_RECEIVER_EMAIL}>`,
subject: "Test Subject",
text: "This is a test email body.",
});
});


it("should return a 400 error if required fields are missing", async () => {
const invalidPayload = {
text: "This is a test email body.",
};

const response = await request(app)
.post("/email")
.send(invalidPayload);

expect(response.statusCode).toBe(400);
expect(response.body).toHaveProperty(
"error",
"Subject, text, and sender name are required."
);
expect(transporterMock.sendMail).not.toHaveBeenCalled();
});

it("should return an authentication error if SMTP credentials are invalid", async () => {
transporterMock.sendMail.mockRejectedValue({
code: "EAUTH",
message: "Invalid login credentials",
});

const validPayload = {
subject: "Test Subject",
text: "This is a test email body.",
senderName: "John Doe",
senderEmail: "[email protected]",
};

const response = await request(app)
.post("/email")
.send(validPayload);

expect(response.statusCode).toBe(500);
expect(response.body).toHaveProperty(
"error",
"Authentication failed. Check your SMTP credentials."
);
expect(transporterMock.sendMail).toHaveBeenCalledTimes(1);
});

it("should return 404 error if SMTP service is not found", async () => {
transporterMock.sendMail.mockRejectedValue({
responseCode: 404,
message: "SMTP service not found",
});

const validPayload = {
subject: "Test Subject",
text: "This is a test email body.",
senderName: "John Doe",
senderEmail: "[email protected]",
};

const response = await request(app)
.post("/email")
.send(validPayload);

expect(response.statusCode).toBe(404);
expect(response.body).toHaveProperty(
"error",
"SMTP service not found. Check your API configuration."
);
expect(transporterMock.sendMail).toHaveBeenCalledTimes(1);
});

it("should return 500 error for server-related issues", async () => {
transporterMock.sendMail.mockRejectedValue({
responseCode: 500,
message: "Internal server error",
});

const validPayload = {
subject: "Test Subject",
text: "This is a test email body.",
senderName: "John Doe",
senderEmail: "[email protected]",
};

const response = await request(app)
.post("/email")
.send(validPayload);

expect(response.statusCode).toBe(500);
expect(response.body).toHaveProperty(
"error",
"Server error. Please try again later."
);
expect(transporterMock.sendMail).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit f2a63d8

Please sign in to comment.