From f2ef9a64df34128e1e85410d7aada75e16aeb1a1 Mon Sep 17 00:00:00 2001 From: Saikumar Date: Thu, 17 Oct 2024 21:50:53 +0530 Subject: [PATCH] test(utils): update getConfig mocks for EmailService and SlackService sendAlert tests --- tests/utils/main.test.js | 617 +++++++++++++++++++++++++-------------- 1 file changed, 399 insertions(+), 218 deletions(-) diff --git a/tests/utils/main.test.js b/tests/utils/main.test.js index 343edfb..c01e02a 100644 --- a/tests/utils/main.test.js +++ b/tests/utils/main.test.js @@ -3,12 +3,30 @@ const axios = require('axios'); const nodemailer = require('nodemailer'); const { getStorageConnection } = require('../../lib/main/server/storageConnection'); /* globals expect, jest, beforeEach, describe, it */ + jest.mock('axios'); jest.mock('nodemailer'); jest.mock('../../lib/main/server/storageConnection'); describe('SlackService.sendAlert', () => { let mockStorageConnection; + const mockConfig = { + item: { + value: JSON.stringify({ + url: 'https://hooks.slack.com/services/test', + username: 'Errsole', + icon_url: 'https://avatars.githubusercontent.com/u/84983840', + status: true + }) + } + }; + const mockAlertUrlData = { + item: { + value: JSON.stringify({ + url: 'http://example.com' + }) + } + }; beforeEach(() => { mockStorageConnection = { @@ -18,30 +36,20 @@ describe('SlackService.sendAlert', () => { jest.clearAllMocks(); jest.spyOn(console, 'error').mockImplementation(() => {}); // Mock console.error jest.spyOn(console, 'log').mockImplementation(() => {}); // Mock console.log - }); - it('should include server name in the Slack payload when provided in messageExtraInfo', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - url: 'https://hooks.slack.com/services/test', - username: 'Errsole', - icon_url: 'https://avatars.githubusercontent.com/u/84983840', - status: true - }) + // Updated mock implementation based on key + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'slackIntegration') { + return Promise.resolve(mockConfig); } - }; - const mockAlertUrlData = { - item: { - value: JSON.stringify({ - url: 'http://example.com' - }) + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); } - }; - mockStorageConnection.getConfig - .mockResolvedValueOnce(mockConfig) // Slack config - .mockResolvedValueOnce(mockAlertUrlData); // Alert URL config + return Promise.resolve(null); + }); + }); + it('should include server name in the Slack payload when provided in messageExtraInfo', async () => { const mockServerName = 'TestServer'; const messageExtraInfo = { appName: 'TestApp', @@ -75,27 +83,6 @@ describe('SlackService.sendAlert', () => { }); it('should not include server name in the Slack payload if not provided', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - url: 'https://hooks.slack.com/services/test', - username: 'Errsole', - icon_url: 'https://avatars.githubusercontent.com/u/84983840', - status: true - }) - } - }; - const mockAlertUrlData = { - item: { - value: JSON.stringify({ - url: 'http://example.com' - }) - } - }; - mockStorageConnection.getConfig - .mockResolvedValueOnce(mockConfig) // Slack config - .mockResolvedValueOnce(mockAlertUrlData); // Alert URL config - const messageExtraInfo = { appName: 'TestApp', environmentName: 'TestEnv' @@ -126,18 +113,6 @@ describe('SlackService.sendAlert', () => { }); it('should successfully send a Slack alert', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - url: 'https://hooks.slack.com/services/test', - username: 'Errsole', - icon_url: 'https://avatars.githubusercontent.com/u/84983840', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - axios.post.mockResolvedValue({}); const result = await SlackService.sendAlert('Test message', 'Test type', { appName: 'TestApp', environmentName: 'TestEnv' }); @@ -150,22 +125,38 @@ describe('SlackService.sendAlert', () => { }); it('should handle Slack integration disabled', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - status: false - }) + // Update mock to disable Slack integration + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'slackIntegration') { + return Promise.resolve({ + item: { + value: JSON.stringify({ + status: false + }) + } + }); } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); + } + return Promise.resolve(null); + }); const result = await SlackService.sendAlert('Test message', 'Test type', {}); expect(result).toBe(false); // Expecting result to be false when Slack integration is disabled. - // Removed the console.log expectation since it doesn't exist in the actual function code. }); it('should handle missing Slack configuration', async () => { - mockStorageConnection.getConfig.mockResolvedValue(null); + // Update mock to return null for slackIntegration + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'slackIntegration') { + return Promise.resolve(null); + } + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); + } + return Promise.resolve(null); + }); const result = await SlackService.sendAlert('Test message', 'Test type', {}); expect(result).toBe(false); @@ -174,18 +165,6 @@ describe('SlackService.sendAlert', () => { it('should handle Slack send timeout', async () => { jest.setTimeout(15000); // Increase the timeout for this test case - const mockConfig = { - item: { - value: JSON.stringify({ - url: 'https://hooks.slack.com/services/test', - username: 'Errsole', - icon_url: 'https://avatars.githubusercontent.com/u/84983840', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - axios.post.mockImplementation(() => new Promise((resolve, reject) => setTimeout(() => reject(new Error('Slack send timed out')), 2000))); // Shorter delay const result = await SlackService.sendAlert('Test message', 'Test type', {}); @@ -193,18 +172,6 @@ describe('SlackService.sendAlert', () => { }); it('should handle Slack send rejection', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - url: 'https://hooks.slack.com/services/test', - username: 'Errsole', - icon_url: 'https://avatars.githubusercontent.com/u/84983840', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - axios.post.mockRejectedValue(new Error('Send failed')); const result = await SlackService.sendAlert('Test message', 'Test type', {}); @@ -222,23 +189,21 @@ describe('SlackService.sendAlert', () => { }); it('should handle no config found', async () => { - const mockConfig = { - item: null - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'slackIntegration') { + return Promise.resolve({ item: null }); + } + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); + } + return Promise.resolve(null); + }); const result = await SlackService.sendAlert('Test message', 'Test type', {}); expect(result).toBe(false); }); it('should correctly parse and construct alert URL with errsoleLogId and timestamp', async () => { - const mockAlertUrlData = { - item: { - value: JSON.stringify({ - url: 'http://example.com' - }) - } - }; const mockErrsoleLogId = '12345'; const parsedAlertUrlValue = JSON.parse(mockAlertUrlData.item.value); const timestamp = new Date(new Date().getTime() + 2000).toISOString(); @@ -248,21 +213,20 @@ describe('SlackService.sendAlert', () => { }); it('should handle invalid JSON in alertUrlData gracefully', () => { - const mockAlertUrlData = { item: { value: 'invalid_json' } }; + const mockAlertUrlDataInvalid = { item: { value: 'invalid_json' } }; + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlDataInvalid); + } + return Promise.resolve(null); + }); expect(() => { - JSON.parse(mockAlertUrlData.item.value); + JSON.parse(mockAlertUrlDataInvalid.item.value); }).toThrow(); }); - it('should handle missing errsoleLogId and still construct the URL', () => { - const mockAlertUrlData = { - item: { - value: JSON.stringify({ - url: 'http://example.com' - }) - } - }; + it('should handle missing errsoleLogId and still construct the URL', async () => { const mockErrsoleLogId = undefined; const parsedAlertUrlValue = JSON.parse(mockAlertUrlData.item.value); const timestamp = new Date(new Date().getTime() + 2000).toISOString(); @@ -272,24 +236,195 @@ describe('SlackService.sendAlert', () => { }); it('should return an empty string if URL is missing in alertUrlData', () => { - const mockAlertUrlData = { + const mockAlertUrlDataEmpty = { item: { value: JSON.stringify({ url: '' }) } }; + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlDataEmpty); + } + return Promise.resolve(null); + }); + const mockErrsoleLogId = '12345'; - const parsedAlertUrlValue = JSON.parse(mockAlertUrlData.item.value); + const parsedAlertUrlValue = JSON.parse(mockAlertUrlDataEmpty.item.value); const timestamp = new Date(new Date().getTime() + 2000).toISOString(); const alertUrl = parsedAlertUrlValue.url + '#/logs?errsole_log_id=' + mockErrsoleLogId + '×tamp=' + timestamp; expect(alertUrl).toEqual(`#/logs?errsole_log_id=${mockErrsoleLogId}×tamp=${timestamp}`); }); + + it('should include todayCount message for Alert type in the Slack payload', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + const todayCount = 3; // Example count + + axios.post.mockResolvedValue({}); + + await SlackService.sendAlert('Test message', 'Alert', messageExtraInfo, '12345', todayCount); + + expect(axios.post).toHaveBeenCalledWith( + 'https://hooks.slack.com/services/test', + expect.objectContaining({ + blocks: expect.arrayContaining([ + expect.objectContaining({ + type: 'section', + text: { + type: 'mrkdwn', + text: `This alert has occurred *${todayCount} times today*.` + } + }) + ]) + }) + ); + }); + + it('should include todayCount message for Uncaught Exception type in the Slack payload', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + const todayCount = 5; // Example count + + axios.post.mockResolvedValue({}); + + await SlackService.sendAlert('Test message', 'Uncaught Exception', messageExtraInfo, '12345', todayCount); + + expect(axios.post).toHaveBeenCalledWith( + 'https://hooks.slack.com/services/test', + expect.objectContaining({ + blocks: expect.arrayContaining([ + expect.objectContaining({ + type: 'section', + text: { + type: 'mrkdwn', + text: `This error has occurred *${todayCount} times today*.` + } + }) + ]) + }) + ); + }); + + it('should not include todayCount message in the Slack payload when todayCount is not provided', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + + axios.post.mockResolvedValue({}); + + await SlackService.sendAlert('Test message', 'Alert', messageExtraInfo, '12345'); + + expect(axios.post).toHaveBeenCalledWith( + 'https://hooks.slack.com/services/test', + expect.objectContaining({ + blocks: expect.not.arrayContaining([ + expect.objectContaining({ + type: 'section', + text: { + type: 'mrkdwn', + text: expect.stringContaining('This alert has occurred') + } + }) + ]) + }) + ); + }); + + it('should correctly handle singular form for todayCount in the Slack payload', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + const todayCount = 1; // Singular count + + axios.post.mockResolvedValue({}); + + // Test with type 'Alert' + const alertResult = await SlackService.sendAlert('Test message', 'Alert', messageExtraInfo, '12345', todayCount); + + expect(axios.post).toHaveBeenCalledWith( + 'https://hooks.slack.com/services/test', + expect.objectContaining({ + blocks: expect.arrayContaining([ + expect.objectContaining({ + type: 'section', + text: { + type: 'mrkdwn', + text: `This alert has occurred *${todayCount} time today*.` + } + }) + ]) + }) + ); + + expect(alertResult).toBe(true); // Optionally verify the return value + + // Reset mocks for the next assertion + jest.clearAllMocks(); + + // Re-apply the mock implementation in beforeEach + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'slackIntegration') { + return Promise.resolve(mockConfig); + } + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); + } + return Promise.resolve(null); + }); + + // Test with type 'Uncaught Exception' + const exceptionResult = await SlackService.sendAlert('Test message', 'Uncaught Exception', messageExtraInfo, '12345', todayCount); + + expect(axios.post).toHaveBeenCalledWith( + 'https://hooks.slack.com/services/test', + expect.objectContaining({ + blocks: expect.arrayContaining([ + expect.objectContaining({ + type: 'section', + text: { + type: 'mrkdwn', + text: `This error has occurred *${todayCount} time today*.` + } + }) + ]) + }) + ); + + expect(exceptionResult).toBe(true); // Optionally verify the return value + }); }); -describe('EmailService.sendAlert2', () => { +describe('EmailService.sendAlert', () => { let mockStorageConnection; + const mockConfig = { + item: { + value: JSON.stringify({ + host: 'smtp.example.com', + port: '587', + username: 'user@example.com', + password: 'password', + sender: 'sender@example.com', + receivers: 'receiver@example.com', + status: true + }) + } + }; + const mockAlertUrlData = { + item: { + value: JSON.stringify({ + url: 'http://example.com' + }) + } + }; beforeEach(() => { mockStorageConnection = { @@ -300,24 +435,20 @@ describe('EmailService.sendAlert2', () => { jest.clearAllMocks(); jest.spyOn(console, 'error').mockImplementation(() => {}); // Mock console.error jest.spyOn(console, 'log').mockImplementation(() => {}); // Mock console.log - }); - it('should successfully send an email alert', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - host: 'smtp.example.com', - port: '587', - username: 'user@example.com', - password: 'password', - sender: 'sender@example.com', - receivers: 'receiver@example.com', - status: true - }) + // Updated mock implementation based on key + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'emailIntegration') { + return Promise.resolve(mockConfig); } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); + } + return Promise.resolve(null); + }); + }); + it('should successfully send an email alert', async () => { const mockTransporter = { sendMail: jest.fn().mockResolvedValue({}) }; @@ -338,8 +469,11 @@ describe('EmailService.sendAlert2', () => { }); it('should log error and return false when email transporter initialization fails', async () => { - mockStorageConnection.getConfig.mockImplementation(() => { - throw new Error('Unexpected error'); + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'emailIntegration') { + throw new Error('Unexpected error'); + } + return Promise.resolve(null); }); const result = await EmailService.sendAlert('Test message', 'Test type', {}); @@ -347,51 +481,41 @@ describe('EmailService.sendAlert2', () => { expect(result).toBe(false); }); - it('should handle no transporter available', async () => { - EmailService.transporter = null; - - const result = await EmailService.sendAlert('Test message', 'Test type', {}); - expect(result).toBe(false); - }); - it('should handle missing email configuration', async () => { - mockStorageConnection.getConfig.mockResolvedValue(null); + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'emailIntegration') { + return Promise.resolve(null); + } + return Promise.resolve(null); + }); const result = await EmailService.sendAlert('Test message', 'Test type', {}); expect(result).toBe(false); }); it('should handle email integration disabled', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - status: false - }) + // Update mock to disable email integration + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'emailIntegration') { + return Promise.resolve({ + item: { + value: JSON.stringify({ + status: false + }) + } + }); } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); + } + return Promise.resolve(null); + }); const result = await EmailService.sendAlert('Test message', 'Test type', {}); expect(result).toBe(false); // Expecting result to be false when email integration is disabled. - // No need to check console.log as it's not used in the function }); it('should construct email with appName and environmentName', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - host: 'smtp.example.com', - port: '587', - username: 'user@example.com', - password: 'password', - sender: 'sender@example.com', - receivers: 'receiver@example.com', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - const mockTransporter = { sendMail: jest.fn().mockResolvedValue({}) }; @@ -411,21 +535,6 @@ describe('EmailService.sendAlert2', () => { }); it('should construct email with only environmentName', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - host: 'smtp.example.com', - port: '587', - username: 'user@example.com', - password: 'password', - sender: 'sender@example.com', - receivers: 'receiver@example.com', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - const mockTransporter = { sendMail: jest.fn().mockResolvedValue({}) }; @@ -445,21 +554,6 @@ describe('EmailService.sendAlert2', () => { }); it('should construct email without appName, environmentName, or serverName', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - host: 'smtp.example.com', - port: '587', - username: 'user@example.com', - password: 'password', - sender: 'sender@example.com', - receivers: 'receiver@example.com', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - const mockTransporter = { sendMail: jest.fn().mockResolvedValue({}) }; @@ -481,21 +575,6 @@ describe('EmailService.sendAlert2', () => { it('should handle email send timeout', async () => { jest.setTimeout(15000); // Increase the timeout for this test case - const mockConfig = { - item: { - value: JSON.stringify({ - host: 'smtp.example.com', - port: '587', - username: 'user@example.com', - password: 'password', - sender: 'sender@example.com', - receivers: 'receiver@example.com', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - const mockTransporter = { sendMail: jest.fn(() => new Promise((resolve, reject) => setTimeout(() => reject(new Error('Email send timed out')), 2000))) // Shorter delay }; @@ -506,21 +585,6 @@ describe('EmailService.sendAlert2', () => { }); it('should send email successfully before timeout', async () => { - const mockConfig = { - item: { - value: JSON.stringify({ - host: 'smtp.example.com', - port: '587', - username: 'user@example.com', - password: 'password', - sender: 'sender@example.com', - receivers: 'receiver@example.com', - status: true - }) - } - }; - mockStorageConnection.getConfig.mockResolvedValue(mockConfig); - const mockTransporter = { sendMail: jest.fn(() => new Promise((resolve) => { // Simulate email sending that finishes before the timeout @@ -535,4 +599,121 @@ describe('EmailService.sendAlert2', () => { expect(result).toBe(true); expect(console.log).not.toHaveBeenCalledWith(expect.any(Error)); }); + + it('should include todayCount message for Alert type in the email content', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + const todayCount = 3; // Example count + + const mockTransporter = { + sendMail: jest.fn().mockResolvedValue({}) + }; + nodemailer.createTransport.mockReturnValue(mockTransporter); + + const result = await EmailService.sendAlert('Test message', 'Alert', messageExtraInfo, '12345', todayCount); + + expect(mockTransporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + from: 'sender@example.com', + to: 'receiver@example.com', + subject: 'Errsole: Alert (TestApp app, TestEnv environment)', + html: expect.stringContaining(`This alert has occurred ${todayCount} times today.`) + })); + expect(result).toBe(true); // Verify successful sending + }); + + it('should include todayCount message for Uncaught Exception type in the email content', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + const todayCount = 5; // Example count + + const mockTransporter = { + sendMail: jest.fn().mockResolvedValue({}) + }; + nodemailer.createTransport.mockReturnValue(mockTransporter); + + const result = await EmailService.sendAlert('Test message', 'Uncaught Exception', messageExtraInfo, '12345', todayCount); + + expect(mockTransporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + from: 'sender@example.com', + to: 'receiver@example.com', + subject: 'Errsole: Uncaught Exception (TestApp app, TestEnv environment)', + html: expect.stringContaining(`This error has occurred ${todayCount} times today.`) + })); + expect(result).toBe(true); // Verify successful sending + }); + + it('should correctly handle singular form for todayCount in the email content', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + const todayCount = 1; // Singular count + + const mockTransporter = { + sendMail: jest.fn().mockResolvedValue({}) + }; + nodemailer.createTransport.mockReturnValue(mockTransporter); + + // Test with type 'Alert' + const alertResult = await EmailService.sendAlert('Test message', 'Alert', messageExtraInfo, '12345', todayCount); + + expect(mockTransporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + from: 'sender@example.com', + to: 'receiver@example.com', + subject: 'Errsole: Alert (TestApp app, TestEnv environment)', + html: expect.stringContaining(`This alert has occurred ${todayCount} time today.`) + })); + expect(alertResult).toBe(true); // Verify successful sending + + // Reset mocks for the next assertion + jest.clearAllMocks(); + + // Re-apply the mock implementation in beforeEach + mockStorageConnection.getConfig.mockImplementation((key) => { + if (key === 'emailIntegration') { + return Promise.resolve(mockConfig); + } + if (key === 'alertUrl') { + return Promise.resolve(mockAlertUrlData); + } + return Promise.resolve(null); + }); + + // Test with type 'Uncaught Exception' + const exceptionResult = await EmailService.sendAlert('Test message', 'Uncaught Exception', messageExtraInfo, '12345', todayCount); + + expect(mockTransporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + from: 'sender@example.com', + to: 'receiver@example.com', + subject: 'Errsole: Uncaught Exception (TestApp app, TestEnv environment)', + html: expect.stringContaining(`This error has occurred ${todayCount} time today.`) + })); + expect(exceptionResult).toBe(true); // Verify successful sending + }); + + it('should not include todayCount message in the email content when todayCount is not provided', async () => { + const messageExtraInfo = { + appName: 'TestApp', + environmentName: 'TestEnv' + }; + + const mockTransporter = { + sendMail: jest.fn().mockResolvedValue({}) + }; + nodemailer.createTransport.mockReturnValue(mockTransporter); + + const result = await EmailService.sendAlert('Test message', 'Alert', messageExtraInfo, '12345'); + + expect(mockTransporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + from: 'sender@example.com', + to: 'receiver@example.com', + subject: 'Errsole: Alert (TestApp app, TestEnv environment)', + html: expect.not.stringContaining('This alert has occurred') + })); + expect(result).toBe(true); // Verify successful sending + }); });