-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
726d8e1
commit 669dcbf
Showing
19 changed files
with
1,118 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import request from 'supertest'; | ||
import app from '../app'; | ||
import { afterAllHook, beforeAllHook, getBuyerToken } from './testSetup'; | ||
|
||
beforeAll(beforeAllHook); | ||
afterAll(afterAllHook); | ||
let buyerToken: string; | ||
|
||
describe('Chatbot Interactions', () => { | ||
beforeAll(async () => { | ||
buyerToken = await getBuyerToken(); | ||
}); | ||
|
||
describe('Ask Question Endpoint', () => { | ||
it('should respond to a valid question with the correct answer for logged-in users', async () => { | ||
const chatData = { | ||
message: 'What do you sell?', | ||
}; | ||
|
||
const response = await request(app) | ||
.post('/api/v1/chat') | ||
.set('Authorization', `Bearer ${buyerToken}`) | ||
.send(chatData); | ||
|
||
expect(response.statusCode).toEqual(200); | ||
expect(response.body.message).toContain( | ||
'We sell the following products:' | ||
); | ||
}); | ||
}); | ||
|
||
describe('Fetch All Chat History', () => { | ||
it('should return the full chat history for the authenticated user', async () => { | ||
const response = await request(app) | ||
.get('/api/v1/chat/history') | ||
.set('Authorization', `Bearer ${buyerToken}`); | ||
|
||
expect(response.statusCode).toEqual(200); | ||
expect(Array.isArray(response.body.history)).toBeTruthy(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import { | ||
getProducts, | ||
getProductByName, | ||
getOrders, | ||
getOrderByUserId, | ||
getOrderStatusByTrackingNumber, | ||
getServices, | ||
getServiceByName, | ||
getChatHistory, | ||
} from '../service/chatbotService'; | ||
import dbConnection from '../database'; | ||
import { analyzeMessage, generateResponse } from '../utilis/nlp'; | ||
import User from '../database/models/userModel'; | ||
import Chat from '../database/models/chatbotModel'; | ||
import { processMessage } from '../service/chatbotService'; | ||
jest.mock('../database'); | ||
jest.mock('../utilis/nlp'); | ||
describe('Service Service', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe('getProducts', () => { | ||
it('should process the message and return a complete response', async () => { | ||
const userId = 1; | ||
const user = { id: userId, firstName: 'John', lastName: 'Doe' } as User; | ||
|
||
const userRepoMock = { | ||
findOne: jest.fn().mockResolvedValue(user), | ||
}; | ||
const chatRepoMock = { | ||
save: jest.fn().mockImplementation(async (chat: Chat) => chat), | ||
}; | ||
(dbConnection.getRepository as jest.Mock).mockImplementation((entity) => { | ||
if (entity === User) return userRepoMock; | ||
if (entity === Chat) return chatRepoMock; | ||
}); | ||
|
||
(analyzeMessage as jest.Mock).mockReturnValue('analyzed message'); | ||
(generateResponse as jest.Mock).mockResolvedValue('response'); | ||
|
||
const currentHour = new Date().getHours(); | ||
let expectedGreeting = ''; | ||
if (currentHour < 12) { | ||
expectedGreeting = 'Good morning'; | ||
} else if (currentHour < 18) { | ||
expectedGreeting = 'Good afternoon'; | ||
} else { | ||
expectedGreeting = 'Good evening'; | ||
} | ||
const expectedCompleteResponse = `${expectedGreeting}, John Doe! response`; | ||
|
||
const result = await processMessage(userId, 'test message'); | ||
|
||
expect(userRepoMock.findOne).toHaveBeenCalledWith({ | ||
where: { id: userId }, | ||
}); | ||
expect(analyzeMessage).toHaveBeenCalledWith('test message'); | ||
expect(generateResponse).toHaveBeenCalledWith('analyzed message', userId); | ||
expect(chatRepoMock.save).toHaveBeenCalledWith(expect.any(Chat)); | ||
expect(result.trim()).toEqual(expectedCompleteResponse.trim()); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('processMessage', () => { | ||
it('should throw an error if user is not found', async () => { | ||
const userId = 1; | ||
const userRepoMock = { | ||
findOne: jest.fn().mockResolvedValue(null), | ||
}; | ||
(dbConnection.getRepository as jest.Mock).mockReturnValue(userRepoMock); | ||
|
||
await expect(processMessage(userId, 'test message')).rejects.toThrow( | ||
'User not found' | ||
); | ||
|
||
expect(userRepoMock.findOne).toHaveBeenCalledWith({ | ||
where: { id: userId }, | ||
}); | ||
}); | ||
}); | ||
|
||
it('should return a list of products', async () => { | ||
const productRepo = { | ||
find: jest.fn().mockResolvedValue([{ name: 'Product1' }]), | ||
}; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(productRepo); | ||
|
||
const products = await getProducts(); | ||
expect(products).toEqual([{ name: 'Product1' }]); | ||
}); | ||
|
||
describe('getProductByName', () => { | ||
it('should return a product by name', async () => { | ||
const productRepo = { | ||
findOne: jest.fn().mockResolvedValue({ name: 'Product1' }), | ||
}; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(productRepo); | ||
|
||
const product = await getProductByName('Product1'); | ||
expect(product).toEqual({ name: 'Product1' }); | ||
}); | ||
}); | ||
|
||
describe('getOrders', () => { | ||
it('should return a list of orders for a user', async () => { | ||
const orderRepo = { find: jest.fn().mockResolvedValue([{ id: 1 }]) }; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(orderRepo); | ||
|
||
const orders = await getOrders(1); | ||
expect(orders).toEqual([{ id: 1 }]); | ||
}); | ||
}); | ||
|
||
describe('getOrderByUserId', () => { | ||
it('should return an order for a user by userId', async () => { | ||
const orderRepo = { findOne: jest.fn().mockResolvedValue({ id: 1 }) }; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(orderRepo); | ||
|
||
const order = await getOrderByUserId(1); | ||
expect(order).toEqual({ id: 1 }); | ||
}); | ||
}); | ||
|
||
describe('getOrderStatusByTrackingNumber', () => { | ||
it('should return the status of an order by tracking number', async () => { | ||
const orderRepo = { | ||
findOne: jest.fn().mockResolvedValue({ status: 'Shipped' }), | ||
}; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(orderRepo); | ||
|
||
const status = await getOrderStatusByTrackingNumber('12345'); | ||
expect(status).toBe('Shipped'); | ||
}); | ||
|
||
it('should return "Tracking number not found" if order is not found', async () => { | ||
const orderRepo = { findOne: jest.fn().mockResolvedValue(null) }; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(orderRepo); | ||
|
||
const status = await getOrderStatusByTrackingNumber('12345'); | ||
expect(status).toBe('Tracking number not found'); | ||
}); | ||
}); | ||
|
||
describe('getServices', () => { | ||
it('should return a list of services', async () => { | ||
const serviceRepo = { | ||
find: jest.fn().mockResolvedValue([{ name: 'Service1' }]), | ||
}; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(serviceRepo); | ||
|
||
const services = await getServices(); | ||
expect(services).toEqual([{ name: 'Service1' }]); | ||
}); | ||
}); | ||
|
||
describe('getServiceByName', () => { | ||
it('should return a service by name', async () => { | ||
const serviceRepo = { | ||
findOne: jest.fn().mockResolvedValue({ name: 'Service1' }), | ||
}; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(serviceRepo); | ||
|
||
const service = await getServiceByName('Service1'); | ||
expect(service).toEqual({ name: 'Service1' }); | ||
}); | ||
}); | ||
|
||
describe('getChatHistory', () => { | ||
it('should return chat history for a user', async () => { | ||
const userId = 1; | ||
const chatRepo = { | ||
find: jest | ||
.fn() | ||
.mockResolvedValue([{ message: 'Hello', createdAt: '2024-05-31' }]), | ||
}; | ||
dbConnection.getRepository = jest.fn().mockReturnValue(chatRepo); | ||
|
||
const chatHistory = await getChatHistory(userId); | ||
expect(chatHistory).toEqual([ | ||
{ message: 'Hello', createdAt: '2024-05-31' }, | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Request, Response } from 'express'; | ||
import * as chatbotService from '../service/chatbotService'; | ||
import errorHandler from '../middlewares/errorHandler'; | ||
|
||
export const getChatResponse = errorHandler( | ||
async ( | ||
req: Request, | ||
res: Response | ||
): Promise<Response<Record<string, unknown>> | undefined> => { | ||
const { message } = req.body; | ||
const userId = req.user?.id; | ||
const response = await chatbotService.processMessage(userId, message); | ||
return res.status(200).json({ message: response }); | ||
} | ||
); | ||
export const getChatHistory = errorHandler( | ||
async ( | ||
req: Request, | ||
res: Response | ||
): Promise<Response<Record<string, unknown>> | undefined> => { | ||
const userId = req.user?.id; | ||
const history = await chatbotService.getChatHistory(userId); | ||
return res.status(200).json({ history }); | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Request, Response } from 'express'; | ||
import * as serviceService from '../service/serviceService'; | ||
import errorHandler from '../middlewares/errorHandler'; | ||
|
||
export const createService = errorHandler( | ||
async (req: Request, res: Response): Promise<Response<Record<string, unknown>> | undefined> => { | ||
const { name, description } = req.body; | ||
const newService = await serviceService.createService({ name, description }); | ||
return res.status(201).json({ message: 'Service created successfully', service: newService }); | ||
} | ||
); | ||
|
||
export const getAllServices = errorHandler( | ||
async (_req: Request, res: Response): Promise<Response<Record<string, unknown>> | undefined> => { | ||
const services = await serviceService.getAllServices(); | ||
return res.status(200).json({ services }); | ||
} | ||
); |
Oops, something went wrong.