Commit
·
e08ecbf
1
Parent(s):
ba6ba3d
initial_update
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +27 -0
- Dockerfile +40 -0
- README.md +4 -4
- backend/__tests__/config/googleOAuth.test.js +77 -0
- backend/__tests__/config/refreshTokens.test.js +101 -0
- backend/__tests__/controller/applicantController.test.js +132 -0
- backend/__tests__/controller/authController.test.js +81 -0
- backend/__tests__/controller/contactController.test.js +198 -0
- backend/__tests__/controller/demo/dateAvailability.test.js +81 -0
- backend/__tests__/controller/demo/holidays.test.js +128 -0
- backend/__tests__/controller/demo/slots.test.js +142 -0
- backend/__tests__/controller/demo/utils.test.js +77 -0
- backend/__tests__/controller/demoRequestController.test.js +293 -0
- backend/__tests__/controller/subscriptionController.test.js +131 -0
- backend/__tests__/db.test.js +64 -0
- backend/__tests__/db/addSubscriber.test.js +95 -0
- backend/__tests__/db/contactRequestDB.test.js +90 -0
- backend/__tests__/db/demoRequestDB.test.js +120 -0
- backend/__tests__/db/jobRequestDB.test.js +105 -0
- backend/__tests__/routes/applicantRoutes.test.js +73 -0
- backend/__tests__/testSetup.js +0 -0
- backend/allure-results/0015a6cd-e653-4a79-ba26-0af43fabbf07-result.json +1 -0
- backend/allure-results/0110d1ba-d337-4de1-9021-f9af16a0683f-container.json +1 -0
- backend/allure-results/016c5d0f-35ce-411e-90a1-61de74ab2ca3-container.json +1 -0
- backend/allure-results/01ea98eb-5e95-43b8-b98c-48074cf6284d-container.json +1 -0
- backend/allure-results/01f5e8cb-ddab-404c-b698-c33f012c014b-container.json +1 -0
- backend/allure-results/02297a4a-89e7-479a-b92e-a8cd2bee7916-container.json +1 -0
- backend/allure-results/02c0b00b-1aa8-498b-83a2-1a37af9977ba-result.json +1 -0
- backend/allure-results/02f5da12-8970-4dff-aeb3-75c9714f2818-container.json +1 -0
- backend/allure-results/0336cd99-4664-4319-8288-b6712f0f9353-container.json +1 -0
- backend/allure-results/0394dd9b-43c3-4ae1-93e9-563120bc3609-container.json +1 -0
- backend/allure-results/04c86ce6-b07e-4c64-af20-1a52682cc83d-container.json +1 -0
- backend/allure-results/04ea9714-733a-45e9-b0cc-7cc1aa55db15-container.json +1 -0
- backend/allure-results/05878ed9-0262-45d3-a27e-270827d51608-result.json +1 -0
- backend/allure-results/05cbfb2c-ea16-4f24-bd3d-f2386b85cde6-container.json +1 -0
- backend/allure-results/06bb4d63-55c7-44df-9572-66d7583c1658-container.json +1 -0
- backend/allure-results/077c5367-cc30-4fef-83c9-68b2137156b1-container.json +1 -0
- backend/allure-results/07e2e71f-c12c-42aa-83ef-ae7c75fc7a73-result.json +1 -0
- backend/allure-results/089e2be5-c31a-455c-841e-3f676ddfd3f6-result.json +1 -0
- backend/allure-results/092b09aa-d891-4211-b725-2cb53f0bc367-result.json +1 -0
- backend/allure-results/098ef27c-b326-44df-9190-c2f20c1ada8e-container.json +1 -0
- backend/allure-results/0a34e919-c2a0-47c0-8c9c-f7baa3bb0098-result.json +1 -0
- backend/allure-results/0a4254f9-1cdb-4c56-8bba-bfc9e2424b4d-container.json +1 -0
- backend/allure-results/0a5de36d-ec71-4d0a-9d60-f17bc71ead0f-result.json +1 -0
- backend/allure-results/0bdecd77-aed5-4cbc-bec8-4149ac95cf00-container.json +1 -0
- backend/allure-results/0ca292e2-e5f8-4bfd-959d-46fb6c883444-result.json +1 -0
- backend/allure-results/0d6fb9a5-b36a-4e35-b985-d89ac351cc09-container.json +1 -0
- backend/allure-results/0da168e7-8953-4657-9458-dd5e186abe96-result.json +1 -0
- backend/allure-results/0dc24df0-7b61-44bb-af09-0f71a0d88f53-container.json +1 -0
- backend/allure-results/0dd25a26-4fcb-4370-a3e5-52758cee400a-result.json +1 -0
.gitignore
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Ignore test-related folders
|
2 |
+
allure_report/
|
3 |
+
__test__/
|
4 |
+
__mocks__/
|
5 |
+
coverage/
|
6 |
+
test_report/
|
7 |
+
|
8 |
+
# Ignore test configuration and setup files
|
9 |
+
jest.config.js
|
10 |
+
jest.setup.js
|
11 |
+
jesthtmlreporter.config.json
|
12 |
+
babel.config.js
|
13 |
+
postTestSetup.js
|
14 |
+
preTestSetup.js
|
15 |
+
runE2ETests.js
|
16 |
+
|
17 |
+
# Ignore environment files
|
18 |
+
.env
|
19 |
+
|
20 |
+
# Ignore database files
|
21 |
+
data.db
|
22 |
+
|
23 |
+
# Ignore video files
|
24 |
+
*.mp4
|
25 |
+
|
26 |
+
# Ignore node_modules
|
27 |
+
node_modules
|
Dockerfile
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Stage 1: Build the React frontend
|
2 |
+
FROM node:16 AS build
|
3 |
+
|
4 |
+
# Set the working directory
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy the React frontend code
|
8 |
+
COPY frontend/package.json frontend/package-lock.json ./frontend/
|
9 |
+
RUN cd frontend && npm install
|
10 |
+
|
11 |
+
# Build the React app (inside the frontend folder)
|
12 |
+
COPY frontend ./frontend
|
13 |
+
RUN cd frontend && npm run build
|
14 |
+
|
15 |
+
# Stage 2: Set up the Node.js backend
|
16 |
+
FROM node:16
|
17 |
+
|
18 |
+
# Set the working directory
|
19 |
+
WORKDIR /app
|
20 |
+
|
21 |
+
# Copy backend code
|
22 |
+
COPY backend/package.json backend/package-lock.json ./backend/
|
23 |
+
RUN cd backend && npm install
|
24 |
+
|
25 |
+
# Copy the React build files from Stage 1 to the backend/build folder
|
26 |
+
COPY --from=build /app/frontend/build ./backend/build
|
27 |
+
|
28 |
+
# Copy the backend source code
|
29 |
+
COPY backend ./backend
|
30 |
+
|
31 |
+
RUN mkdir -p /app/backend/database && chmod -R 777 /app/backend/database
|
32 |
+
|
33 |
+
|
34 |
+
# Expose the backend's port
|
35 |
+
EXPOSE 7860
|
36 |
+
|
37 |
+
WORKDIR /app/backend
|
38 |
+
|
39 |
+
# Start the backend server
|
40 |
+
CMD [ "node", "./index.js"]
|
README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
|
|
1 |
---
|
2 |
+
title: Geno React Modularised
|
3 |
+
emoji: 🏃
|
4 |
+
colorFrom: pink
|
5 |
+
colorTo: red
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
backend/__tests__/config/googleOAuth.test.js
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { google } = require('googleapis'); // Import googleapis here
|
2 |
+
jest.mock('googleapis', () => ({
|
3 |
+
google: {
|
4 |
+
auth: {
|
5 |
+
OAuth2: jest.fn().mockImplementation(() => ({
|
6 |
+
setCredentials: jest.fn(),
|
7 |
+
isTokenExpiring: jest.fn(),
|
8 |
+
refreshAccessToken: jest.fn(),
|
9 |
+
})),
|
10 |
+
},
|
11 |
+
},
|
12 |
+
}));
|
13 |
+
|
14 |
+
describe('googleOAuth client', () => {
|
15 |
+
beforeEach(() => {
|
16 |
+
// Clear all mocks before each test
|
17 |
+
jest.clearAllMocks();
|
18 |
+
process.env.CLIENT_ID = 'test-client-id';
|
19 |
+
process.env.CLIENT_SECRET = 'test-client-secret';
|
20 |
+
process.env.REDIRECT_URI = 'http://localhost';
|
21 |
+
});
|
22 |
+
|
23 |
+
it('should initialize OAuth2 client with credentials from .env', () => {
|
24 |
+
// Import the oAuth2Client after setting environment variables and mocks
|
25 |
+
const { oAuth2Client } = require('../../config/googleOAuth');
|
26 |
+
|
27 |
+
// Test if OAuth2 constructor was called with the expected parameters
|
28 |
+
expect(google.auth.OAuth2).toHaveBeenCalledWith(
|
29 |
+
'test-client-id',
|
30 |
+
'test-client-secret',
|
31 |
+
'http://localhost'
|
32 |
+
);
|
33 |
+
expect(oAuth2Client).toBeDefined();
|
34 |
+
});
|
35 |
+
|
36 |
+
it('should call setCredentials on OAuth2 client with proper tokens', async () => {
|
37 |
+
const tokens = { access_token: 'access-token', refresh_token: 'refresh-token' };
|
38 |
+
|
39 |
+
const { oAuth2Client } = require('../../config/googleOAuth'); // Ensure this is required here
|
40 |
+
oAuth2Client.setCredentials(tokens);
|
41 |
+
|
42 |
+
expect(oAuth2Client.setCredentials).toHaveBeenCalledWith(tokens);
|
43 |
+
});
|
44 |
+
|
45 |
+
it('should call refreshAccessToken and handle success', async () => {
|
46 |
+
const mockRefreshResponse = {
|
47 |
+
credentials: {
|
48 |
+
access_token: 'new-access-token',
|
49 |
+
refresh_token: 'new-refresh-token',
|
50 |
+
},
|
51 |
+
};
|
52 |
+
|
53 |
+
const { oAuth2Client } = require('../../config/googleOAuth'); // Ensure this is required here
|
54 |
+
oAuth2Client.refreshAccessToken.mockResolvedValueOnce(mockRefreshResponse);
|
55 |
+
|
56 |
+
const response = await oAuth2Client.refreshAccessToken();
|
57 |
+
expect(response.credentials.access_token).toBe('new-access-token');
|
58 |
+
expect(oAuth2Client.refreshAccessToken).toHaveBeenCalled();
|
59 |
+
});
|
60 |
+
|
61 |
+
it('should call refreshAccessToken and handle error', async () => {
|
62 |
+
const { oAuth2Client } = require('../../config/googleOAuth'); // Ensure this is required here
|
63 |
+
oAuth2Client.refreshAccessToken.mockRejectedValueOnce(new Error('Token refresh failed'));
|
64 |
+
|
65 |
+
await expect(oAuth2Client.refreshAccessToken()).rejects.toThrow('Token refresh failed');
|
66 |
+
expect(oAuth2Client.refreshAccessToken).toHaveBeenCalled();
|
67 |
+
});
|
68 |
+
|
69 |
+
it('should correctly check if the token is expiring', () => {
|
70 |
+
const { oAuth2Client } = require('../../config/googleOAuth'); // Ensure this is required here
|
71 |
+
oAuth2Client.isTokenExpiring.mockReturnValue(true);
|
72 |
+
|
73 |
+
const isExpiring = oAuth2Client.isTokenExpiring();
|
74 |
+
expect(isExpiring).toBe(true);
|
75 |
+
expect(oAuth2Client.isTokenExpiring).toHaveBeenCalled();
|
76 |
+
});
|
77 |
+
});
|
backend/__tests__/config/refreshTokens.test.js
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { checkAndRefreshAccessToken } = require('../../utils/refreshToken');
|
2 |
+
const { oAuth2Client } = require('../../config/googleOAuth');
|
3 |
+
|
4 |
+
jest.mock('../../config/googleOAuth', () => ({
|
5 |
+
oAuth2Client: {
|
6 |
+
setCredentials: jest.fn(),
|
7 |
+
isTokenExpiring: jest.fn(),
|
8 |
+
refreshAccessToken: jest.fn(),
|
9 |
+
},
|
10 |
+
}));
|
11 |
+
|
12 |
+
describe('checkAndRefreshAccessToken', () => {
|
13 |
+
beforeEach(() => {
|
14 |
+
jest.clearAllMocks();
|
15 |
+
// Set mock tokens in environment before each test
|
16 |
+
process.env.ACCESS_TOKEN = 'test-access-token';
|
17 |
+
process.env.REFRESH_TOKEN = 'test-refresh-token';
|
18 |
+
});
|
19 |
+
|
20 |
+
afterEach(() => {
|
21 |
+
// Clear the environment variables after each test
|
22 |
+
delete process.env.ACCESS_TOKEN;
|
23 |
+
delete process.env.REFRESH_TOKEN;
|
24 |
+
});
|
25 |
+
|
26 |
+
it('should use tokens from .env and set credentials', async () => {
|
27 |
+
oAuth2Client.isTokenExpiring.mockReturnValue(false);
|
28 |
+
|
29 |
+
const tokens = await checkAndRefreshAccessToken();
|
30 |
+
|
31 |
+
expect(oAuth2Client.setCredentials).toHaveBeenCalledWith({
|
32 |
+
access_token: 'test-access-token',
|
33 |
+
refresh_token: 'test-refresh-token',
|
34 |
+
});
|
35 |
+
expect(tokens).toEqual({
|
36 |
+
access_token: 'test-access-token',
|
37 |
+
refresh_token: 'test-refresh-token',
|
38 |
+
});
|
39 |
+
});
|
40 |
+
|
41 |
+
it('should refresh tokens if the access token is expiring', async () => {
|
42 |
+
oAuth2Client.isTokenExpiring.mockReturnValue(true);
|
43 |
+
oAuth2Client.refreshAccessToken.mockResolvedValueOnce({
|
44 |
+
credentials: {
|
45 |
+
access_token: 'new-access-token',
|
46 |
+
refresh_token: 'new-refresh-token',
|
47 |
+
},
|
48 |
+
});
|
49 |
+
|
50 |
+
const tokens = await checkAndRefreshAccessToken();
|
51 |
+
|
52 |
+
expect(oAuth2Client.refreshAccessToken).toHaveBeenCalled();
|
53 |
+
expect(oAuth2Client.setCredentials).toHaveBeenCalledWith({
|
54 |
+
access_token: 'new-access-token',
|
55 |
+
refresh_token: 'new-refresh-token',
|
56 |
+
});
|
57 |
+
expect(tokens).toEqual({
|
58 |
+
access_token: 'new-access-token',
|
59 |
+
refresh_token: 'new-refresh-token',
|
60 |
+
});
|
61 |
+
});
|
62 |
+
|
63 |
+
it('should log an error if token refresh fails', async () => {
|
64 |
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
65 |
+
oAuth2Client.isTokenExpiring.mockReturnValue(true);
|
66 |
+
oAuth2Client.refreshAccessToken.mockRejectedValueOnce(new Error('Refresh failed'));
|
67 |
+
|
68 |
+
const tokens = await checkAndRefreshAccessToken();
|
69 |
+
|
70 |
+
expect(oAuth2Client.refreshAccessToken).toHaveBeenCalled();
|
71 |
+
expect(consoleErrorSpy).toHaveBeenCalledWith('Error refreshing access token:', expect.any(Error));
|
72 |
+
expect(tokens).toEqual({
|
73 |
+
access_token: 'test-access-token',
|
74 |
+
refresh_token: 'test-refresh-token',
|
75 |
+
});
|
76 |
+
|
77 |
+
consoleErrorSpy.mockRestore();
|
78 |
+
});
|
79 |
+
|
80 |
+
it('should log a message if no tokens are found in .env', async () => {
|
81 |
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
82 |
+
|
83 |
+
// Set the environment variables to undefined to simulate missing tokens
|
84 |
+
delete process.env.ACCESS_TOKEN;
|
85 |
+
delete process.env.REFRESH_TOKEN;
|
86 |
+
|
87 |
+
const tokens = await checkAndRefreshAccessToken();
|
88 |
+
|
89 |
+
// Ensure the log message was printed
|
90 |
+
expect(consoleLogSpy).toHaveBeenCalledWith('No tokens found in .env file. Please authenticate.');
|
91 |
+
|
92 |
+
// Ensure that setCredentials is NOT called because no tokens were provided
|
93 |
+
expect(oAuth2Client.setCredentials).not.toHaveBeenCalled();
|
94 |
+
|
95 |
+
// Check that the returned tokens are empty as expected
|
96 |
+
expect(tokens).toEqual({});
|
97 |
+
|
98 |
+
// Clean up the spy
|
99 |
+
consoleLogSpy.mockRestore();
|
100 |
+
});
|
101 |
+
});
|
backend/__tests__/controller/applicantController.test.js
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { submitJobApplication } = require('../../controller/applicantController');
|
2 |
+
const { checkExistingJobApplication, insertJobApplication } = require('../../utils/jobRequestDB');
|
3 |
+
const { checkAndRefreshAccessToken } = require('../../utils/refreshToken');
|
4 |
+
const { google } = require('googleapis');
|
5 |
+
const { oAuth2Client } = require('../../config/googleOAuth');
|
6 |
+
|
7 |
+
// Mock the dependencies
|
8 |
+
jest.mock('../../utils/jobRequestDB', () => ({
|
9 |
+
checkExistingJobApplication: jest.fn(),
|
10 |
+
insertJobApplication: jest.fn(),
|
11 |
+
}));
|
12 |
+
|
13 |
+
jest.mock('../../utils/refreshToken', () => ({
|
14 |
+
checkAndRefreshAccessToken: jest.fn(),
|
15 |
+
}));
|
16 |
+
|
17 |
+
// Mock googleapis and oAuth2Client setup
|
18 |
+
jest.mock('googleapis', () => ({
|
19 |
+
google: {
|
20 |
+
auth: {
|
21 |
+
OAuth2: jest.fn().mockImplementation(() => ({
|
22 |
+
setCredentials: jest.fn(),
|
23 |
+
})),
|
24 |
+
},
|
25 |
+
gmail: jest.fn().mockReturnValue({
|
26 |
+
users: {
|
27 |
+
messages: {
|
28 |
+
send: jest.fn(),
|
29 |
+
},
|
30 |
+
},
|
31 |
+
}),
|
32 |
+
},
|
33 |
+
}));
|
34 |
+
|
35 |
+
describe('submitJobApplication', () => {
|
36 |
+
let req;
|
37 |
+
let res;
|
38 |
+
|
39 |
+
beforeEach(() => {
|
40 |
+
// Mock request and response objects
|
41 |
+
req = {
|
42 |
+
body: {
|
43 |
+
name: 'John Doe',
|
44 |
+
email: '[email protected]',
|
45 |
+
phone: '1234567890',
|
46 |
+
experience: '5 years',
|
47 |
+
role: 'Software Engineer',
|
48 |
+
linkedin: 'https://linkedin.com/in/johndoe',
|
49 |
+
},
|
50 |
+
file: {
|
51 |
+
originalname: 'resume.pdf',
|
52 |
+
buffer: Buffer.from('file data'),
|
53 |
+
},
|
54 |
+
};
|
55 |
+
res = {
|
56 |
+
status: jest.fn().mockReturnThis(), // Mocking status method to return 'res' object itself
|
57 |
+
send: jest.fn().mockReturnThis(), // Mocking send method
|
58 |
+
json: jest.fn().mockReturnThis(), // Mocking json method
|
59 |
+
};
|
60 |
+
|
61 |
+
// Clear previous mocks
|
62 |
+
jest.clearAllMocks();
|
63 |
+
});
|
64 |
+
|
65 |
+
it('should return an error if any required fields are missing', async () => {
|
66 |
+
req.body.email = ''; // Missing email
|
67 |
+
|
68 |
+
await submitJobApplication(req, res);
|
69 |
+
|
70 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
71 |
+
expect(res.json).toHaveBeenCalledWith({ error: 'Please fill in all required fields.' });
|
72 |
+
});
|
73 |
+
|
74 |
+
it('should return an error if email format is invalid', async () => {
|
75 |
+
req.body.email = 'invalid-email'; // Invalid email format
|
76 |
+
|
77 |
+
await submitJobApplication(req, res);
|
78 |
+
|
79 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
80 |
+
expect(res.json).toHaveBeenCalledWith({ error: 'Please enter a valid email address.' });
|
81 |
+
});
|
82 |
+
|
83 |
+
it('should refresh access token if needed', async () => {
|
84 |
+
await submitJobApplication(req, res);
|
85 |
+
|
86 |
+
expect(checkAndRefreshAccessToken).toHaveBeenCalled();
|
87 |
+
});
|
88 |
+
|
89 |
+
it('should return an error if job application already exists', async () => {
|
90 |
+
checkExistingJobApplication.mockResolvedValueOnce(true); // Simulate an existing job application
|
91 |
+
|
92 |
+
await submitJobApplication(req, res);
|
93 |
+
|
94 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
95 |
+
expect(res.send).toHaveBeenCalledWith({
|
96 |
+
error: 'Your Job request for the role Software Engineer is already in queue'
|
97 |
+
});
|
98 |
+
});
|
99 |
+
|
100 |
+
it('should successfully submit job application and send an email', async () => {
|
101 |
+
checkExistingJobApplication.mockResolvedValueOnce(false); // Simulate no existing application
|
102 |
+
insertJobApplication.mockResolvedValueOnce(); // Simulate successful insertion
|
103 |
+
|
104 |
+
// Mocking Gmail API call
|
105 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({
|
106 |
+
data: { id: 'message-id' },
|
107 |
+
});
|
108 |
+
|
109 |
+
await submitJobApplication(req, res);
|
110 |
+
|
111 |
+
expect(res.status).toHaveBeenCalledWith(200);
|
112 |
+
expect(res.json).toHaveBeenCalledWith({ message: 'Application submitted successfully!', response: { data: { id: 'message-id' } } });
|
113 |
+
|
114 |
+
// Check if email was sent
|
115 |
+
expect(google.gmail().users.messages.send).toHaveBeenCalled();
|
116 |
+
});
|
117 |
+
|
118 |
+
it('should handle errors while sending the email', async () => {
|
119 |
+
checkExistingJobApplication.mockResolvedValueOnce(false); // Simulate no existing application
|
120 |
+
insertJobApplication.mockResolvedValueOnce(); // Simulate successful insertion
|
121 |
+
|
122 |
+
// Simulate email sending failure
|
123 |
+
google.gmail().users.messages.send.mockRejectedValueOnce(new Error('Email sending failed'));
|
124 |
+
|
125 |
+
await submitJobApplication(req, res);
|
126 |
+
|
127 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
128 |
+
expect(res.json).toHaveBeenCalledWith({
|
129 |
+
error: 'Failed to send job application reception email, however your job application has been received successfully!',
|
130 |
+
});
|
131 |
+
});
|
132 |
+
});
|
backend/__tests__/controller/authController.test.js
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { redirectToGoogleOAuth, handleOAuthCallback } = require('../../controller/authController');
|
2 |
+
const { oAuth2Client } = require('../../config/googleOAuth');
|
3 |
+
|
4 |
+
// Mock dependencies
|
5 |
+
jest.mock('../../config/googleOAuth', () => ({
|
6 |
+
oAuth2Client: {
|
7 |
+
generateAuthUrl: jest.fn(),
|
8 |
+
getToken: jest.fn(),
|
9 |
+
setCredentials: jest.fn(),
|
10 |
+
}
|
11 |
+
}));
|
12 |
+
|
13 |
+
describe('Google OAuth Controller', () => {
|
14 |
+
|
15 |
+
// Test for redirectToGoogleOAuth
|
16 |
+
it('should redirect to Google OAuth URL', async () => {
|
17 |
+
const req = {}; // Mock the request if needed
|
18 |
+
const res = {
|
19 |
+
redirect: jest.fn() // Mock the redirect method
|
20 |
+
};
|
21 |
+
|
22 |
+
// Simulate generating the auth URL
|
23 |
+
const expectedAuthUrl = 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&scope=https://www.googleapis.com/auth/gmail.send+https://www.googleapis.com/auth/calendar.readonly';
|
24 |
+
oAuth2Client.generateAuthUrl.mockReturnValueOnce(expectedAuthUrl);
|
25 |
+
|
26 |
+
// Call the function
|
27 |
+
redirectToGoogleOAuth(req, res);
|
28 |
+
|
29 |
+
// Verify the redirect URL
|
30 |
+
expect(res.redirect).toHaveBeenCalledWith(expectedAuthUrl);
|
31 |
+
});
|
32 |
+
|
33 |
+
// Test for handleOAuthCallback (success)
|
34 |
+
it('should handle OAuth callback successfully and send a success message', async () => {
|
35 |
+
const req = { query: { code: 'valid-code' } }; // Simulate the query parameter with a valid code
|
36 |
+
const res = {
|
37 |
+
send: jest.fn(), // Mock the send method
|
38 |
+
status: jest.fn().mockReturnThis(), // Mock status to chain calls
|
39 |
+
};
|
40 |
+
|
41 |
+
// Mock the oAuth2Client methods
|
42 |
+
const mockTokens = {
|
43 |
+
access_token: 'access-token',
|
44 |
+
refresh_token: 'refresh-token',
|
45 |
+
};
|
46 |
+
|
47 |
+
// Mock the getToken method to resolve with mock tokens
|
48 |
+
oAuth2Client.getToken.mockResolvedValueOnce({
|
49 |
+
tokens: mockTokens,
|
50 |
+
});
|
51 |
+
|
52 |
+
// Mock the setCredentials method to resolve
|
53 |
+
oAuth2Client.setCredentials.mockImplementationOnce(() => {});
|
54 |
+
|
55 |
+
// Call the function
|
56 |
+
await handleOAuthCallback(req, res);
|
57 |
+
|
58 |
+
// Check if the send method was called with the expected success message
|
59 |
+
expect(res.send).toHaveBeenCalledWith('Authorization successful! You can now send emails.');
|
60 |
+
});
|
61 |
+
|
62 |
+
// Test for handleOAuthCallback (error scenario)
|
63 |
+
it('should handle OAuth callback error and send failure message', async () => {
|
64 |
+
const req = { query: { code: 'invalid-code' } }; // Simulate an invalid code
|
65 |
+
const res = {
|
66 |
+
send: jest.fn(),
|
67 |
+
status: jest.fn().mockReturnThis(),
|
68 |
+
};
|
69 |
+
|
70 |
+
// Mock the oAuth2Client methods to throw an error
|
71 |
+
oAuth2Client.getToken.mockRejectedValueOnce(new Error('OAuth error'));
|
72 |
+
|
73 |
+
// Call the function
|
74 |
+
await handleOAuthCallback(req, res);
|
75 |
+
|
76 |
+
// Check if the status was set to 500 and send was called with the failure message
|
77 |
+
expect(res.status).toHaveBeenCalledWith(500);
|
78 |
+
expect(res.send).toHaveBeenCalledWith('Failed to authenticate with Google');
|
79 |
+
});
|
80 |
+
|
81 |
+
});
|
backend/__tests__/controller/contactController.test.js
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Importing dependencies once to avoid re-declaration errors
|
2 |
+
const { submitContactForm } = require('../../controller/contactController');
|
3 |
+
const { checkAndRefreshAccessToken } = require('../../utils/refreshToken');
|
4 |
+
const { checkExistingContactRequest, insertContactRequest } = require('../../utils/contactRequestDB');
|
5 |
+
const fetch = require('node-fetch');
|
6 |
+
const { google } = require('googleapis');
|
7 |
+
|
8 |
+
// Mock dependencies
|
9 |
+
jest.mock('../../utils/refreshToken', () => ({
|
10 |
+
checkAndRefreshAccessToken: jest.fn(),
|
11 |
+
}));
|
12 |
+
|
13 |
+
jest.mock('../../utils/contactRequestDB', () => ({
|
14 |
+
checkExistingContactRequest: jest.fn(),
|
15 |
+
insertContactRequest: jest.fn(),
|
16 |
+
}));
|
17 |
+
|
18 |
+
jest.mock('node-fetch', () => jest.fn().mockResolvedValue({
|
19 |
+
json: jest.fn().mockResolvedValue({ success: true })
|
20 |
+
}));
|
21 |
+
|
22 |
+
// Mock googleapis and oAuth2Client setup
|
23 |
+
jest.mock('googleapis', () => ({
|
24 |
+
google: {
|
25 |
+
auth: {
|
26 |
+
OAuth2: jest.fn().mockImplementation(() => ({
|
27 |
+
setCredentials: jest.fn(),
|
28 |
+
})),
|
29 |
+
},
|
30 |
+
gmail: jest.fn().mockReturnValue({
|
31 |
+
users: {
|
32 |
+
messages: {
|
33 |
+
send: jest.fn(),
|
34 |
+
},
|
35 |
+
},
|
36 |
+
}),
|
37 |
+
},
|
38 |
+
}));
|
39 |
+
|
40 |
+
describe('Contact Controller', () => {
|
41 |
+
|
42 |
+
afterEach(() => {
|
43 |
+
jest.clearAllMocks();
|
44 |
+
});
|
45 |
+
|
46 |
+
it('should handle contact form submission successfully', async () => {
|
47 |
+
const req = {
|
48 |
+
body: {
|
49 |
+
name: 'John Doe',
|
50 |
+
email: '[email protected]',
|
51 |
+
phone: '1234567890',
|
52 |
+
subject: 'Inquiry',
|
53 |
+
message: 'Hello, I have a question.',
|
54 |
+
},
|
55 |
+
};
|
56 |
+
|
57 |
+
const res = {
|
58 |
+
send: jest.fn(),
|
59 |
+
status: jest.fn().mockReturnThis(),
|
60 |
+
};
|
61 |
+
|
62 |
+
// Mock dependencies
|
63 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce();
|
64 |
+
checkExistingContactRequest.mockResolvedValueOnce(null); // Simulate no existing request
|
65 |
+
insertContactRequest.mockResolvedValueOnce();
|
66 |
+
|
67 |
+
// Mock the Gmail API response
|
68 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({
|
69 |
+
data: {
|
70 |
+
id: '123',
|
71 |
+
threadId: '456',
|
72 |
+
},
|
73 |
+
});
|
74 |
+
|
75 |
+
|
76 |
+
// Call the controller function
|
77 |
+
await submitContactForm(req, res);
|
78 |
+
|
79 |
+
// Check the successful response
|
80 |
+
expect(res.status).toHaveBeenCalledWith(200);
|
81 |
+
expect(res.send).toHaveBeenCalledWith({
|
82 |
+
message: 'Contact message sent successfully!',
|
83 |
+
response: {data: { id: '123', threadId: '456' }},
|
84 |
+
});
|
85 |
+
|
86 |
+
// Check if the required functions were called
|
87 |
+
expect(checkAndRefreshAccessToken).toHaveBeenCalledTimes(1);
|
88 |
+
expect(checkExistingContactRequest).toHaveBeenCalledWith('John Doe', '[email protected]', 'Inquiry');
|
89 |
+
expect(insertContactRequest).toHaveBeenCalledWith('John Doe', '[email protected]', '1234567890', 'Inquiry', 'Hello, I have a question.');
|
90 |
+
expect(google.gmail().users.messages.send).toHaveBeenCalledTimes(1);
|
91 |
+
expect(fetch).toHaveBeenCalledTimes(1);
|
92 |
+
});
|
93 |
+
|
94 |
+
it('should return error if contact request with same subject exists', async () => {
|
95 |
+
const req = {
|
96 |
+
body: {
|
97 |
+
name: 'John Doe',
|
98 |
+
email: '[email protected]',
|
99 |
+
phone: '1234567890',
|
100 |
+
subject: 'Inquiry',
|
101 |
+
message: 'Hello, I have a question.',
|
102 |
+
},
|
103 |
+
};
|
104 |
+
|
105 |
+
const res = {
|
106 |
+
send: jest.fn(),
|
107 |
+
status: jest.fn().mockReturnThis(),
|
108 |
+
};
|
109 |
+
|
110 |
+
// Mock dependencies
|
111 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce();
|
112 |
+
checkExistingContactRequest.mockResolvedValueOnce(true); // Simulate an existing request
|
113 |
+
insertContactRequest.mockResolvedValueOnce();
|
114 |
+
|
115 |
+
// Call the function
|
116 |
+
await submitContactForm(req, res);
|
117 |
+
|
118 |
+
// Check if it returned the error message for existing request
|
119 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
120 |
+
expect(res.send).toHaveBeenCalledWith({
|
121 |
+
error: 'Your contact request with same subject is in queue',
|
122 |
+
});
|
123 |
+
|
124 |
+
// Check if insertContactRequest wasn't called
|
125 |
+
expect(insertContactRequest).not.toHaveBeenCalled();
|
126 |
+
});
|
127 |
+
|
128 |
+
it('should return an error if required fields are missing', async () => {
|
129 |
+
const req = {
|
130 |
+
body: {
|
131 |
+
name: 'John Doe',
|
132 |
+
email: '[email protected]',
|
133 |
+
phone: '', // Missing phone
|
134 |
+
subject: 'Inquiry',
|
135 |
+
message: 'Hello, I have a question.',
|
136 |
+
},
|
137 |
+
};
|
138 |
+
|
139 |
+
const res = {
|
140 |
+
send: jest.fn(),
|
141 |
+
status: jest.fn().mockReturnThis(),
|
142 |
+
};
|
143 |
+
|
144 |
+
// Call the function
|
145 |
+
await submitContactForm(req, res);
|
146 |
+
|
147 |
+
// Check if the response was the correct error message
|
148 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
149 |
+
expect(res.send).toHaveBeenCalledWith({ error: 'All fields are required.' });
|
150 |
+
|
151 |
+
// Check if any other functions were called (they shouldn't have been)
|
152 |
+
expect(checkAndRefreshAccessToken).not.toHaveBeenCalled();
|
153 |
+
expect(checkExistingContactRequest).not.toHaveBeenCalled();
|
154 |
+
expect(insertContactRequest).not.toHaveBeenCalled();
|
155 |
+
expect(fetch).not.toHaveBeenCalled();
|
156 |
+
});
|
157 |
+
|
158 |
+
it('should handle error when sending email fails', async () => {
|
159 |
+
const req = {
|
160 |
+
body: {
|
161 |
+
name: 'John Doe',
|
162 |
+
email: '[email protected]',
|
163 |
+
phone: '1234567890',
|
164 |
+
subject: 'Inquiry',
|
165 |
+
message: 'Hello, I have a question.',
|
166 |
+
},
|
167 |
+
};
|
168 |
+
|
169 |
+
const res = {
|
170 |
+
send: jest.fn(),
|
171 |
+
status: jest.fn().mockReturnThis(),
|
172 |
+
};
|
173 |
+
|
174 |
+
// Mock dependencies
|
175 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce();
|
176 |
+
checkExistingContactRequest.mockResolvedValueOnce(null); // Simulate no existing request
|
177 |
+
insertContactRequest.mockResolvedValueOnce();
|
178 |
+
|
179 |
+
// Simulate Gmail API failure
|
180 |
+
google.gmail().users.messages.send.mockRejectedValueOnce(new Error('Email send failed'));
|
181 |
+
|
182 |
+
// Call the controller function
|
183 |
+
await submitContactForm(req, res);
|
184 |
+
|
185 |
+
// Check the error response
|
186 |
+
expect(res.status).toHaveBeenCalledWith(200);
|
187 |
+
expect(res.send).toHaveBeenCalledWith({
|
188 |
+
error: 'Failed to send Contact request confirmation email, however request registered successfully!',
|
189 |
+
});
|
190 |
+
|
191 |
+
// Check if other functions were called
|
192 |
+
expect(checkAndRefreshAccessToken).toHaveBeenCalledTimes(1);
|
193 |
+
expect(checkExistingContactRequest).toHaveBeenCalledWith('John Doe', '[email protected]', 'Inquiry');
|
194 |
+
expect(insertContactRequest).toHaveBeenCalledWith('John Doe', '[email protected]', '1234567890', 'Inquiry', 'Hello, I have a question.');
|
195 |
+
expect(google.gmail().users.messages.send).toHaveBeenCalledTimes(1);
|
196 |
+
});
|
197 |
+
|
198 |
+
});
|
backend/__tests__/controller/demo/dateAvailability.test.js
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { checkDateAvailability, getAvailableDates } = require('../../../controller/dateAvailability');
|
2 |
+
const { runSelectQuery } = require('../../../utils/queries');
|
3 |
+
const { isHoliday } = require('../../../controller/holidays');
|
4 |
+
const { DateTime } = require('luxon');
|
5 |
+
|
6 |
+
// Mock dependencies
|
7 |
+
jest.mock('../../../utils/queries', () => ({
|
8 |
+
runSelectQuery: jest.fn(),
|
9 |
+
}));
|
10 |
+
|
11 |
+
jest.mock('../../../controller/holidays', () => ({
|
12 |
+
isHoliday: jest.fn(),
|
13 |
+
}));
|
14 |
+
|
15 |
+
describe('Date Availability Module', () => {
|
16 |
+
afterEach(() => {
|
17 |
+
jest.clearAllMocks();
|
18 |
+
});
|
19 |
+
|
20 |
+
describe('checkDateAvailability', () => {
|
21 |
+
it('should return true if the date is available (less than 11 bookings)', async () => {
|
22 |
+
runSelectQuery.mockResolvedValue([{ count: 5 }]);
|
23 |
+
|
24 |
+
const result = await checkDateAvailability('2025-01-01');
|
25 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
26 |
+
'SELECT COUNT(*) AS count FROM demo_requests WHERE demo_date = ?;',
|
27 |
+
['2025-01-01']
|
28 |
+
);
|
29 |
+
expect(result).toBe(true);
|
30 |
+
});
|
31 |
+
|
32 |
+
it('should return false if the date is fully booked (11 or more bookings)', async () => {
|
33 |
+
runSelectQuery.mockResolvedValue([{ count: 11 }]);
|
34 |
+
|
35 |
+
const result = await checkDateAvailability('2025-01-01');
|
36 |
+
expect(result).toBe(false);
|
37 |
+
});
|
38 |
+
|
39 |
+
it('should handle empty results gracefully', async () => {
|
40 |
+
runSelectQuery.mockResolvedValue([]);
|
41 |
+
|
42 |
+
const result = await checkDateAvailability('2025-01-01');
|
43 |
+
expect(result).toBe(true); // No bookings mean the date is available
|
44 |
+
});
|
45 |
+
});
|
46 |
+
|
47 |
+
describe('getAvailableDates', () => {
|
48 |
+
it('should return 14 available dates excluding holidays and weekends', async () => {
|
49 |
+
const currentDate = DateTime.fromISO('2025-01-01', { zone: 'Asia/Kolkata' });
|
50 |
+
|
51 |
+
// Mock isHoliday to return false for all dates
|
52 |
+
isHoliday.mockImplementation(async () => false);
|
53 |
+
|
54 |
+
// Mock runSelectQuery to simulate all dates have <11 bookings
|
55 |
+
runSelectQuery.mockImplementation(async () => [{ count: 0 }]);
|
56 |
+
|
57 |
+
const result = await getAvailableDates(currentDate);
|
58 |
+
|
59 |
+
// Check that 14 dates are returned
|
60 |
+
expect(result.length).toBe(14);
|
61 |
+
});
|
62 |
+
|
63 |
+
it('should skip holidays, Sundays, and 1st/3rd/5th Saturdays', async () => {
|
64 |
+
const currentDate = DateTime.fromISO('2025-01-01', { zone: 'Asia/Kolkata' });
|
65 |
+
|
66 |
+
// Mock isHoliday to return true for specific dates
|
67 |
+
isHoliday.mockImplementation(async date =>
|
68 |
+
['2025-01-02', '2025-01-05'].includes(date)
|
69 |
+
);
|
70 |
+
|
71 |
+
// Mock runSelectQuery to simulate availability
|
72 |
+
runSelectQuery.mockImplementation(async () => [{ count: 0 }]);
|
73 |
+
|
74 |
+
const result = await getAvailableDates(currentDate);
|
75 |
+
|
76 |
+
// Ensure skipped dates are not in the result
|
77 |
+
expect(result).not.toContain('2025-01-02');
|
78 |
+
expect(result).not.toContain('2025-01-05');
|
79 |
+
});
|
80 |
+
});
|
81 |
+
});
|
backend/__tests__/controller/demo/holidays.test.js
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { getIndiaTamilHolidays, refreshHolidays, isHoliday } = require('../../../controller/holidays');
|
2 |
+
const { google } = require('googleapis');
|
3 |
+
const { oAuth2Client } = require('../../../config/googleOAuth');
|
4 |
+
const { checkAndRefreshAccessToken } = require('../../../utils/refreshToken');
|
5 |
+
|
6 |
+
// Mock the dependencies
|
7 |
+
jest.mock('googleapis', () => ({
|
8 |
+
google: {
|
9 |
+
calendar: jest.fn().mockReturnValue({
|
10 |
+
events: {
|
11 |
+
list: jest.fn(),
|
12 |
+
},
|
13 |
+
}),
|
14 |
+
},
|
15 |
+
}));
|
16 |
+
|
17 |
+
jest.mock('../../../utils/refreshToken', () => ({
|
18 |
+
checkAndRefreshAccessToken: jest.fn(),
|
19 |
+
}));
|
20 |
+
|
21 |
+
jest.mock('../../../config/googleOAuth', () => ({
|
22 |
+
oAuth2Client: {
|
23 |
+
setCredentials: jest.fn(),
|
24 |
+
},
|
25 |
+
}));
|
26 |
+
|
27 |
+
describe('Holidays Controller', () => {
|
28 |
+
let mockResponse;
|
29 |
+
let consoleLogSpy;
|
30 |
+
let setTimeoutSpy;
|
31 |
+
|
32 |
+
beforeEach(() => {
|
33 |
+
jest.clearAllMocks();
|
34 |
+
|
35 |
+
// Mock response to return a list of holiday events
|
36 |
+
mockResponse = {
|
37 |
+
data: {
|
38 |
+
items: [
|
39 |
+
{
|
40 |
+
summary: 'Holiday 1',
|
41 |
+
start: { date: '2025-01-01' },
|
42 |
+
end: { date: '2025-01-01' },
|
43 |
+
},
|
44 |
+
{
|
45 |
+
summary: 'Holiday 2',
|
46 |
+
start: { date: '2025-02-01' },
|
47 |
+
end: { date: '2025-02-02' },
|
48 |
+
},
|
49 |
+
],
|
50 |
+
},
|
51 |
+
};
|
52 |
+
|
53 |
+
checkAndRefreshAccessToken.mockResolvedValue({ access_token: 'valid-token' });
|
54 |
+
google.calendar().events.list.mockResolvedValue(mockResponse);
|
55 |
+
|
56 |
+
// Spy on console.log to suppress output during tests
|
57 |
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
58 |
+
|
59 |
+
// Spy on setTimeout to suppress interval during tests
|
60 |
+
setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation((fn, delay) => fn());
|
61 |
+
});
|
62 |
+
|
63 |
+
afterEach(() => {
|
64 |
+
consoleLogSpy.mockRestore();
|
65 |
+
setTimeoutSpy.mockRestore();
|
66 |
+
});
|
67 |
+
|
68 |
+
describe('getIndiaTamilHolidays', () => {
|
69 |
+
it('should return a list of holidays', async () => {
|
70 |
+
const holidays = await getIndiaTamilHolidays();
|
71 |
+
|
72 |
+
expect(holidays).toEqual([
|
73 |
+
{ festival: 'Holiday 1', date: '2025-01-01' },
|
74 |
+
{ festival: 'Holiday 2', date: '2025-02-01' },
|
75 |
+
{ festival: 'Holiday 2', date: '2025-02-02' },
|
76 |
+
]);
|
77 |
+
expect(google.calendar().events.list).toHaveBeenCalledTimes(1);
|
78 |
+
expect(checkAndRefreshAccessToken).toHaveBeenCalledTimes(1);
|
79 |
+
});
|
80 |
+
|
81 |
+
it('should return an error if OAuth credentials are missing', async () => {
|
82 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce(null);
|
83 |
+
|
84 |
+
const result = await getIndiaTamilHolidays();
|
85 |
+
expect(result).toEqual({ error: 'OAuth credentials missing. Please authorize the app first.' });
|
86 |
+
});
|
87 |
+
|
88 |
+
it('should handle errors properly', async () => {
|
89 |
+
google.calendar().events.list.mockRejectedValueOnce(new Error('API Error'));
|
90 |
+
|
91 |
+
await expect(getIndiaTamilHolidays()).rejects.toThrow('API Error');
|
92 |
+
});
|
93 |
+
});
|
94 |
+
|
95 |
+
describe('refreshHolidays', () => {
|
96 |
+
it('should refresh holidays and log them', async () => {
|
97 |
+
await refreshHolidays();
|
98 |
+
|
99 |
+
expect(consoleLogSpy).toHaveBeenCalledWith('India Holidays:', expect.any(Array));
|
100 |
+
expect(consoleLogSpy).toHaveBeenCalledWith('Total holidays:', 3);
|
101 |
+
expect(consoleLogSpy).toHaveBeenCalledWith('Last holiday:', expect.any(Object));
|
102 |
+
});
|
103 |
+
|
104 |
+
it('should handle retrying on error', async () => {
|
105 |
+
google.calendar().events.list.mockRejectedValueOnce(new Error('API Error'));
|
106 |
+
|
107 |
+
await refreshHolidays();
|
108 |
+
|
109 |
+
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
|
110 |
+
});
|
111 |
+
});
|
112 |
+
|
113 |
+
describe('isHoliday', () => {
|
114 |
+
it('should return true if the date is a holiday', async () => {
|
115 |
+
await refreshHolidays();
|
116 |
+
|
117 |
+
const result = await isHoliday('2025-01-01');
|
118 |
+
expect(result).toBe(true);
|
119 |
+
});
|
120 |
+
|
121 |
+
it('should return false if the date is not a holiday', async () => {
|
122 |
+
await refreshHolidays();
|
123 |
+
|
124 |
+
const result = await isHoliday('2025-01-03');
|
125 |
+
expect(result).toBe(false);
|
126 |
+
});
|
127 |
+
});
|
128 |
+
});
|
backend/__tests__/controller/demo/slots.test.js
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { getAvailableSlots, getBookedSlots } = require('../../../controller/slots');
|
2 |
+
const { runSelectQuery } = require('../../../utils/queries');
|
3 |
+
|
4 |
+
// Mock the runSelectQuery function
|
5 |
+
jest.mock('../../../utils/queries', () => ({
|
6 |
+
runSelectQuery: jest.fn(),
|
7 |
+
}));
|
8 |
+
|
9 |
+
describe('Slots Controller', () => {
|
10 |
+
beforeEach(() => {
|
11 |
+
jest.clearAllMocks();
|
12 |
+
});
|
13 |
+
|
14 |
+
describe('getBookedSlots', () => {
|
15 |
+
it('should throw an error if no date is provided', async () => {
|
16 |
+
await expect(getBookedSlots()).rejects.toThrow('Date is required');
|
17 |
+
});
|
18 |
+
|
19 |
+
|
20 |
+
it('should return an empty array if no slots are found for a valid date', async () => {
|
21 |
+
const date = '2025-01-01';
|
22 |
+
runSelectQuery.mockResolvedValueOnce([]); // Simulate no booked slots
|
23 |
+
|
24 |
+
const slots = await getBookedSlots(date);
|
25 |
+
|
26 |
+
expect(slots).toEqual([]); // No booked slots
|
27 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
28 |
+
expect.any(String),
|
29 |
+
date
|
30 |
+
);
|
31 |
+
});
|
32 |
+
|
33 |
+
it('should return a list of booked slots for a given date', async () => {
|
34 |
+
const date = '2025-01-01';
|
35 |
+
const mockBookedSlots = [
|
36 |
+
{ slot: '10:00 - 10:30' },
|
37 |
+
{ slot: '11:30 - 12:00' },
|
38 |
+
];
|
39 |
+
runSelectQuery.mockResolvedValueOnce(mockBookedSlots);
|
40 |
+
|
41 |
+
const slots = await getBookedSlots(date);
|
42 |
+
|
43 |
+
expect(slots).toEqual(['10:00 - 10:30', '11:30 - 12:00']);
|
44 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
45 |
+
expect.any(String),
|
46 |
+
date
|
47 |
+
);
|
48 |
+
});
|
49 |
+
|
50 |
+
it('should handle missing slot property gracefully', async () => {
|
51 |
+
const date = '2025-01-01';
|
52 |
+
const mockBookedSlots = [
|
53 |
+
{ slot: '10:00 - 10:30' },
|
54 |
+
{ /* missing slot property */ },
|
55 |
+
];
|
56 |
+
runSelectQuery.mockResolvedValueOnce(mockBookedSlots);
|
57 |
+
|
58 |
+
const slots = await getBookedSlots(date);
|
59 |
+
|
60 |
+
expect(slots).toEqual(['10:00 - 10:30']); // Ignore the entry without a slot
|
61 |
+
});
|
62 |
+
});
|
63 |
+
|
64 |
+
describe('getAvailableSlots', () => {
|
65 |
+
it('should throw an error if no date is provided', async () => {
|
66 |
+
await expect(getAvailableSlots()).rejects.toThrow('Date is required');
|
67 |
+
});
|
68 |
+
|
69 |
+
it('should throw an error for invalid date format', async () => {
|
70 |
+
const invalidDate = '2025-01-32';
|
71 |
+
await expect(getAvailableSlots(invalidDate)).rejects.toThrow('Invalid date format. Expected YYYY-MM-DD');
|
72 |
+
});
|
73 |
+
|
74 |
+
it('should return all general slots if no slots are booked for a given date', async () => {
|
75 |
+
const date = '2025-01-01';
|
76 |
+
runSelectQuery.mockResolvedValueOnce([]); // Simulate no booked slots
|
77 |
+
|
78 |
+
const availableSlots = await getAvailableSlots(date);
|
79 |
+
|
80 |
+
expect(availableSlots).toEqual([
|
81 |
+
'10:00 - 10:30', '10:45 - 11:15', '11:30 - 12:00', '12:15 - 12:45', '13:00 - 13:30',
|
82 |
+
'13:45 - 14:15', '14:30 - 15:00', '15:15 - 15:45', '16:00 - 16:30', '16:45 - 17:15', '17:30 - 18:00'
|
83 |
+
]);
|
84 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
85 |
+
expect.any(String),
|
86 |
+
date
|
87 |
+
);
|
88 |
+
});
|
89 |
+
|
90 |
+
it('should return available slots after removing booked slots', async () => {
|
91 |
+
const date = '2025-01-01';
|
92 |
+
const mockBookedSlots = [
|
93 |
+
{ slot: '10:00 - 10:30' },
|
94 |
+
{ slot: '11:30 - 12:00' },
|
95 |
+
];
|
96 |
+
runSelectQuery.mockResolvedValueOnce(mockBookedSlots);
|
97 |
+
|
98 |
+
const availableSlots = await getAvailableSlots(date);
|
99 |
+
|
100 |
+
expect(availableSlots).toEqual([
|
101 |
+
'10:45 - 11:15', '12:15 - 12:45', '13:00 - 13:30', '13:45 - 14:15',
|
102 |
+
'14:30 - 15:00', '15:15 - 15:45', '16:00 - 16:30', '16:45 - 17:15', '17:30 - 18:00'
|
103 |
+
]);
|
104 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
105 |
+
expect.any(String),
|
106 |
+
date
|
107 |
+
);
|
108 |
+
});
|
109 |
+
|
110 |
+
it('should handle empty results gracefully', async () => {
|
111 |
+
const date = '2025-01-01';
|
112 |
+
const mockBookedSlots = []; // No booked slots
|
113 |
+
runSelectQuery.mockResolvedValueOnce(mockBookedSlots);
|
114 |
+
|
115 |
+
const availableSlots = await getAvailableSlots(date);
|
116 |
+
|
117 |
+
expect(availableSlots).toEqual([
|
118 |
+
'10:00 - 10:30', '10:45 - 11:15', '11:30 - 12:00', '12:15 - 12:45', '13:00 - 13:30',
|
119 |
+
'13:45 - 14:15', '14:30 - 15:00', '15:15 - 15:45', '16:00 - 16:30', '16:45 - 17:15', '17:30 - 18:00'
|
120 |
+
]);
|
121 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
122 |
+
expect.any(String),
|
123 |
+
date
|
124 |
+
);
|
125 |
+
});
|
126 |
+
|
127 |
+
it('should throw an error if general slots are not properly defined', async () => {
|
128 |
+
const date = '2025-01-01';
|
129 |
+
const mockBookedSlots = [{ slot: '10:00 - 10:30' }];
|
130 |
+
runSelectQuery.mockResolvedValueOnce(mockBookedSlots);
|
131 |
+
|
132 |
+
// Temporarily break the generalSlots array
|
133 |
+
const originalGeneralSlots = [...global.generalSlots];
|
134 |
+
global.generalSlots = undefined;
|
135 |
+
|
136 |
+
await expect(getAvailableSlots(date)).rejects.toThrow('General slots are not properly defined');
|
137 |
+
|
138 |
+
// Restore the general slots for other tests
|
139 |
+
global.generalSlots = originalGeneralSlots;
|
140 |
+
});
|
141 |
+
});
|
142 |
+
});
|
backend/__tests__/controller/demo/utils.test.js
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { createDateTimeSlotMapping, slotsInUserZone, getDatesSlots } = require('../../../controller/utils');
|
2 |
+
const { DateTime } = require('luxon');
|
3 |
+
|
4 |
+
describe('Utils Controller', () => {
|
5 |
+
|
6 |
+
describe('createDateTimeSlotMapping', () => {
|
7 |
+
it('should map date-time to slots correctly', () => {
|
8 |
+
const data = [
|
9 |
+
{ '2025-01-01': ['10:00 - 10:30', '11:00 - 11:30'] },
|
10 |
+
{ '2025-01-02': ['12:00 - 12:30'] }
|
11 |
+
];
|
12 |
+
const expected = {
|
13 |
+
'2025-01-01T10:00': '10:00 - 10:30',
|
14 |
+
'2025-01-01T11:00': '11:00 - 11:30',
|
15 |
+
'2025-01-02T12:00': '12:00 - 12:30'
|
16 |
+
};
|
17 |
+
const result = createDateTimeSlotMapping(data);
|
18 |
+
expect(result).toEqual(expected);
|
19 |
+
});
|
20 |
+
|
21 |
+
it('should return an empty object if no data is provided', () => {
|
22 |
+
const result = createDateTimeSlotMapping([]);
|
23 |
+
expect(result).toEqual({});
|
24 |
+
});
|
25 |
+
|
26 |
+
it('should handle invalid date format gracefully', () => {
|
27 |
+
const data = [
|
28 |
+
{ '2025-01-01': ['10:00 - 10:30'] },
|
29 |
+
{ '2025-31-31': ['11:00 - 11:30'] } // Invalid date format
|
30 |
+
];
|
31 |
+
const result = createDateTimeSlotMapping(data);
|
32 |
+
expect(result['2025-31-31T11:00']).toBeUndefined(); // Should not map an invalid date
|
33 |
+
});
|
34 |
+
});
|
35 |
+
|
36 |
+
describe('slotsInUserZone', () => {
|
37 |
+
it('should convert slot times correctly for user timezone', async () => {
|
38 |
+
const inter = {
|
39 |
+
'2025-01-01T10:00': '10:00 - 10:30',
|
40 |
+
'2025-01-01T11:00': '11:00 - 11:30'
|
41 |
+
};
|
42 |
+
const timezone = 'America/New_York';
|
43 |
+
const result = await slotsInUserZone(inter, timezone);
|
44 |
+
console.log(result);
|
45 |
+
expect(result['2024-12-31T23:30:00.000-05:00']).toBe('23:30 - 00:00 (America/New_York)');
|
46 |
+
});
|
47 |
+
});
|
48 |
+
|
49 |
+
describe('getDatesSlots', () => {
|
50 |
+
it('should organize slots by date correctly', async () => {
|
51 |
+
const input = {
|
52 |
+
'2025-01-01T10:00': '10:00 - 10:30',
|
53 |
+
'2025-01-01T11:00': '11:00 - 11:30',
|
54 |
+
'2025-01-02T12:00': '12:00 - 12:30'
|
55 |
+
};
|
56 |
+
const result = await getDatesSlots(input);
|
57 |
+
expect(result).toEqual({
|
58 |
+
'2025-01-01': ['10:00 - 10:30', '11:00 - 11:30'],
|
59 |
+
'2025-01-02': ['12:00 - 12:30']
|
60 |
+
});
|
61 |
+
});
|
62 |
+
|
63 |
+
it('should handle empty input', async () => {
|
64 |
+
const input = {};
|
65 |
+
const result = await getDatesSlots(input);
|
66 |
+
expect(result).toEqual({});
|
67 |
+
});
|
68 |
+
|
69 |
+
it('should handle invalid timestamp formats', async () => {
|
70 |
+
const input = {
|
71 |
+
'invalid-date': '10:00 - 10:30'
|
72 |
+
};
|
73 |
+
const result = await getDatesSlots(input);
|
74 |
+
expect(result).toEqual({});
|
75 |
+
});
|
76 |
+
});
|
77 |
+
});
|
backend/__tests__/controller/demoRequestController.test.js
ADDED
@@ -0,0 +1,293 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { demoRequest } = require('../../controller/demoRequestController');
|
2 |
+
const { checkExistingDemoRequest, insertDemoRequest } = require('../../utils/demoRequestDB');
|
3 |
+
const { checkAndRefreshAccessToken } = require('../../utils/refreshToken');
|
4 |
+
const { oAuth2Client } = require('../../config/googleOAuth');
|
5 |
+
const { google } = require('googleapis');
|
6 |
+
const fetch = require('node-fetch');
|
7 |
+
const { DateTime } = require('luxon');
|
8 |
+
|
9 |
+
jest.mock('../../utils/demoRequestDB', () => ({
|
10 |
+
checkExistingDemoRequest: jest.fn(),
|
11 |
+
insertDemoRequest: jest.fn(),
|
12 |
+
}));
|
13 |
+
|
14 |
+
jest.mock('../../utils/refreshToken', () => ({
|
15 |
+
checkAndRefreshAccessToken: jest.fn(),
|
16 |
+
}));
|
17 |
+
|
18 |
+
jest.mock('../../config/googleOAuth', () => ({
|
19 |
+
oAuth2Client: {
|
20 |
+
setCredentials: jest.fn(),
|
21 |
+
},
|
22 |
+
}));
|
23 |
+
|
24 |
+
jest.mock('node-fetch', () => jest.fn());
|
25 |
+
|
26 |
+
jest.mock('googleapis', () => ({
|
27 |
+
google: {
|
28 |
+
gmail: jest.fn().mockReturnValue({
|
29 |
+
users: {
|
30 |
+
messages: {
|
31 |
+
send: jest.fn(),
|
32 |
+
},
|
33 |
+
},
|
34 |
+
}),
|
35 |
+
},
|
36 |
+
}));
|
37 |
+
|
38 |
+
describe('demoRequest Controller', () => {
|
39 |
+
let req, res;
|
40 |
+
|
41 |
+
beforeEach(() => {
|
42 |
+
jest.clearAllMocks();
|
43 |
+
|
44 |
+
req = {
|
45 |
+
body: {
|
46 |
+
name: 'John Doe',
|
47 |
+
email: '[email protected]',
|
48 |
+
company: 'Example Corp',
|
49 |
+
product: 'Test Product',
|
50 |
+
demoDate: '2025-02-01',
|
51 |
+
selectedSlot: '10:00 - 11:00 (IST)',
|
52 |
+
phone: '+1234567890',
|
53 |
+
additionalComments: 'Looking forward to it!',
|
54 |
+
timezone: 'America/New_York',
|
55 |
+
},
|
56 |
+
};
|
57 |
+
|
58 |
+
res = {
|
59 |
+
status: jest.fn().mockReturnThis(),
|
60 |
+
send: jest.fn(),
|
61 |
+
};
|
62 |
+
});
|
63 |
+
|
64 |
+
it('should return 400 if required fields are missing', async () => {
|
65 |
+
req.body = {}; // Missing fields
|
66 |
+
|
67 |
+
await demoRequest(req, res);
|
68 |
+
|
69 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
70 |
+
expect(res.send).toHaveBeenCalledWith({ error: 'Please fill in all required fields.' });
|
71 |
+
});
|
72 |
+
|
73 |
+
it('should return 400 if demo request already exists within 15 days', async () => {
|
74 |
+
checkExistingDemoRequest.mockResolvedValueOnce(true);
|
75 |
+
|
76 |
+
await demoRequest(req, res);
|
77 |
+
|
78 |
+
expect(checkExistingDemoRequest).toHaveBeenCalledWith(
|
79 |
+
'John Doe',
|
80 |
+
'[email protected]',
|
81 |
+
'Test Product',
|
82 |
+
'2025-02-01',
|
83 |
+
'+1234567890'
|
84 |
+
);
|
85 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
86 |
+
expect(res.send).toHaveBeenCalledWith({
|
87 |
+
error: 'You have already requested a demo for this product within 15 days. Our team will get back to you shortly.',
|
88 |
+
});
|
89 |
+
});
|
90 |
+
|
91 |
+
it('should insert a new demo request if no existing request is found', async () => {
|
92 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
93 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
94 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({}); // Mock email sending
|
95 |
+
|
96 |
+
await demoRequest(req, res);
|
97 |
+
|
98 |
+
const convertedDemoDate = DateTime.fromISO(req.body.demoDate, { zone: 'utc' })
|
99 |
+
.setZone('Asia/Kolkata')
|
100 |
+
.toFormat('yyyy-MM-dd');
|
101 |
+
|
102 |
+
expect(insertDemoRequest).toHaveBeenCalledWith(
|
103 |
+
'John Doe',
|
104 |
+
'[email protected]',
|
105 |
+
'Example Corp',
|
106 |
+
'Test Product',
|
107 |
+
convertedDemoDate,
|
108 |
+
'10:00 - 11:00',
|
109 |
+
'+1234567890',
|
110 |
+
'Looking forward to it!'
|
111 |
+
);
|
112 |
+
expect(res.status).toHaveBeenCalledWith(200);
|
113 |
+
expect(res.send).toHaveBeenCalledWith({
|
114 |
+
message: 'Demo request submitted successfully! Our team will get in touch with you soon.',
|
115 |
+
response: {},
|
116 |
+
});
|
117 |
+
});
|
118 |
+
|
119 |
+
it('should send an email via Gmail API', async () => {
|
120 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
121 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
122 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({}); // Mock email sending
|
123 |
+
|
124 |
+
await demoRequest(req, res);
|
125 |
+
|
126 |
+
expect(google.gmail().users.messages.send).toHaveBeenCalledWith({
|
127 |
+
userId: 'me',
|
128 |
+
requestBody: {
|
129 |
+
raw: expect.any(String),
|
130 |
+
},
|
131 |
+
});
|
132 |
+
});
|
133 |
+
|
134 |
+
it('should handle email sending failure gracefully', async () => {
|
135 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
136 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
137 |
+
google.gmail().users.messages.send.mockRejectedValueOnce(new Error('Email API Error'));
|
138 |
+
|
139 |
+
await demoRequest(req, res);
|
140 |
+
|
141 |
+
expect(res.status).toHaveBeenCalledWith(200);
|
142 |
+
expect(res.send).toHaveBeenCalledWith({
|
143 |
+
error: 'Failed to send demo request confirmation email, however the demo request has been registered successfully!',
|
144 |
+
});
|
145 |
+
});
|
146 |
+
|
147 |
+
it('should call VAPI API with correct data', async () => {
|
148 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
149 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
150 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({}); // Mock email sending
|
151 |
+
|
152 |
+
// Mock fetch response for the VAPI API call
|
153 |
+
fetch.mockResolvedValueOnce({
|
154 |
+
json: jest.fn().mockResolvedValueOnce({ success: true }),
|
155 |
+
});
|
156 |
+
|
157 |
+
await demoRequest(req, res);
|
158 |
+
|
159 |
+
expect(fetch).toHaveBeenCalledWith('https://api.vapi.ai/call', {
|
160 |
+
method: 'POST',
|
161 |
+
headers: {
|
162 |
+
'Content-Type': 'application/json',
|
163 |
+
Authorization: `Bearer ${process.env.VAPI_KEY}`,
|
164 |
+
},
|
165 |
+
body: JSON.stringify({
|
166 |
+
name: '+1234567890_2025-01-28_DRC',
|
167 |
+
assistantId: `${process.env.DEMO_ASSISTANT_ID}`,
|
168 |
+
assistantOverrides: {
|
169 |
+
variableValues: {
|
170 |
+
name: 'John Doe',
|
171 |
+
product_name: 'Test Product',
|
172 |
+
demo_date: '2025-02-01',
|
173 |
+
slot: '10:00 - 11:00 (IST)',
|
174 |
+
comments: 'Looking forward to it!',
|
175 |
+
},
|
176 |
+
},
|
177 |
+
customer: {
|
178 |
+
number: '+1234567890',
|
179 |
+
},
|
180 |
+
phoneNumberId: `${process.env.PHONE_NUMBER_ID}`,
|
181 |
+
}),
|
182 |
+
});
|
183 |
+
});
|
184 |
+
|
185 |
+
it('should correctly convert the selected time slot to Asia/Kolkata timezone', async () => {
|
186 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
187 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
188 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({}); // Mock email sending
|
189 |
+
|
190 |
+
await demoRequest(req, res);
|
191 |
+
|
192 |
+
// Ensure the demo date and time are converted to Asia/Kolkata timezone
|
193 |
+
const convertedTime = '10:00 - 11:00'; // Expected converted time
|
194 |
+
expect(insertDemoRequest).toHaveBeenCalledWith(
|
195 |
+
'John Doe',
|
196 |
+
'[email protected]',
|
197 |
+
'Example Corp',
|
198 |
+
'Test Product',
|
199 |
+
expect.any(String), // Date will be formatted and converted
|
200 |
+
convertedTime,
|
201 |
+
'+1234567890',
|
202 |
+
'Looking forward to it!'
|
203 |
+
);
|
204 |
+
});
|
205 |
+
|
206 |
+
it('should return 400 if product field is missing', async () => {
|
207 |
+
req.body.product = ''; // Missing product
|
208 |
+
|
209 |
+
await demoRequest(req, res);
|
210 |
+
|
211 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
212 |
+
expect(res.send).toHaveBeenCalledWith({ error: 'Please fill in all required fields.' });
|
213 |
+
});
|
214 |
+
|
215 |
+
it('should generate correctly formatted HTML email content', async () => {
|
216 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
217 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
218 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({}); // Mock email sending
|
219 |
+
|
220 |
+
await demoRequest(req, res);
|
221 |
+
|
222 |
+
const expectedHtmlMessage = `
|
223 |
+
<div style="font-family: Arial, sans-serif; color: #333;">
|
224 |
+
<img src="https://www.genomatics.in/wp-content/uploads/2020/01/cropped-LOGO-1-1.png" alt="Genomatics Logo">
|
225 |
+
<h2 style="color: #0056b3;">Hello John Doe,</h2>
|
226 |
+
<p>Thank you for requesting a demo of our <strong>Genomatics</strong> product: <strong>Test Product</strong>. We're excited to showcase how our solutions can benefit your team at <strong>Example Corp</strong>.</p>
|
227 |
+
<p><strong>Requested demo date:</strong> 2025-02-01 - Time zone: America/New_York</p>
|
228 |
+
<p><strong>Preferred time slot:</strong> 10:00 - 11:00 (IST)</p>
|
229 |
+
<p>We'll be in touch soon to confirm the details. In the meantime, please feel free to reach out with any specific questions or topics you’d like us to cover during the demo.</p>
|
230 |
+
<h3 style="color: #0056b3;">Additional Message from You:</h3>
|
231 |
+
<p>Looking forward to it!</p>
|
232 |
+
<p style="margin-top: 20px;">Best regards,</p>
|
233 |
+
<p><strong>The Genomatics Team</strong></p>
|
234 |
+
<hr style="border: 0; height: 1px; background: #ddd; margin: 20px 0;">
|
235 |
+
<p style="font-size: 12px; color: #666;">
|
236 |
+
This email was sent in response to your request for a demo on the Genomatics platform.
|
237 |
+
</p>
|
238 |
+
</div>`;
|
239 |
+
|
240 |
+
expect(google.gmail().users.messages.send).toHaveBeenCalledWith({
|
241 |
+
userId: 'me',
|
242 |
+
requestBody: {
|
243 |
+
raw: expect.any(String),
|
244 |
+
},
|
245 |
+
});
|
246 |
+
|
247 |
+
// Verify that email content matches the expected content
|
248 |
+
const emailContent = google.gmail().users.messages.send.mock.calls[0][0].requestBody.raw;
|
249 |
+
expect(emailContent).toContain(expectedHtmlMessage);
|
250 |
+
});
|
251 |
+
|
252 |
+
it('should handle fetch API error during VAPI call', async () => {
|
253 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
254 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
255 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({}); // Mock email sending
|
256 |
+
|
257 |
+
// Mock fetch to throw an error during VAPI API call
|
258 |
+
fetch.mockRejectedValueOnce(new Error('VAPI call failed'));
|
259 |
+
|
260 |
+
await demoRequest(req, res);
|
261 |
+
|
262 |
+
expect(res.status).toHaveBeenCalledWith(200);
|
263 |
+
expect(res.send).toHaveBeenCalledWith({
|
264 |
+
error: 'Failed to send demo request confirmation email, however the demo request has been registered successfully!',
|
265 |
+
});
|
266 |
+
});
|
267 |
+
|
268 |
+
it('should correctly handle daylight saving time transitions for timezones', async () => {
|
269 |
+
req.body.timezone = 'America/New_York'; // A timezone with daylight saving time transition
|
270 |
+
|
271 |
+
checkExistingDemoRequest.mockResolvedValueOnce(false);
|
272 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce({});
|
273 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({}); // Mock email sending
|
274 |
+
|
275 |
+
await demoRequest(req, res);
|
276 |
+
|
277 |
+
// Test if the correct timezone is applied
|
278 |
+
const convertedDemoDate = DateTime.fromISO(req.body.demoDate, { zone: 'utc' })
|
279 |
+
.setZone('America/New_York')
|
280 |
+
.toFormat('yyyy-MM-dd');
|
281 |
+
|
282 |
+
expect(insertDemoRequest).toHaveBeenCalledWith(
|
283 |
+
'John Doe',
|
284 |
+
'[email protected]',
|
285 |
+
'Example Corp',
|
286 |
+
'Test Product',
|
287 |
+
convertedDemoDate,
|
288 |
+
'10:00 - 11:00',
|
289 |
+
'+1234567890',
|
290 |
+
'Looking forward to it!'
|
291 |
+
);
|
292 |
+
});
|
293 |
+
});
|
backend/__tests__/controller/subscriptionController.test.js
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { handleSubscriptionEmail } = require('../../controller/subscriptionController');
|
2 |
+
const { addSubscriber } = require('../../utils/addSubscriber');
|
3 |
+
const { checkAndRefreshAccessToken } = require('../../utils/refreshToken');
|
4 |
+
const { google } = require('googleapis');
|
5 |
+
const { oAuth2Client } = require('../../config/googleOAuth');
|
6 |
+
|
7 |
+
// Mock the dependencies
|
8 |
+
jest.mock('../../utils/addSubscriber', () => ({
|
9 |
+
addSubscriber: jest.fn(),
|
10 |
+
}));
|
11 |
+
|
12 |
+
jest.mock('../../utils/refreshToken', () => ({
|
13 |
+
checkAndRefreshAccessToken: jest.fn(),
|
14 |
+
}));
|
15 |
+
|
16 |
+
// Mock googleapis and oAuth2Client setup
|
17 |
+
jest.mock('googleapis', () => ({
|
18 |
+
google: {
|
19 |
+
auth: {
|
20 |
+
OAuth2: jest.fn().mockImplementation(() => ({
|
21 |
+
setCredentials: jest.fn(),
|
22 |
+
})),
|
23 |
+
},
|
24 |
+
gmail: jest.fn().mockReturnValue({
|
25 |
+
users: {
|
26 |
+
messages: {
|
27 |
+
send: jest.fn(),
|
28 |
+
},
|
29 |
+
},
|
30 |
+
}),
|
31 |
+
},
|
32 |
+
}));
|
33 |
+
|
34 |
+
describe('handleSubscriptionEmail', () => {
|
35 |
+
let req;
|
36 |
+
let res;
|
37 |
+
let userTokens;
|
38 |
+
|
39 |
+
beforeEach(() => {
|
40 |
+
// Mock request and response objects
|
41 |
+
req = {
|
42 |
+
body: {
|
43 |
+
email: '[email protected]',
|
44 |
+
},
|
45 |
+
};
|
46 |
+
res = {
|
47 |
+
status: jest.fn().mockReturnThis(), // Mocking status method to return 'res' object itself
|
48 |
+
send: jest.fn().mockReturnThis(), // Mocking send method
|
49 |
+
json: jest.fn().mockReturnThis(), // Mocking json method
|
50 |
+
};
|
51 |
+
|
52 |
+
oAuth2Client.credentials = { access_token: 'fake-access-token', refresh_token: `fake-refresh-token` }; // Mocked OAuth tokens
|
53 |
+
|
54 |
+
// Clear previous mocks
|
55 |
+
jest.clearAllMocks();
|
56 |
+
});
|
57 |
+
|
58 |
+
it('should return an error if email is missing', async () => {
|
59 |
+
req.body.email = ''; // Missing email
|
60 |
+
|
61 |
+
await handleSubscriptionEmail(req, res);
|
62 |
+
|
63 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
64 |
+
expect(res.send).toHaveBeenCalledWith({ error: 'Email is required' });
|
65 |
+
});
|
66 |
+
|
67 |
+
it('should return an error if OAuth credentials are missing', async () => {
|
68 |
+
req.body.email = '[email protected]'; // Valid email
|
69 |
+
oAuth2Client.credentials = null; // Mocked OAuth tokens
|
70 |
+
|
71 |
+
addSubscriber.mockResolvedValueOnce({ status: 200, message: 'Subscriber added' });
|
72 |
+
|
73 |
+
await handleSubscriptionEmail(req, res);
|
74 |
+
|
75 |
+
expect(res.status).toHaveBeenCalledWith(500);
|
76 |
+
expect(res.send).toHaveBeenCalledWith({ error: 'OAuth credentials missing. Please authorize the app first.' });
|
77 |
+
});
|
78 |
+
|
79 |
+
it('should successfully subscribe a user and send a subscription email', async () => {
|
80 |
+
addSubscriber.mockResolvedValueOnce({ status: 200, message: 'Subscriber added' }); // Mock success
|
81 |
+
checkAndRefreshAccessToken.mockResolvedValueOnce(); // Mock successful token refresh
|
82 |
+
|
83 |
+
oAuth2Client.credentials = { access_token: 'fake-access-token', refresh_token: `fake-refresh-token` }; // Mocked OAuth tokens
|
84 |
+
|
85 |
+
google.gmail().users.messages.send.mockResolvedValueOnce({
|
86 |
+
data: { id: '123', threadId: '456' }, // Mock successful email sending
|
87 |
+
});
|
88 |
+
|
89 |
+
await handleSubscriptionEmail(req, res);
|
90 |
+
|
91 |
+
expect(res.status).toHaveBeenCalledWith(200);
|
92 |
+
expect(res.send).toHaveBeenCalledWith({
|
93 |
+
message: 'Subscription email sent!',
|
94 |
+
response: { data: { id: '123', threadId: '456' } },
|
95 |
+
});
|
96 |
+
|
97 |
+
expect(addSubscriber).toHaveBeenCalledWith('[email protected]');
|
98 |
+
expect(checkAndRefreshAccessToken).toHaveBeenCalledTimes(1);
|
99 |
+
expect(google.gmail().users.messages.send).toHaveBeenCalledTimes(1);
|
100 |
+
});
|
101 |
+
|
102 |
+
it('should return an error if email is already in the database', async () => {
|
103 |
+
const result = {
|
104 |
+
error: 'Email already exists',
|
105 |
+
status: 400,
|
106 |
+
};
|
107 |
+
addSubscriber.mockResolvedValueOnce(result); // Simulate email already existing
|
108 |
+
|
109 |
+
await handleSubscriptionEmail(req, res);
|
110 |
+
|
111 |
+
expect(res.status).toHaveBeenCalledWith(400);
|
112 |
+
expect(res.send).toHaveBeenCalledWith({ error: 'Email already exists' });
|
113 |
+
|
114 |
+
expect(addSubscriber).toHaveBeenCalledWith('[email protected]');
|
115 |
+
});
|
116 |
+
|
117 |
+
it('should handle error when sending email fails', async () => {
|
118 |
+
addSubscriber.mockResolvedValueOnce({ status: 200, message: 'Subscriber added' }); // Mock success
|
119 |
+
google.gmail().users.messages.send.mockRejectedValueOnce(new Error('Failed to send email')); // Mock email send failure
|
120 |
+
|
121 |
+
await handleSubscriptionEmail(req, res);
|
122 |
+
|
123 |
+
expect(res.status).toHaveBeenCalledWith(500);
|
124 |
+
expect(res.send).toHaveBeenCalledWith({
|
125 |
+
error: 'Failed to send subscription confirmation email, however you are subscribed and will receive newsletters',
|
126 |
+
});
|
127 |
+
|
128 |
+
expect(addSubscriber).toHaveBeenCalledWith('[email protected]');
|
129 |
+
expect(google.gmail().users.messages.send).toHaveBeenCalledTimes(1);
|
130 |
+
});
|
131 |
+
});
|
backend/__tests__/db.test.js
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// initializeDatabase.test.js
|
2 |
+
const { initializeDatabase } = require('../utils/setupDB'); // Import the function
|
3 |
+
const { runQuery } = require('../utils/queries'); // Import the runQuery function
|
4 |
+
|
5 |
+
jest.mock('../utils/queries'); // Mock the runQuery function
|
6 |
+
|
7 |
+
describe('initializeDatabase', () => {
|
8 |
+
beforeEach(() => {
|
9 |
+
// Clear all previous mocks before each test
|
10 |
+
jest.clearAllMocks();
|
11 |
+
});
|
12 |
+
|
13 |
+
it('should call runQuery for each table creation query', async () => {
|
14 |
+
// Arrange: Simulate the successful execution of runQuery
|
15 |
+
runQuery.mockResolvedValueOnce({}); // Mocking a successful query execution
|
16 |
+
|
17 |
+
// Act: Call the initializeDatabase function
|
18 |
+
await initializeDatabase();
|
19 |
+
|
20 |
+
// Assert: Check that runQuery was called once for each table creation query
|
21 |
+
const tableCreationQueries = [
|
22 |
+
expect.stringContaining('CREATE TABLE IF NOT EXISTS subscribers'),
|
23 |
+
expect.stringContaining('CREATE TABLE IF NOT EXISTS applicants'),
|
24 |
+
expect.stringContaining('CREATE TABLE IF NOT EXISTS demo_requests'),
|
25 |
+
expect.stringContaining('CREATE TABLE IF NOT EXISTS contact_requests'),
|
26 |
+
expect.stringContaining('CREATE TABLE IF NOT EXISTS purchases'),
|
27 |
+
expect.stringContaining('CREATE TABLE IF NOT EXISTS jobs')
|
28 |
+
];
|
29 |
+
|
30 |
+
tableCreationQueries.forEach((query, index) => {
|
31 |
+
expect(runQuery).toHaveBeenNthCalledWith(index + 1, query);
|
32 |
+
});
|
33 |
+
});
|
34 |
+
|
35 |
+
it('should handle errors properly if runQuery fails', async () => {
|
36 |
+
// Arrange: Simulate a failure when runQuery is called
|
37 |
+
runQuery.mockRejectedValueOnce(new Error('Database error'));
|
38 |
+
|
39 |
+
// Act & Assert: Call the function and expect it to log the error
|
40 |
+
await expect(initializeDatabase()).resolves.not.toThrow(); // Since error is logged but not re-thrown
|
41 |
+
expect(runQuery).toHaveBeenCalled();
|
42 |
+
});
|
43 |
+
|
44 |
+
it('should log successful table creation', async () => {
|
45 |
+
// Arrange: Mock successful query responses
|
46 |
+
runQuery.mockResolvedValueOnce({}); // Mocking a successful query execution
|
47 |
+
|
48 |
+
// Act: Call the function and capture the console log output
|
49 |
+
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); // Spy on console.log
|
50 |
+
|
51 |
+
await initializeDatabase();
|
52 |
+
|
53 |
+
// Assert: Check that each table creation success message is logged
|
54 |
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Table 'subscribers' is checked/created successfully."));
|
55 |
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Table 'applicants' is checked/created successfully."));
|
56 |
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Table 'demo_requests' is checked/created successfully."));
|
57 |
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Table 'contact_requests' is checked/created successfully."));
|
58 |
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Table 'purchases' is checked/created successfully."));
|
59 |
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Table 'jobs' is checked/created successfully."));
|
60 |
+
|
61 |
+
// Cleanup the spy
|
62 |
+
logSpy.mockRestore();
|
63 |
+
});
|
64 |
+
});
|
backend/__tests__/db/addSubscriber.test.js
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { addSubscriber } = require('../../utils/addSubscriber'); // Import the addSubscriber function
|
2 |
+
const { runQuery, runSelectQuery } = require('../../utils/queries'); // Import the functions that interact with the DB
|
3 |
+
|
4 |
+
jest.mock('../../utils/queries'); // Mock the runQuery and runSelectQuery functions
|
5 |
+
|
6 |
+
describe('addSubscriber', () => {
|
7 |
+
beforeEach(() => {
|
8 |
+
// Clear all previous mocks before each test
|
9 |
+
jest.clearAllMocks();
|
10 |
+
});
|
11 |
+
|
12 |
+
it('should return an error if the email already exists', async () => {
|
13 |
+
// Arrange: Mock the result of runSelectQuery to simulate that the email already exists
|
14 |
+
const email = '[email protected]';
|
15 |
+
runSelectQuery.mockResolvedValueOnce([{ email }]); // Simulate an existing email in the database
|
16 |
+
|
17 |
+
// Act: Call the addSubscriber function
|
18 |
+
const result = await addSubscriber(email);
|
19 |
+
|
20 |
+
// Assert: Ensure the function returns the correct error response
|
21 |
+
expect(result).toEqual({ status: 400, error: 'You have already subscribed!!' });
|
22 |
+
expect(runSelectQuery).toHaveBeenCalledWith('SELECT * FROM subscribers WHERE email = ?', [email]);
|
23 |
+
expect(runQuery).not.toHaveBeenCalled(); // Ensure runQuery was not called since email already exists
|
24 |
+
});
|
25 |
+
|
26 |
+
it('should add a new subscriber if the email does not exist', async () => {
|
27 |
+
// Arrange: Mock the result of runSelectQuery to simulate that the email does not exist
|
28 |
+
const email = '[email protected]';
|
29 |
+
runSelectQuery.mockResolvedValueOnce([]); // Simulate no matching email in the database
|
30 |
+
runQuery.mockResolvedValueOnce({}); // Mock the successful insert query
|
31 |
+
|
32 |
+
// Act: Call the addSubscriber function
|
33 |
+
const result = await addSubscriber(email);
|
34 |
+
|
35 |
+
// Assert: Ensure the subscriber was added and a success message is returned
|
36 |
+
expect(result).toEqual({ status: 200, message: 'Subscriber added' });
|
37 |
+
expect(runSelectQuery).toHaveBeenCalledWith('SELECT * FROM subscribers WHERE email = ?', [email]);
|
38 |
+
expect(runQuery).toHaveBeenCalledWith(
|
39 |
+
'INSERT INTO subscribers (date, time, email) VALUES (DATE("now"), TIME("now"), ?)',
|
40 |
+
[email]
|
41 |
+
);
|
42 |
+
});
|
43 |
+
|
44 |
+
it('should handle errors properly if there is a database error', async () => {
|
45 |
+
// Arrange: Simulate an error in the database operation
|
46 |
+
const email = '[email protected]';
|
47 |
+
runSelectQuery.mockResolvedValueOnce([]); // Simulate no matching email
|
48 |
+
runQuery.mockRejectedValueOnce(new Error('Database error')); // Simulate an error during the insert
|
49 |
+
|
50 |
+
// Act: Call the addSubscriber function
|
51 |
+
const result = await addSubscriber(email);
|
52 |
+
|
53 |
+
// Assert: Ensure the error is logged and the error response is returned
|
54 |
+
expect(result).toEqual({ status: 500, error: 'Failed to add subscriber' });
|
55 |
+
expect(runSelectQuery).toHaveBeenCalledWith('SELECT * FROM subscribers WHERE email = ?', [email]);
|
56 |
+
expect(runQuery).toHaveBeenCalledWith(
|
57 |
+
'INSERT INTO subscribers (date, time, email) VALUES (DATE("now"), TIME("now"), ?)',
|
58 |
+
[email]
|
59 |
+
);
|
60 |
+
});
|
61 |
+
|
62 |
+
it('should log success message when a new subscriber is added', async () => {
|
63 |
+
// Arrange: Mock the result of runSelectQuery to simulate that the email does not exist
|
64 |
+
const email = '[email protected]';
|
65 |
+
runSelectQuery.mockResolvedValueOnce([]); // Simulate no matching email
|
66 |
+
runQuery.mockResolvedValueOnce({}); // Mock successful insert
|
67 |
+
|
68 |
+
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); // Spy on console.log
|
69 |
+
|
70 |
+
// Act: Call the addSubscriber function
|
71 |
+
await addSubscriber(email);
|
72 |
+
|
73 |
+
// Assert: Ensure the success message is logged
|
74 |
+
expect(logSpy).toHaveBeenCalledWith('Subscriber added successfully');
|
75 |
+
|
76 |
+
logSpy.mockRestore(); // Clean up the spy
|
77 |
+
});
|
78 |
+
|
79 |
+
it('should log an error message if there is a database error', async () => {
|
80 |
+
// Arrange: Simulate an error scenario
|
81 |
+
const email = '[email protected]';
|
82 |
+
runSelectQuery.mockResolvedValueOnce([]); // Simulate no matching email
|
83 |
+
runQuery.mockRejectedValueOnce(new Error('Database error')); // Simulate insert error
|
84 |
+
|
85 |
+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); // Spy on console.error
|
86 |
+
|
87 |
+
// Act: Call the addSubscriber function
|
88 |
+
await addSubscriber(email);
|
89 |
+
|
90 |
+
// Assert: Ensure the error message is logged
|
91 |
+
expect(errorSpy).toHaveBeenCalledWith('Database error:', expect.any(Error));
|
92 |
+
|
93 |
+
errorSpy.mockRestore(); // Clean up the spy
|
94 |
+
});
|
95 |
+
});
|
backend/__tests__/db/contactRequestDB.test.js
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { checkExistingContactRequest, insertContactRequest } = require('../../utils/contactRequestDB');
|
2 |
+
const { runQuery, runSelectQuery } = require('../../utils/queries');
|
3 |
+
|
4 |
+
jest.mock('../../utils/queries');
|
5 |
+
|
6 |
+
describe('checkExistingContactRequest', () => {
|
7 |
+
beforeEach(() => {
|
8 |
+
jest.clearAllMocks();
|
9 |
+
});
|
10 |
+
|
11 |
+
it('should return true if a contact request with the same name, email, and subject exists', async () => {
|
12 |
+
const name = 'John Doe';
|
13 |
+
const email = '[email protected]';
|
14 |
+
const subject = 'Inquiry';
|
15 |
+
runSelectQuery.mockResolvedValueOnce([{ name, email, subject }]);
|
16 |
+
|
17 |
+
const result = await checkExistingContactRequest(name, email, subject);
|
18 |
+
|
19 |
+
expect(result).toBe(true);
|
20 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
21 |
+
expect.stringContaining(`SELECT * FROM contact_requests WHERE name = ? AND email = ? AND subject = ?;`),
|
22 |
+
[name, email, subject]
|
23 |
+
);
|
24 |
+
});
|
25 |
+
|
26 |
+
it('should return false if no contact request with the same name, email, and subject exists', async () => {
|
27 |
+
const name = 'Jane Doe';
|
28 |
+
const email = '[email protected]';
|
29 |
+
const subject = 'Support';
|
30 |
+
runSelectQuery.mockResolvedValueOnce([]);
|
31 |
+
|
32 |
+
const result = await checkExistingContactRequest(name, email, subject);
|
33 |
+
|
34 |
+
expect(result).toBe(false);
|
35 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
36 |
+
expect.stringContaining(`SELECT * FROM contact_requests WHERE name = ? AND email = ? AND subject = ?;`),
|
37 |
+
[name, email, subject]
|
38 |
+
);
|
39 |
+
});
|
40 |
+
|
41 |
+
it('should handle database errors gracefully when checking existing contact requests', async () => {
|
42 |
+
const name = 'John Doe';
|
43 |
+
const email = '[email protected]';
|
44 |
+
const subject = 'Inquiry';
|
45 |
+
runSelectQuery.mockRejectedValueOnce(new Error('Database error'));
|
46 |
+
|
47 |
+
await expect(checkExistingContactRequest(name, email, subject)).rejects.toThrow('Database error');
|
48 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
49 |
+
expect.stringContaining(`SELECT * FROM contact_requests WHERE name = ? AND email = ? AND subject = ?;`),
|
50 |
+
[name, email, subject]
|
51 |
+
);
|
52 |
+
});
|
53 |
+
});
|
54 |
+
|
55 |
+
describe('insertContactRequest', () => {
|
56 |
+
beforeEach(() => {
|
57 |
+
jest.clearAllMocks();
|
58 |
+
});
|
59 |
+
|
60 |
+
it('should insert a contact request successfully', async () => {
|
61 |
+
const name = 'John Doe';
|
62 |
+
const email = '[email protected]';
|
63 |
+
const phone = '1234567890';
|
64 |
+
const subject = 'Inquiry';
|
65 |
+
const message = 'Hello, I have a question.';
|
66 |
+
runQuery.mockResolvedValueOnce({});
|
67 |
+
|
68 |
+
await insertContactRequest(name, email, phone, subject, message);
|
69 |
+
|
70 |
+
expect(runQuery).toHaveBeenCalledWith(
|
71 |
+
expect.stringContaining(`INSERT INTO contact_requests (date, time, name, email, phone, subject, message) VALUES (DATE("now"), TIME("now"), ?, ?, ?, ?, ?);`),
|
72 |
+
[name, email, phone, subject, message]
|
73 |
+
);
|
74 |
+
});
|
75 |
+
|
76 |
+
it('should handle errors properly if the insert query fails', async () => {
|
77 |
+
const name = 'John Doe';
|
78 |
+
const email = '[email protected]';
|
79 |
+
const phone = '1234567890';
|
80 |
+
const subject = 'Inquiry';
|
81 |
+
const message = 'Hello, I have a question.';
|
82 |
+
runQuery.mockRejectedValueOnce(new Error('Database error'));
|
83 |
+
|
84 |
+
await expect(insertContactRequest(name, email, phone, subject, message)).rejects.toThrow('Database error');
|
85 |
+
expect(runQuery).toHaveBeenCalledWith(
|
86 |
+
expect.stringContaining(`INSERT INTO contact_requests (date, time, name, email, phone, subject, message) VALUES (DATE("now"), TIME("now"), ?, ?, ?, ?, ?);`),
|
87 |
+
[name, email, phone, subject, message]
|
88 |
+
);
|
89 |
+
});
|
90 |
+
});
|
backend/__tests__/db/demoRequestDB.test.js
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { checkExistingDemoRequest, insertDemoRequest } = require('../../utils/demoRequestDB');
|
2 |
+
const { runQuery, runSelectQuery } = require('../../utils/queries');
|
3 |
+
|
4 |
+
jest.mock('../../utils/queries');
|
5 |
+
|
6 |
+
describe('checkExistingDemoRequest', () => {
|
7 |
+
beforeEach(() => {
|
8 |
+
jest.clearAllMocks();
|
9 |
+
});
|
10 |
+
|
11 |
+
it('should return true if a demo request with the same name, email, product, demoDate, and phone exists', async () => {
|
12 |
+
const name = 'John Doe';
|
13 |
+
const email = '[email protected]';
|
14 |
+
const product = 'Software XYZ';
|
15 |
+
const demoDate = '2025-02-15';
|
16 |
+
const phone = '1234567890';
|
17 |
+
runSelectQuery.mockResolvedValueOnce([{ name, email, product, demo_date: demoDate, phone }]);
|
18 |
+
|
19 |
+
const result = await checkExistingDemoRequest(name, email, product, demoDate, phone);
|
20 |
+
|
21 |
+
expect(result).toBe(true);
|
22 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
23 |
+
expect.stringContaining(`
|
24 |
+
SELECT * FROM demo_requests
|
25 |
+
WHERE name = ? AND email = ? AND product = ?
|
26 |
+
AND ABS(julianday(demo_date) - julianday(?)) <= 15 AND phone = ?;
|
27 |
+
`),
|
28 |
+
[name, email, product, demoDate, phone]
|
29 |
+
);
|
30 |
+
});
|
31 |
+
|
32 |
+
it('should return false if no demo request with the same name, email, product, demoDate, and phone exists', async () => {
|
33 |
+
const name = 'Jane Doe';
|
34 |
+
const email = '[email protected]';
|
35 |
+
const product = 'Software XYZ';
|
36 |
+
const demoDate = '2025-02-15';
|
37 |
+
const phone = '9876543210';
|
38 |
+
runSelectQuery.mockResolvedValueOnce([]);
|
39 |
+
|
40 |
+
const result = await checkExistingDemoRequest(name, email, product, demoDate, phone);
|
41 |
+
|
42 |
+
expect(result).toBe(false);
|
43 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
44 |
+
expect.stringContaining(`
|
45 |
+
SELECT * FROM demo_requests
|
46 |
+
WHERE name = ? AND email = ? AND product = ?
|
47 |
+
AND ABS(julianday(demo_date) - julianday(?)) <= 15 AND phone = ?;
|
48 |
+
`),
|
49 |
+
[name, email, product, demoDate, phone]
|
50 |
+
);
|
51 |
+
});
|
52 |
+
|
53 |
+
it('should handle database errors gracefully when checking existing demo requests', async () => {
|
54 |
+
const name = 'John Doe';
|
55 |
+
const email = '[email protected]';
|
56 |
+
const product = 'Software XYZ';
|
57 |
+
const demoDate = '2025-02-15';
|
58 |
+
const phone = '1234567890';
|
59 |
+
runSelectQuery.mockRejectedValueOnce(new Error('Database error'));
|
60 |
+
|
61 |
+
await expect(checkExistingDemoRequest(name, email, product, demoDate, phone)).rejects.toThrow('Database error');
|
62 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
63 |
+
expect.stringContaining(`
|
64 |
+
SELECT * FROM demo_requests
|
65 |
+
WHERE name = ? AND email = ? AND product = ?
|
66 |
+
AND ABS(julianday(demo_date) - julianday(?)) <= 15 AND phone = ?;
|
67 |
+
`),
|
68 |
+
[name, email, product, demoDate, phone]
|
69 |
+
);
|
70 |
+
});
|
71 |
+
});
|
72 |
+
|
73 |
+
describe('insertDemoRequest', () => {
|
74 |
+
beforeEach(() => {
|
75 |
+
jest.clearAllMocks();
|
76 |
+
});
|
77 |
+
|
78 |
+
it('should insert a demo request successfully', async () => {
|
79 |
+
const name = 'John Doe';
|
80 |
+
const email = '[email protected]';
|
81 |
+
const company = 'ABC Corp';
|
82 |
+
const product = 'Software XYZ';
|
83 |
+
const demoDate = '2025-02-15';
|
84 |
+
const slot = '10:00 AM';
|
85 |
+
const phone = '1234567890';
|
86 |
+
const message = 'Looking forward to the demo!';
|
87 |
+
runQuery.mockResolvedValueOnce({});
|
88 |
+
|
89 |
+
await insertDemoRequest(name, email, company, product, demoDate, slot, phone, message);
|
90 |
+
|
91 |
+
expect(runQuery).toHaveBeenCalledWith(
|
92 |
+
expect.stringContaining(`
|
93 |
+
INSERT INTO demo_requests (date, time, name, email, company, product, demo_date, slot, phone, comments)
|
94 |
+
VALUES (DATE("now"), TIME("now"), ?, ?, ?, ?, ?, ?, ?, ?);
|
95 |
+
`),
|
96 |
+
[name, email, company, product, demoDate, slot, phone, message]
|
97 |
+
);
|
98 |
+
});
|
99 |
+
|
100 |
+
it('should handle errors properly if the insert query fails', async () => {
|
101 |
+
const name = 'John Doe';
|
102 |
+
const email = '[email protected]';
|
103 |
+
const company = 'ABC Corp';
|
104 |
+
const product = 'Software XYZ';
|
105 |
+
const demoDate = '2025-02-15';
|
106 |
+
const slot = '10:00 AM';
|
107 |
+
const phone = '1234567890';
|
108 |
+
const message = 'Looking forward to the demo!';
|
109 |
+
runQuery.mockRejectedValueOnce(new Error('Database error'));
|
110 |
+
|
111 |
+
await expect(insertDemoRequest(name, email, company, product, demoDate, slot, phone, message)).rejects.toThrow('Database error');
|
112 |
+
expect(runQuery).toHaveBeenCalledWith(
|
113 |
+
expect.stringContaining(`
|
114 |
+
INSERT INTO demo_requests (date, time, name, email, company, product, demo_date, slot, phone, comments)
|
115 |
+
VALUES (DATE("now"), TIME("now"), ?, ?, ?, ?, ?, ?, ?, ?);
|
116 |
+
`),
|
117 |
+
[name, email, company, product, demoDate, slot, phone, message]
|
118 |
+
);
|
119 |
+
});
|
120 |
+
});
|
backend/__tests__/db/jobRequestDB.test.js
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { checkExistingJobApplication, insertJobApplication } = require('../../utils/jobRequestDB');
|
2 |
+
const { runQuery, runSelectQuery } = require('../../utils/queries');
|
3 |
+
|
4 |
+
jest.mock('../../utils/queries');
|
5 |
+
|
6 |
+
describe('checkExistingJobApplication', () => {
|
7 |
+
beforeEach(() => {
|
8 |
+
jest.clearAllMocks();
|
9 |
+
});
|
10 |
+
|
11 |
+
it('should return true if a job application with the same name, email, and role exists', async () => {
|
12 |
+
const name = 'John Doe';
|
13 |
+
const email = '[email protected]';
|
14 |
+
const role = 'Developer';
|
15 |
+
|
16 |
+
runSelectQuery.mockResolvedValueOnce([{ name, email, role }]);
|
17 |
+
|
18 |
+
const result = await checkExistingJobApplication(name, email, role);
|
19 |
+
|
20 |
+
expect(result).toBe(true);
|
21 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
22 |
+
expect.stringContaining(`SELECT * FROM applicants WHERE name = ? AND email = ? AND role = ?;`),
|
23 |
+
[name, email, role]
|
24 |
+
);
|
25 |
+
});
|
26 |
+
|
27 |
+
it('should return false if no job application with the same name, email, and role exists', async () => {
|
28 |
+
const name = 'Jane Doe';
|
29 |
+
const email = '[email protected]';
|
30 |
+
const role = 'Designer';
|
31 |
+
|
32 |
+
runSelectQuery.mockResolvedValueOnce([]);
|
33 |
+
|
34 |
+
const result = await checkExistingJobApplication(name, email, role);
|
35 |
+
|
36 |
+
expect(result).toBe(false);
|
37 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
38 |
+
expect.stringContaining(`SELECT * FROM applicants WHERE name = ? AND email = ? AND role = ?;`),
|
39 |
+
[name, email, role]
|
40 |
+
);
|
41 |
+
});
|
42 |
+
|
43 |
+
it('should handle database errors gracefully when checking for existing job applications', async () => {
|
44 |
+
const name = 'John Doe';
|
45 |
+
const email = '[email protected]';
|
46 |
+
const role = 'Developer';
|
47 |
+
|
48 |
+
runSelectQuery.mockRejectedValueOnce(new Error('Database error'));
|
49 |
+
|
50 |
+
await expect(checkExistingJobApplication(name, email, role)).rejects.toThrow('Database error');
|
51 |
+
expect(runSelectQuery).toHaveBeenCalledWith(
|
52 |
+
expect.stringContaining(`SELECT * FROM applicants WHERE name = ? AND email = ? AND role = ?;`),
|
53 |
+
[name, email, role]
|
54 |
+
);
|
55 |
+
});
|
56 |
+
});
|
57 |
+
|
58 |
+
describe('insertJobApplication', () => {
|
59 |
+
beforeEach(() => {
|
60 |
+
jest.clearAllMocks();
|
61 |
+
});
|
62 |
+
|
63 |
+
it('should insert a job application successfully', async () => {
|
64 |
+
const name = 'John Doe';
|
65 |
+
const email = '[email protected]';
|
66 |
+
const phone = '1234567890';
|
67 |
+
const experience = '5 years';
|
68 |
+
const role = 'Developer';
|
69 |
+
const linkedin = 'https://linkedin.com/johndoe';
|
70 |
+
const resume = Buffer.from('resume data');
|
71 |
+
const filename = 'resume.pdf';
|
72 |
+
|
73 |
+
runQuery.mockResolvedValueOnce({});
|
74 |
+
|
75 |
+
await insertJobApplication(name, email, phone, experience, role, linkedin, resume, filename);
|
76 |
+
|
77 |
+
expect(runQuery).toHaveBeenCalledWith(
|
78 |
+
expect.stringContaining(
|
79 |
+
`INSERT INTO applicants (date, time, name, email, phone, experience, role, linkedin, resume, filename) VALUES (DATE("now"), TIME("now"), ?, ?, ?, ?, ?, ?, ?, ?);`
|
80 |
+
),
|
81 |
+
[name, email, phone, experience, role, linkedin, resume, filename]
|
82 |
+
);
|
83 |
+
});
|
84 |
+
|
85 |
+
it('should handle errors properly if the insert query fails', async () => {
|
86 |
+
const name = 'John Doe';
|
87 |
+
const email = '[email protected]';
|
88 |
+
const phone = '1234567890';
|
89 |
+
const experience = '5 years';
|
90 |
+
const role = 'Developer';
|
91 |
+
const linkedin = 'https://linkedin.com/johndoe';
|
92 |
+
const resume = Buffer.from('resume data');
|
93 |
+
const filename = 'resume.pdf';
|
94 |
+
|
95 |
+
runQuery.mockRejectedValueOnce(new Error('Database error'));
|
96 |
+
|
97 |
+
await expect(insertJobApplication(name, email, phone, experience, role, linkedin, resume, filename)).rejects.toThrow('Database error');
|
98 |
+
expect(runQuery).toHaveBeenCalledWith(
|
99 |
+
expect.stringContaining(
|
100 |
+
`INSERT INTO applicants (date, time, name, email, phone, experience, role, linkedin, resume, filename) VALUES (DATE("now"), TIME("now"), ?, ?, ?, ?, ?, ?, ?, ?);`
|
101 |
+
),
|
102 |
+
[name, email, phone, experience, role, linkedin, resume, filename]
|
103 |
+
);
|
104 |
+
});
|
105 |
+
});
|
backend/__tests__/routes/applicantRoutes.test.js
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const request = require('supertest');
|
2 |
+
const express = require('express');
|
3 |
+
const app = express();
|
4 |
+
const { submitJobApplication } = require('../../controller/applicantController');
|
5 |
+
const applicantRoutes = require('../../routes/applicantRoutes');
|
6 |
+
|
7 |
+
// Jest timeout increased to prevent timeouts
|
8 |
+
jest.setTimeout(10000);
|
9 |
+
|
10 |
+
// Middleware to parse JSON and form data
|
11 |
+
app.use(express.json());
|
12 |
+
app.use(express.urlencoded({ extended: true }));
|
13 |
+
|
14 |
+
// Mocking the controller function
|
15 |
+
jest.mock('../../controller/applicantController', () => ({
|
16 |
+
submitJobApplication: jest.fn(),
|
17 |
+
}));
|
18 |
+
|
19 |
+
// Use the route in the app
|
20 |
+
app.use('/api', applicantRoutes);
|
21 |
+
|
22 |
+
describe('Applicant Routes', () => {
|
23 |
+
beforeEach(() => {
|
24 |
+
jest.clearAllMocks();
|
25 |
+
});
|
26 |
+
|
27 |
+
it('should successfully submit a job application', async () => {
|
28 |
+
const mockFile = Buffer.from('fake file content');
|
29 |
+
|
30 |
+
// Mock successful response
|
31 |
+
submitJobApplication.mockImplementation((req, res) => {
|
32 |
+
res.status(200).json({ message: 'Application submitted successfully' });
|
33 |
+
});
|
34 |
+
|
35 |
+
const response = await request(app)
|
36 |
+
.post('/api/submit-job-application')
|
37 |
+
.attach('resume', mockFile, 'test_resume.pdf')
|
38 |
+
.field('name', 'John Doe')
|
39 |
+
.field('email', '[email protected]')
|
40 |
+
.expect(200);
|
41 |
+
|
42 |
+
expect(response.body.message).toBe('Application submitted successfully');
|
43 |
+
});
|
44 |
+
|
45 |
+
it('should return 400 if no resume is provided', async () => {
|
46 |
+
submitJobApplication.mockImplementation((req, res) => {
|
47 |
+
res.status(400).json({ error: 'No resume uploaded' });
|
48 |
+
});
|
49 |
+
|
50 |
+
const response = await request(app)
|
51 |
+
.post('/api/submit-job-application')
|
52 |
+
.field('name', 'John Doe')
|
53 |
+
.field('email', '[email protected]')
|
54 |
+
.expect(400);
|
55 |
+
|
56 |
+
expect(response.body.error).toBe('No resume uploaded');
|
57 |
+
});
|
58 |
+
|
59 |
+
it('should handle errors gracefully', async () => {
|
60 |
+
submitJobApplication.mockImplementation((req, res) => {
|
61 |
+
res.status(500).json({ error: 'Something went wrong' });
|
62 |
+
});
|
63 |
+
|
64 |
+
const response = await request(app)
|
65 |
+
.post('/api/submit-job-application')
|
66 |
+
.attach('resume', Buffer.from('fake file content'), 'test_resume.pdf')
|
67 |
+
.field('name', 'John Doe')
|
68 |
+
.field('email', '[email protected]')
|
69 |
+
.expect(500);
|
70 |
+
|
71 |
+
expect(response.body.error).toBe('Something went wrong');
|
72 |
+
});
|
73 |
+
});
|
backend/__tests__/testSetup.js
ADDED
File without changes
|
backend/allure-results/0015a6cd-e653-4a79-ba26-0af43fabbf07-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0015a6cd-e653-4a79-ba26-0af43fabbf07","name":"should handle empty results gracefully","historyId":"07c64f737f02e55d0fcf270fc88e4122:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.controller.demo.dateAvailability.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"2"},{"name":"parentSuite","value":"Date Availability Module"},{"name":"suite","value":"checkDateAvailability"}],"links":[],"start":1738134362878,"fullName":"__tests__/controller/demo/dateAvailability.test.js#Date Availability Module checkDateAvailability should handle empty results gracefully","testCaseId":"07c64f737f02e55d0fcf270fc88e4122","stop":1738134362879}
|
backend/allure-results/0110d1ba-d337-4de1-9021-f9af16a0683f-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0110d1ba-d337-4de1-9021-f9af16a0683f","name":"beforeEach","children":["29098c4e-4c22-4620-ab3b-cbb19e3d20c7"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134362293,"name":"beforeEach","stop":1738134362293}],"afters":[]}
|
backend/allure-results/016c5d0f-35ce-411e-90a1-61de74ab2ca3-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"016c5d0f-35ce-411e-90a1-61de74ab2ca3","name":"beforeEach","children":["089e2be5-c31a-455c-841e-3f676ddfd3f6"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134362568,"name":"beforeEach","stop":1738134362568}],"afters":[]}
|
backend/allure-results/01ea98eb-5e95-43b8-b98c-48074cf6284d-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"01ea98eb-5e95-43b8-b98c-48074cf6284d","name":"beforeEach","children":["0eb2fc93-b1c7-40eb-b8e3-c6698747952f"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134363951,"name":"beforeEach","stop":1738134363952}],"afters":[]}
|
backend/allure-results/01f5e8cb-ddab-404c-b698-c33f012c014b-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"01f5e8cb-ddab-404c-b698-c33f012c014b","name":"afterEach","children":["1982483e-00ea-4e03-8760-34a80ea65b71"],"befores":[],"afters":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738133802587,"name":"afterEach","stop":1738133802587}]}
|
backend/allure-results/02297a4a-89e7-479a-b92e-a8cd2bee7916-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"02297a4a-89e7-479a-b92e-a8cd2bee7916","name":"beforeEach","children":["413b14db-f238-4422-971b-08c18c5659af"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134319166,"name":"beforeEach","stop":1738134319166}],"afters":[]}
|
backend/allure-results/02c0b00b-1aa8-498b-83a2-1a37af9977ba-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"02c0b00b-1aa8-498b-83a2-1a37af9977ba","name":"should return a list of booked slots for a given date","historyId":"13bd204f774f5d66e5352bb0e5628906:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.controller.demo.slots.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"3"},{"name":"parentSuite","value":"Slots Controller"},{"name":"suite","value":"getBookedSlots"}],"links":[],"start":1738133800851,"fullName":"__tests__/controller/demo/slots.test.js#Slots Controller getBookedSlots should return a list of booked slots for a given date","testCaseId":"13bd204f774f5d66e5352bb0e5628906","stop":1738133800853}
|
backend/allure-results/02f5da12-8970-4dff-aeb3-75c9714f2818-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"02f5da12-8970-4dff-aeb3-75c9714f2818","name":"beforeEach","children":["caa49835-a394-4e87-bb29-8eaf769513f0"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134318609,"name":"beforeEach","stop":1738134318610}],"afters":[]}
|
backend/allure-results/0336cd99-4664-4319-8288-b6712f0f9353-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0336cd99-4664-4319-8288-b6712f0f9353","name":"afterEach","children":["47b2aca6-5e15-48de-9395-c0198587de22"],"befores":[],"afters":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738133804125,"name":"afterEach","stop":1738133804125}]}
|
backend/allure-results/0394dd9b-43c3-4ae1-93e9-563120bc3609-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0394dd9b-43c3-4ae1-93e9-563120bc3609","name":"beforeEach","children":["6ffbe1df-a94f-4b3e-93bf-debf5f7b7aba"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738133803622,"name":"beforeEach","stop":1738133803623}],"afters":[]}
|
backend/allure-results/04c86ce6-b07e-4c64-af20-1a52682cc83d-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"04c86ce6-b07e-4c64-af20-1a52682cc83d","name":"afterEach","children":["0015a6cd-e653-4a79-ba26-0af43fabbf07"],"befores":[],"afters":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134362879,"name":"afterEach","stop":1738134362879}]}
|
backend/allure-results/04ea9714-733a-45e9-b0cc-7cc1aa55db15-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"04ea9714-733a-45e9-b0cc-7cc1aa55db15","name":"afterEach","children":["2b018f77-86c2-4aca-97ba-0f7a13f6165c"],"befores":[],"afters":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134360705,"name":"afterEach","stop":1738134360707}]}
|
backend/allure-results/05878ed9-0262-45d3-a27e-270827d51608-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"05878ed9-0262-45d3-a27e-270827d51608","name":"should return an error if OAuth credentials are missing","historyId":"734c4f6702939dad1db8c195b765d5c9:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.controller.subscriptionController.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"1"},{"name":"parentSuite","value":"handleSubscriptionEmail"}],"links":[],"start":1738133803636,"fullName":"__tests__/controller/subscriptionController.test.js#handleSubscriptionEmail should return an error if OAuth credentials are missing","testCaseId":"734c4f6702939dad1db8c195b765d5c9","stop":1738133803639}
|
backend/allure-results/05cbfb2c-ea16-4f24-bd3d-f2386b85cde6-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"05cbfb2c-ea16-4f24-bd3d-f2386b85cde6","name":"beforeEach","children":["34b5f767-d539-4c9b-a4e4-df3a2b2e1237"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134361718,"name":"beforeEach","stop":1738134361718}],"afters":[]}
|
backend/allure-results/06bb4d63-55c7-44df-9572-66d7583c1658-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"06bb4d63-55c7-44df-9572-66d7583c1658","name":"beforeEach","children":["3c8e9d74-108a-4549-9123-bccb972f7b43"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134364433,"name":"beforeEach","stop":1738134364433}],"afters":[]}
|
backend/allure-results/077c5367-cc30-4fef-83c9-68b2137156b1-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"077c5367-cc30-4fef-83c9-68b2137156b1","name":"beforeEach","children":["e0800728-f403-4bb0-902f-ca1d478d4c9c"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134316718,"name":"beforeEach","stop":1738134316718}],"afters":[]}
|
backend/allure-results/07e2e71f-c12c-42aa-83ef-ae7c75fc7a73-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"07e2e71f-c12c-42aa-83ef-ae7c75fc7a73","name":"should call runQuery for each table creation query","historyId":"2708c10dc7a9525026f0ef93874778a6:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.db.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"3"},{"name":"parentSuite","value":"initializeDatabase"}],"links":[],"start":1738134363945,"fullName":"__tests__/db.test.js#initializeDatabase should call runQuery for each table creation query","testCaseId":"2708c10dc7a9525026f0ef93874778a6","stop":1738134363984}
|
backend/allure-results/089e2be5-c31a-455c-841e-3f676ddfd3f6-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"089e2be5-c31a-455c-841e-3f676ddfd3f6","name":"should handle errors properly if the insert query fails","historyId":"ecb0f74af105dfe6dc2534c3c03b6394:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.db.jobRequestDB.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"1"},{"name":"parentSuite","value":"insertJobApplication"}],"links":[],"start":1738134362568,"fullName":"__tests__/db/jobRequestDB.test.js#insertJobApplication should handle errors properly if the insert query fails","testCaseId":"ecb0f74af105dfe6dc2534c3c03b6394","stop":1738134362571}
|
backend/allure-results/092b09aa-d891-4211-b725-2cb53f0bc367-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"092b09aa-d891-4211-b725-2cb53f0bc367","name":"should handle errors properly","historyId":"576da12b25442688502c27aba589b5f4:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.controller.demo.holidays.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"3"},{"name":"parentSuite","value":"Holidays Controller"},{"name":"suite","value":"getIndiaTamilHolidays"}],"links":[],"start":1738134316692,"fullName":"__tests__/controller/demo/holidays.test.js#Holidays Controller getIndiaTamilHolidays should handle errors properly","testCaseId":"576da12b25442688502c27aba589b5f4","stop":1738134316698}
|
backend/allure-results/098ef27c-b326-44df-9190-c2f20c1ada8e-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"098ef27c-b326-44df-9190-c2f20c1ada8e","name":"beforeEach","children":["2f843ff8-0689-4452-8006-19d5646056cc"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134318200,"name":"beforeEach","stop":1738134318200}],"afters":[]}
|
backend/allure-results/0a34e919-c2a0-47c0-8c9c-f7baa3bb0098-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0a34e919-c2a0-47c0-8c9c-f7baa3bb0098","name":"should return true if a job application with the same name, email, and role exists","historyId":"2eaa16996f06ff3c1362aed34070279c:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.db.jobRequestDB.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"1"},{"name":"parentSuite","value":"checkExistingJobApplication"}],"links":[],"start":1738134318180,"fullName":"__tests__/db/jobRequestDB.test.js#checkExistingJobApplication should return true if a job application with the same name, email, and role exists","testCaseId":"2eaa16996f06ff3c1362aed34070279c","stop":1738134318193}
|
backend/allure-results/0a4254f9-1cdb-4c56-8bba-bfc9e2424b4d-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0a4254f9-1cdb-4c56-8bba-bfc9e2424b4d","name":"afterEach","children":["a43c59fb-041b-4808-a344-b7df55474f39"],"befores":[],"afters":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738133802982,"name":"afterEach","stop":1738133802982}]}
|
backend/allure-results/0a5de36d-ec71-4d0a-9d60-f17bc71ead0f-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0a5de36d-ec71-4d0a-9d60-f17bc71ead0f","name":"should throw an error if general slots are not properly defined","historyId":"6dfb9140c55c1ec840a8bfa35c817316:d41d8cd98f00b204e9800998ecf8427e","status":"broken","statusDetails":{"message":"undefined is not iterable (cannot read property Symbol(Symbol.iterator))","trace":"TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))\n at _iterableToArray (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/iterableToArray.js:2:48)\n at _toConsumableArray (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/toConsumableArray.js:6:34)\n at _callee10$ (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/__tests__/controller/demo/slots.test.js:133:39)\n at tryCatch (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/regeneratorRuntime.js:45:16)\n at Generator.<anonymous> (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/regeneratorRuntime.js:133:17)\n at Generator.next (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/regeneratorRuntime.js:74:21)\n at asyncGeneratorStep (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:17)\n at _next (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/asyncToGenerator.js:17:9)\n at /data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:7\n at new Promise (<anonymous>)\n at Object.<anonymous> (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/@babel/runtime/helpers/asyncToGenerator.js:14:12)\n at Promise.then.completed (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/utils.js:298:28)\n at new Promise (<anonymous>)\n at callAsyncCircusFn (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/utils.js:231:10)\n at _callCircusTest (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/run.js:316:40)\n at processTicksAndRejections (node:internal/process/task_queues:96:5)\n at _runTest (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/run.js:252:3)\n at _runTestsForDescribeBlock (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/run.js:126:9)\n at _runTestsForDescribeBlock (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/run.js:121:9)\n at _runTestsForDescribeBlock (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/run.js:121:9)\n at run (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/run.js:71:3)\n at runAndTransformResultsToJestFormat (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)\n at jestAdapter (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)\n at runTestInternal (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-runner/build/runTest.js:367:16)\n at runTest (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-runner/build/runTest.js:444:34)\n at Object.worker (/data/darshan/data/genomatics-site-react-tailwind/versions/geno-react-modularised/backend/node_modules/jest-runner/build/testWorker.js:106:12)"},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.controller.demo.slots.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"3"},{"name":"parentSuite","value":"Slots Controller"},{"name":"suite","value":"getAvailableSlots"}],"links":[],"start":1738133800901,"fullName":"__tests__/controller/demo/slots.test.js#Slots Controller getAvailableSlots should throw an error if general slots are not properly defined","testCaseId":"6dfb9140c55c1ec840a8bfa35c817316","stop":1738133800903}
|
backend/allure-results/0bdecd77-aed5-4cbc-bec8-4149ac95cf00-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0bdecd77-aed5-4cbc-bec8-4149ac95cf00","name":"beforeEach","children":["23ae57ad-ca22-4cd3-b75c-da0bd515c826"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134316940,"name":"beforeEach","stop":1738134316940}],"afters":[]}
|
backend/allure-results/0ca292e2-e5f8-4bfd-959d-46fb6c883444-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0ca292e2-e5f8-4bfd-959d-46fb6c883444","name":"should handle errors properly if the insert query fails","historyId":"ebe32158e8d9d95830c96140ceb08de9:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.db.demoRequestDB.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"2"},{"name":"parentSuite","value":"insertDemoRequest"}],"links":[],"start":1738133804135,"fullName":"__tests__/db/demoRequestDB.test.js#insertDemoRequest should handle errors properly if the insert query fails","testCaseId":"ebe32158e8d9d95830c96140ceb08de9","stop":1738133804137}
|
backend/allure-results/0d6fb9a5-b36a-4e35-b985-d89ac351cc09-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0d6fb9a5-b36a-4e35-b985-d89ac351cc09","name":"beforeEach","children":["c0033509-0b15-4b75-ab25-65b996d98a64"],"befores":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134318957,"name":"beforeEach","stop":1738134318957}],"afters":[]}
|
backend/allure-results/0da168e7-8953-4657-9458-dd5e186abe96-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0da168e7-8953-4657-9458-dd5e186abe96","name":"should return true if a demo request with the same name, email, product, demoDate, and phone exists","historyId":"cf295a79793f2c923b75425847a58d87:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.db.demoRequestDB.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"1"},{"name":"parentSuite","value":"checkExistingDemoRequest"}],"links":[],"start":1738134362943,"fullName":"__tests__/db/demoRequestDB.test.js#checkExistingDemoRequest should return true if a demo request with the same name, email, product, demoDate, and phone exists","testCaseId":"cf295a79793f2c923b75425847a58d87","stop":1738134362961}
|
backend/allure-results/0dc24df0-7b61-44bb-af09-0f71a0d88f53-container.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0dc24df0-7b61-44bb-af09-0f71a0d88f53","name":"afterEach","children":["2ff5b722-f94a-4bd7-84b3-c862281b159c"],"befores":[],"afters":[{"status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"start":1738134317142,"name":"afterEach","stop":1738134317142}]}
|
backend/allure-results/0dd25a26-4fcb-4370-a3e5-52758cee400a-result.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"uuid":"0dd25a26-4fcb-4370-a3e5-52758cee400a","name":"should return all general slots if no slots are booked for a given date","historyId":"f6b2f672fe18b502f2bafc9e90dfca64:d41d8cd98f00b204e9800998ecf8427e","status":"passed","statusDetails":{},"stage":"finished","steps":[],"attachments":[],"parameters":[],"labels":[{"name":"language","value":"javascript"},{"name":"framework","value":"jest"},{"name":"package","value":"__tests__.controller.demo.slots.test.js"},{"name":"host","value":"cur4"},{"name":"thread","value":"2"},{"name":"parentSuite","value":"Slots Controller"},{"name":"suite","value":"getAvailableSlots"}],"links":[],"start":1738134316722,"fullName":"__tests__/controller/demo/slots.test.js#Slots Controller getAvailableSlots should return all general slots if no slots are booked for a given date","testCaseId":"f6b2f672fe18b502f2bafc9e90dfca64","stop":1738134316725}
|