Skip to content

Commit

Permalink
service testing
Browse files Browse the repository at this point in the history
  • Loading branch information
jkarenzi authored and ambroisegithub committed May 30, 2024
1 parent 726d8e1 commit 669dcbf
Show file tree
Hide file tree
Showing 19 changed files with 1,118 additions and 39 deletions.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,16 @@
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/src/emails/"
"/src/emails/",
"/src/utilis/"

],
"testPathIgnorePatterns": [
"/node_modules/",
"/src/emails/",
"/src/middlewares/"
"/src/middlewares/",
"/src/utilis/"

]
},
"devDependencies": {
Expand Down
42 changes: 42 additions & 0 deletions src/__test__/chatbot.test.ts
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();
});
});
});
185 changes: 185 additions & 0 deletions src/__test__/chatbotService.test.ts
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' },
]);
});
});
3 changes: 2 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import cookieSession from 'cookie-session';
import passport from 'passport';
import userRouter from './routes/userRoutes';
import searchRoutes from './routes/searchRoutes'
import chatRoutes from './routes/chatbotRoutes'
// Require Passport midleware
import './middlewares/passport-setup';

Expand Down Expand Up @@ -80,7 +81,7 @@ app.get('/', (req: Request, res: Response) => {
app.use('/api/v1', router);
app.use('/api/v1', userRouter);
app.use('/api/v1', searchRoutes);

app.use('/api/v1', chatRoutes);
// Endpoints for serving social login
app.use('/auth', authRoutes);

Expand Down
25 changes: 25 additions & 0 deletions src/controller/chatbotController.ts
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 });
}
);
29 changes: 14 additions & 15 deletions src/controller/productController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import { check, validationResult } from 'express-validator';
import errorHandler from '../middlewares/errorHandler';
import productQuantityWatch from '../middlewares/productAvailabilityWatch';




const userRepository = dbConnection.getRepository(UserModel);
const productRepository = dbConnection.getRepository(Product);

Expand Down Expand Up @@ -70,7 +67,7 @@ export const createProduct = [
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}

const vendorId = req.user!.id;

const {
Expand Down Expand Up @@ -373,17 +370,21 @@ export const AvailableProducts = errorHandler(
message: 'Items retrieved successfully.',
availableProducts,
totalPages: Math.ceil(totalCount / limit),
currentPage: page
});
})
currentPage: page,
});
}
);

// From Bernard #38

export const updateProductAvailability = async (req: Request, res: Response) => {
export const updateProductAvailability = async (
req: Request,
res: Response
) => {
const { productId } = req.params;
const { availability } = req.body;
const user = await userRepository.findOne({
where: { id: (req.user as User).id},
where: { id: (req.user as User).id },
});

if (!user) {
Expand All @@ -392,7 +393,7 @@ export const updateProductAvailability = async (req: Request, res: Response) =>

const product = await productRepository.findOne({
where: { id: Number(productId) },
relations: ['vendor']
relations: ['vendor'],
});

if (!product) {
Expand All @@ -409,18 +410,16 @@ export const updateProductAvailability = async (req: Request, res: Response) =>
res.json({ msg: 'Product availability updated' });
};


export const checkProductAvailability = async (req: Request, res: Response) => {
const { productId } = req.params;

const product = await productRepository.findOne({
where: { id: Number(productId) },
relations: ['vendor']
relations: ['vendor'],
});


const user = await userRepository.findOne({
where: { id: (req.user as User).id},
where: { id: (req.user as User).id },
});

if (!user) {
Expand All @@ -430,7 +429,7 @@ export const checkProductAvailability = async (req: Request, res: Response) => {
if (!product) {
return res.status(404).json({ msg: 'Product not found' });
}
if (product.vendor.id!== user.id) {
if (product.vendor.id !== user.id) {
return res.status(403).json({ msg: 'Product not owned by vendor' });
}

Expand Down
18 changes: 18 additions & 0 deletions src/controller/serviceController.ts
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 });
}
);
Loading

0 comments on commit 669dcbf

Please sign in to comment.