i-darrshan commited on
Commit
e08ecbf
·
1 Parent(s): ba6ba3d

initial_update

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +27 -0
  2. Dockerfile +40 -0
  3. README.md +4 -4
  4. backend/__tests__/config/googleOAuth.test.js +77 -0
  5. backend/__tests__/config/refreshTokens.test.js +101 -0
  6. backend/__tests__/controller/applicantController.test.js +132 -0
  7. backend/__tests__/controller/authController.test.js +81 -0
  8. backend/__tests__/controller/contactController.test.js +198 -0
  9. backend/__tests__/controller/demo/dateAvailability.test.js +81 -0
  10. backend/__tests__/controller/demo/holidays.test.js +128 -0
  11. backend/__tests__/controller/demo/slots.test.js +142 -0
  12. backend/__tests__/controller/demo/utils.test.js +77 -0
  13. backend/__tests__/controller/demoRequestController.test.js +293 -0
  14. backend/__tests__/controller/subscriptionController.test.js +131 -0
  15. backend/__tests__/db.test.js +64 -0
  16. backend/__tests__/db/addSubscriber.test.js +95 -0
  17. backend/__tests__/db/contactRequestDB.test.js +90 -0
  18. backend/__tests__/db/demoRequestDB.test.js +120 -0
  19. backend/__tests__/db/jobRequestDB.test.js +105 -0
  20. backend/__tests__/routes/applicantRoutes.test.js +73 -0
  21. backend/__tests__/testSetup.js +0 -0
  22. backend/allure-results/0015a6cd-e653-4a79-ba26-0af43fabbf07-result.json +1 -0
  23. backend/allure-results/0110d1ba-d337-4de1-9021-f9af16a0683f-container.json +1 -0
  24. backend/allure-results/016c5d0f-35ce-411e-90a1-61de74ab2ca3-container.json +1 -0
  25. backend/allure-results/01ea98eb-5e95-43b8-b98c-48074cf6284d-container.json +1 -0
  26. backend/allure-results/01f5e8cb-ddab-404c-b698-c33f012c014b-container.json +1 -0
  27. backend/allure-results/02297a4a-89e7-479a-b92e-a8cd2bee7916-container.json +1 -0
  28. backend/allure-results/02c0b00b-1aa8-498b-83a2-1a37af9977ba-result.json +1 -0
  29. backend/allure-results/02f5da12-8970-4dff-aeb3-75c9714f2818-container.json +1 -0
  30. backend/allure-results/0336cd99-4664-4319-8288-b6712f0f9353-container.json +1 -0
  31. backend/allure-results/0394dd9b-43c3-4ae1-93e9-563120bc3609-container.json +1 -0
  32. backend/allure-results/04c86ce6-b07e-4c64-af20-1a52682cc83d-container.json +1 -0
  33. backend/allure-results/04ea9714-733a-45e9-b0cc-7cc1aa55db15-container.json +1 -0
  34. backend/allure-results/05878ed9-0262-45d3-a27e-270827d51608-result.json +1 -0
  35. backend/allure-results/05cbfb2c-ea16-4f24-bd3d-f2386b85cde6-container.json +1 -0
  36. backend/allure-results/06bb4d63-55c7-44df-9572-66d7583c1658-container.json +1 -0
  37. backend/allure-results/077c5367-cc30-4fef-83c9-68b2137156b1-container.json +1 -0
  38. backend/allure-results/07e2e71f-c12c-42aa-83ef-ae7c75fc7a73-result.json +1 -0
  39. backend/allure-results/089e2be5-c31a-455c-841e-3f676ddfd3f6-result.json +1 -0
  40. backend/allure-results/092b09aa-d891-4211-b725-2cb53f0bc367-result.json +1 -0
  41. backend/allure-results/098ef27c-b326-44df-9190-c2f20c1ada8e-container.json +1 -0
  42. backend/allure-results/0a34e919-c2a0-47c0-8c9c-f7baa3bb0098-result.json +1 -0
  43. backend/allure-results/0a4254f9-1cdb-4c56-8bba-bfc9e2424b4d-container.json +1 -0
  44. backend/allure-results/0a5de36d-ec71-4d0a-9d60-f17bc71ead0f-result.json +1 -0
  45. backend/allure-results/0bdecd77-aed5-4cbc-bec8-4149ac95cf00-container.json +1 -0
  46. backend/allure-results/0ca292e2-e5f8-4bfd-959d-46fb6c883444-result.json +1 -0
  47. backend/allure-results/0d6fb9a5-b36a-4e35-b985-d89ac351cc09-container.json +1 -0
  48. backend/allure-results/0da168e7-8953-4657-9458-dd5e186abe96-result.json +1 -0
  49. backend/allure-results/0dc24df0-7b61-44bb-af09-0f71a0d88f53-container.json +1 -0
  50. 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: Genomatics React Modularised V2
3
- emoji: 📚
4
- colorFrom: indigo
5
- colorTo: gray
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
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
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
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
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}