diff --git a/src/controllers/ratingController.js b/src/controllers/ratingController.js new file mode 100644 index 0000000..5932496 --- /dev/null +++ b/src/controllers/ratingController.js @@ -0,0 +1,22 @@ +import { postAccommodationRating, getAccommodationRatings } from '../services/ratingServices'; +import { respondWithSuccess, respondWithWarning } from '../helpers/responseHandler'; +import statusCode from '../helpers/statusCode'; + +export const createAccommodationRating = async (req, res) => { + const payload = { accommodationId: req.params.accommodationId, rating: req.body.rating }; + try { + const rating = await postAccommodationRating(payload); + return respondWithSuccess(res, statusCode.created, 'Accommodation has been rated successfuly', rating.toJSON()); + } catch (error) { + return respondWithWarning(res, statusCode.internalServerError, 'Oops, something bad happened'); + } +}; + +export const getSingleAccommodationRatings = async (req, res) => { + try { + const accommodationRatings = await getAccommodationRatings(req.params.accommodationId); + return !accommodationRatings.length ? respondWithWarning(res, statusCode.resourceNotFound, 'No ratings for this accommodation') : respondWithSuccess(res, statusCode.success, 'Accommodation ratings has been retrieved', accommodationRatings); + } catch (error) { + return respondWithWarning(res, statusCode.internalServerError, 'Oops, something bad happened'); + } +}; diff --git a/src/controllers/tripController.js b/src/controllers/tripController.js index 2fc28b2..3148dc1 100644 --- a/src/controllers/tripController.js +++ b/src/controllers/tripController.js @@ -1,7 +1,7 @@ import { postTrip, updateTripStatus, getRequesterEmail, bulkCreate, - getTripRequests, findOneTripRequest, rejectRequest, fetchUserTripStats, - fetchTripStats, fetchTripRequests + getTripRequests, findOneTripRequest, rejectRequest, searchTripRequest, + fetchUserTripStats, fetchTripStats, fetchTripRequests } from '../services/tripServices'; import { respondWithSuccess, respondWithWarning } from '../helpers/responseHandler'; import statusCode from '../helpers/statusCode'; @@ -206,3 +206,13 @@ export const retrieveTripRequest = async (req, res) => { return respondWithWarning(res, statusCode.internalServerError, error.message); } }; + +export const searchTripRequests = async (req, res) => { + try { + const tripRequests = await searchTripRequest(req.query.key); + return !tripRequests.length ? respondWithWarning(res, statusCode.resourceNotFound, 'Trip request not found') + : respondWithSuccess(res, statusCode.success, 'Data has been retrieved successfully', tripRequests); + } catch (error) { + return respondWithWarning(res, statusCode.internalServerError, 'Oops something bad happened'); + } +}; diff --git a/src/database/migrations/20190829084157-create-trip-request.js b/src/database/migrations/20190829084157-create-trip-request.js index 1463222..376add0 100644 --- a/src/database/migrations/20190829084157-create-trip-request.js +++ b/src/database/migrations/20190829084157-create-trip-request.js @@ -19,11 +19,11 @@ export default { allowNull: false, }, departureDate: { - type: Sequelize.DATE, + type: Sequelize.STRING, allowNull: false, }, returnDate: { - type: Sequelize.DATE + type: Sequelize.STRING }, type: { type: Sequelize.STRING, diff --git a/src/database/migrations/20190912211238-create-rating.js b/src/database/migrations/20190912211238-create-rating.js new file mode 100644 index 0000000..c48f05c --- /dev/null +++ b/src/database/migrations/20190912211238-create-rating.js @@ -0,0 +1,29 @@ +export default { + up: (queryInterface, Sequelize) => queryInterface.createTable('Ratings', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + accommodationId: { + type: Sequelize.INTEGER, + allowNull: false, + }, + rating: { + type: Sequelize.INTEGER, + allowNull: false, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: new Date(), + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: new Date(), + } + }), + down: (queryInterface) => queryInterface.dropTable('Ratings') +}; diff --git a/src/docs/swagger/definitions/rating.js b/src/docs/swagger/definitions/rating.js new file mode 100644 index 0000000..d4d20bb --- /dev/null +++ b/src/docs/swagger/definitions/rating.js @@ -0,0 +1,10 @@ +export const accommodationRatingSchema = { + type: 'object', + properties: { + rating: { + type: 'integer', + format: 'int32', + example: 1, + } + } +}; diff --git a/src/docs/swagger/paths/rating.js b/src/docs/swagger/paths/rating.js new file mode 100644 index 0000000..6c8039b --- /dev/null +++ b/src/docs/swagger/paths/rating.js @@ -0,0 +1,133 @@ +export const accommodationRating = { + post: { + tags: [ + 'accommodations' + ], + security: [ + { + BearerToken: [] + } + ], + summary: 'Post ratings for a particular accommodation', + description: 'Post ratings for a particular accommodation', + parameters: [ + { + name: 'accommodationId', + in: 'path', + description: 'path parameter takes the accommodation id', + required: true, + type: 'integer', + format: 'int32' + }, + { + name: 'body', + in: 'body', + required: false, + schema: { + $ref: '#definitions/accommodationRatingSchema' + } + } + ], + responses: { + 201: { + description: 'Room successfully created', + schema: { + $ref: '#/definitions/createRoomRes' + } + }, + 400: { + description: 'Bad input data', + schema: { + $ref: '#/definitions/badRequest' + } + }, + 401: { + description: 'Incorrect login details', + schema: { + $ref: '#/definitions/notAuthorized' + } + }, + 403: { + description: 'Forbidden access', + schema: { + $ref: '#/definitions/accessForbidden' + } + }, + 404: { + description: 'Accommodation not found', + schema: { + $ref: '#/definitions/notFound' + } + }, + 500: { + description: 'Server error', + schema: { + $ref: '#/definitions/serverError' + } + } + } + } +}; + +export const getAccommodationRatings = { + get: { + tags: [ + 'accommodations' + ], + security: [ + { + BearerToken: [] + } + ], + summary: 'Post ratings for a particular accommodation', + description: 'Post ratings for a particular accommodation', + parameters: [ + { + name: 'accommodationId', + in: 'path', + description: 'path parameter takes the accommodation id', + required: true, + type: 'integer', + format: 'int32' + } + ], + responses: { + 200: { + description: 'Ratings have been retrieved successfully', + schema: { + $ref: '#/definitions/success' + } + }, + 400: { + description: 'Bad input data', + schema: { + $ref: '#/definitions/badRequest' + } + }, + 401: { + description: 'Incorrect login details', + schema: { + $ref: '#/definitions/notAuthorized' + } + }, + 403: { + description: 'Forbidden access', + schema: { + $ref: '#/definitions/accessForbidden' + } + }, + 404: { + description: 'Accommodation not found', + schema: { + $ref: '#/definitions/notFound' + } + }, + 500: { + description: 'Server error', + schema: { + $ref: '#/definitions/serverError' + } + } + } + } +}; diff --git a/src/docs/swagger/paths/socialAuth.js b/src/docs/swagger/paths/socialAuth.js index 7345e62..9db3a17 100644 --- a/src/docs/swagger/paths/socialAuth.js +++ b/src/docs/swagger/paths/socialAuth.js @@ -9,7 +9,7 @@ const facebookPath = { 200: { description: 'Login with a facebook account', schema: { - $ref: '#/definitions/socialMedia' + $ref: '#/definitions/socialMediaAuthentication' } } @@ -28,7 +28,7 @@ const googlePath = { 200: { description: 'Login with a google account', schema: { - $ref: '#/definitions/socialMedia' + $ref: '#/definitions/socialMediaAuthentication' } } diff --git a/src/docs/swagger/paths/trips.js b/src/docs/swagger/paths/trips.js index 1489a84..e185baa 100644 --- a/src/docs/swagger/paths/trips.js +++ b/src/docs/swagger/paths/trips.js @@ -1,3 +1,4 @@ + export const requestTrip = { post: { tags: [ @@ -54,7 +55,7 @@ export const requestTrip = { } } }, - + }; export const multiCityTripPath = { @@ -422,3 +423,65 @@ export const getTripStatsPath = { } } }; + +export const searchTripRequestPath = { + get: { + tags: [ + 'trips' + ], + security: [ + { + BearerToken: [] + } + ], + summary: 'Search for trips', + description: 'Search for trip requests', + parameters: [ + { + name: 'key', + in: 'query', + description: 'Query parameter takes in the search query', + required: true, + type: 'string' + } + ], + responses: { + 200: { + description: 'Trip resquest has been approved', + schema: { + $ref: '#/definitions/success' + } + }, + 400: { + description: 'Invalid request details', + schema: { + $ref: '#/definitions/badRequest' + } + }, + 401: { + description: 'Unauthorized', + schema: { + $ref: '#/definitions/notAuthorized' + } + }, + 403: { + description: 'Access forbidden', + schema: { + $ref: '#/definitions/accessForbidden' + } + }, + 404: { + description: 'Trip not found', + schema: { + $ref: '#/definitions/notFound' + } + }, + 500: { + description: 'Server error', + schema: { + $ref: '#/definitions/serverError' + } + } + } + } +}; diff --git a/src/docs/swagger/swagger.js b/src/docs/swagger/swagger.js index d050075..b0422aa 100644 --- a/src/docs/swagger/swagger.js +++ b/src/docs/swagger/swagger.js @@ -27,8 +27,7 @@ import { verifyUserPath, fetchTripPath, } from './paths/users'; import { - requestTrip, approvedTripPath, getTripPath, returnTrip, multiCityTripPath, - rejectTripPath, getTripStatsPath + requestTrip, approvedTripPath, getTripPath, returnTrip, multiCityTripPath, rejectTripPath, getTripStatsPath, searchTripRequestPath } from './paths/trips'; import { createTrip, returnTripSchema, createMultiCityTrip, multiCityTripRes, @@ -56,6 +55,8 @@ import { markAllNotficationPath } from './paths/notification'; import { createChat } from './definitions/chat'; import { flightPath } from './paths/flight'; import { FlightCreate } from './definitions/flight'; +import { accommodationRatingSchema } from './definitions/rating'; +import { accommodationRating, getAccommodationRatings } from './paths/rating'; const swaggerDocument = { swagger: '2.0', @@ -112,7 +113,7 @@ const swaggerDocument = { { name: 'chats', description: 'Chat related endpoints' - }, + }, { name: 'flights', description: 'Flight related endpoints' @@ -142,11 +143,14 @@ const swaggerDocument = { '/trips/{tripId}/reject': rejectTripPath, '/trips/{tripId}/comments': getTripCommentsPath, '/trips/{tripId}/comments/{commentId}': deleteCommentPath, + '/trips/search/?key={key}': searchTripRequestPath, '/trips/return': returnTrip, '/trips/stats': getTripStatsPath, '/accommodations': createAccommodationPath, '/accommodations/rooms/{accommodationId}': createRoomPath, '/accommodations/{accommodationId}': getAccommodationPath, + '/accommodations/{accommodationId}/rating': accommodationRating, + '/accommodations/{accommodationId}/ratings': getAccommodationRatings, '/accommodations/trip/{tripId}': getTripAccommodationsPath, '/accommodations/like/{accommodationId}': accommodationLike, '/accommodations/trips/{tripId}': getTripAccommodationsPath, @@ -196,6 +200,7 @@ const swaggerDocument = { resetUserPassword, updateRolePermissionsReq, updateRolePermissionsRes, + accommodationRatingSchema, rolesRes, createTrip, createComment, diff --git a/src/helpers/validator.js b/src/helpers/validator.js index 271063b..e69ec5e 100644 --- a/src/helpers/validator.js +++ b/src/helpers/validator.js @@ -5,7 +5,7 @@ export default { password: Joi.string().required(), email: Joi.string().email().required().trim(), username: Joi.string().required().trim().min(3), - id: Joi.number().required(), + id: Joi.number().integer().required(), // Profile validations rememberMe: Joi.boolean(), diff --git a/src/index.js b/src/index.js index 11ab27f..8f0b426 100644 --- a/src/index.js +++ b/src/index.js @@ -27,7 +27,7 @@ app.use(logger('dev')); app.use('*', cloudinaryConfig); // handles default route -app.get('/', (req, res) => respondWithSuccess(res, 200, 'Welcome to barefoot Normad')); +app.get('/', async (req, res) => respondWithSuccess(res, 200, 'Welcome to barefoot Normad')); app.use(apiRouter); app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(swaggerDocument)); diff --git a/src/middlewares/accommodationValidation.js b/src/middlewares/accommodationValidation.js index f52e94c..e2bcae8 100644 --- a/src/middlewares/accommodationValidation.js +++ b/src/middlewares/accommodationValidation.js @@ -87,7 +87,7 @@ export const validateAccommodationId = (req, res, next) => { accommodationId: req.params.accommodationId, }; const schema = Joi.object().keys({ - accommodationId: Joi.number().required() + accommodationId: Joi.number().integer().required() }); const errors = joiValidator(data, schema); if (!errors) { @@ -121,3 +121,19 @@ export const verifyAccommodationReview = async (req, res, next) => { } return next(); }; + +export const validateAccommodationRating = (req, res, next) => { + const data = { + accommodationId: req.params.accommodationId, + rating: req.body.rating + }; + const schema = Joi.object().keys({ + accommodationId: Joi.number().integer().required(), + rating: Joi.number().min(1).max(5).required() + }); + const errors = joiValidator(data, schema); + if (!errors) { + return next(); + } + return respondWithWarning(res, 400, 'Bad request', errors); +}; diff --git a/src/middlewares/tripMiddleware.js b/src/middlewares/tripMiddleware.js index 18b1021..d41bcff 100644 --- a/src/middlewares/tripMiddleware.js +++ b/src/middlewares/tripMiddleware.js @@ -4,7 +4,7 @@ import statusCode from '../helpers/statusCode'; import { getUserProfile } from '../services/userServices'; export const verifyTrip = async (req, res, next) => { - const trip = await findTripById(req.params.tripId); + const trip = await findTripById(Number(req.params.tripId)); if (!trip) { return respondWithWarning(res, statusCode.resourceNotFound, 'Trip not found'); diff --git a/src/middlewares/validateTripRequest.js b/src/middlewares/validateTripRequest.js index 3da8bc0..515191e 100644 --- a/src/middlewares/validateTripRequest.js +++ b/src/middlewares/validateTripRequest.js @@ -132,3 +132,15 @@ export const validateTripStatDate = (req, res, next) => { } return respondWithWarning(res, 400, errors); }; + +export const validateSearchQuery = (req, res, next) => { + const searchQuery = Joi.object().keys({ + key: Joi.string().required().trim() + }); + + const errors = joiValidator(req.query, searchQuery); + if (!errors) { + return next(); + } + return respondWithWarning(res, statusCode.badRequest, resMessage.badInputRequest, errors); +}; diff --git a/src/models/rating.js b/src/models/rating.js new file mode 100644 index 0000000..7d34ed0 --- /dev/null +++ b/src/models/rating.js @@ -0,0 +1,21 @@ +export default (sequelize, DataTypes) => { + const Rating = sequelize.define('Rating', { + accommodationId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + rating: { + type: DataTypes.INTEGER, + allowNull: false, + } + }, {}); + Rating.associate = (models) => { + Rating.belongsTo(models.Accommodation, { + foreignKey: 'accommodationId', + as: 'accommodation', + timestamps: false, + onDelete: 'CASCADE' + }); + }; + return Rating; +}; diff --git a/src/models/triprequest.js b/src/models/triprequest.js index bf4f856..9a62763 100644 --- a/src/models/triprequest.js +++ b/src/models/triprequest.js @@ -21,11 +21,11 @@ export default (sequelize, DataTypes) => { allowNull: false, }, departureDate: { - type: DataTypes.DATE, + type: DataTypes.STRING, allowNull: false, }, returnDate: { - type: DataTypes.DATE + type: DataTypes.STRING }, userId: { type: DataTypes.INTEGER, diff --git a/src/routes/api/rating.routes.js b/src/routes/api/rating.routes.js new file mode 100644 index 0000000..a9c9e5f --- /dev/null +++ b/src/routes/api/rating.routes.js @@ -0,0 +1,13 @@ +import { Router } from 'express'; +import { createAccommodationRating, getSingleAccommodationRatings } from '../../controllers/ratingController'; +import { validateAccommodationId, validateAccommodationRating } from '../../middlewares/accommodationValidation'; +import { authenticateUserToken } from '../../middlewares/authentication'; +import { verifyAccommodation } from '../../middlewares/accommodationMiddleware'; + +const router = Router(); + +router.post('/:accommodationId/rating', authenticateUserToken, validateAccommodationRating, verifyAccommodation, createAccommodationRating); + +router.get('/:accommodationId/ratings', authenticateUserToken, validateAccommodationId, verifyAccommodation, getSingleAccommodationRatings); + +export default router; diff --git a/src/routes/api/trip.routes.js b/src/routes/api/trip.routes.js index bffb156..8f724e6 100644 --- a/src/routes/api/trip.routes.js +++ b/src/routes/api/trip.routes.js @@ -1,10 +1,11 @@ import { Router } from 'express'; import { - oneWayTripRequest, approveTripRequest, getTripRequest, returnTripRequest, + oneWayTripRequest, approveTripRequest, getTripRequest, returnTripRequest, searchTripRequests, multiCityTripRequest, getAllTripRequests, rejectTripRequest, getUserTripStats, getTripStats } from '../../controllers/tripController'; import { - validateRequestTripForm, validateTripId, validateReturnTripForm, validateTripStatDate + validateRequestTripForm, validateTripId, validateReturnTripForm, validateTripStatDate, + validateSearchQuery } from '../../middlewares/validateTripRequest'; import { authenticateUserToken } from '../../middlewares/authentication'; @@ -15,15 +16,25 @@ import { import { validateMultipleRequests } from '../../middlewares/validateMultipleRequests'; const trip = Router(); + +trip.get('/search', authenticateUserToken, validateSearchQuery, searchTripRequests); + trip.get('/', authenticateUserToken, checkPermission('VIEW_USERS_TRIP_REQUESTS'), getAllTripRequests); + trip.post('/multicity', authenticateUserToken, checkPermission('CREATE_TRIP_REQUEST'), validateMultipleRequests, verifyTripDestination, multiCityTripRequest); + trip.post('/oneway', authenticateUserToken, checkPermission('CREATE_TRIP_REQUEST'), validateRequestTripForm, verifyTripDestination, oneWayTripRequest); + trip.post('/return', authenticateUserToken, checkPermission('CREATE_TRIP_REQUEST'), validateReturnTripForm, verifyTripDestination, returnTripRequest); + trip.post('/stats', authenticateUserToken, checkPermission('VIEW_TRIP_STATS'), validateTripStatDate, getUserTripStats, getTripStats); + trip.get('/:tripId', authenticateUserToken, checkPermission('VIEW_USERS_TRIP_REQUESTS'), validateTripId, verifyTrip, getTripWithProfile, getTripRequest); trip.patch('/:tripId/approve', authenticateUserToken, checkPermission('APPROVE_TRIP_REQUEST'), validateTripId, verifyTrip, checkTripStatus, approveTripRequest); + trip.get('/', authenticateUserToken, checkPermission('VIEW_USERS_TRIP_REQUESTS'), getAllTripRequests); + trip.patch('/:tripId/reject', authenticateUserToken, checkPermission('APPROVE_TRIP_REQUEST'), validateTripId, verifyTrip, rejectTripRequest); export default trip; diff --git a/src/routes/index.js b/src/routes/index.js index 5dc70a9..173f30b 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -10,6 +10,7 @@ import notification from './api/notification.route'; import comments from './api/comment.routes'; import chats from './api/chats.routes'; import flight from './api/flight.routes'; +import ratings from './api/rating.routes'; const apiRouter = Router(); @@ -21,10 +22,11 @@ apiRouter.use('/api/v1/trips', comments); apiRouter.use('/api/v1/permissions', permissions); apiRouter.use('/api/v1/roles', roles); apiRouter.use('/api/v1/accommodations', accommodation); +apiRouter.use('/api/v1/accommodations', ratings); apiRouter.use('/api/v1/bookings', booking); apiRouter.use('/api/v1', chats); - apiRouter.use('/api/v1/flights', flight); + export default apiRouter; diff --git a/src/services/ratingServices.js b/src/services/ratingServices.js new file mode 100644 index 0000000..ad16248 --- /dev/null +++ b/src/services/ratingServices.js @@ -0,0 +1,27 @@ +import Model from '../models'; + +const { Rating } = Model; + +export const postAccommodationRating = async (payload) => { + try { + const rating = await Rating.create(payload); + return rating; + } catch (error) { + return { + errors: error + }; + } +}; + +export const getAccommodationRatings = async (accommodationId) => { + try { + const tripRatings = await Rating.findAll({ + where: { accommodationId } + }); + return tripRatings; + } catch (error) { + return { + errors: error + }; + } +}; diff --git a/src/services/tripServices.js b/src/services/tripServices.js index 0dc7a90..9782e3a 100644 --- a/src/services/tripServices.js +++ b/src/services/tripServices.js @@ -125,6 +125,7 @@ export const findOneDestination = async (destinationId) => { export const findUserTrip = async (id, userId) => TripRequest.findOne({ where: { id, userId } }); + export const rejectRequest = async (tripId, status) => updateTripStatus(tripId, status); export const fetchUserTripStats = async (date, userId) => TripRequest.findAndCountAll({ @@ -169,3 +170,37 @@ export const fetchTripRequests = async userId => TripRequest.findAll({ } }] }); + +export const searchTripRequest = async (payload) => { + try { + const tripRequests = await TripRequest.findAll({ + where: { + [Model.Sequelize.Op.or]: [ + { origin: payload }, + { departureDate: payload }, + { status: payload }, + { type: payload }, + { '$destination.destination$': payload }, + { '$user.email$': payload } + ] + }, + include: [ + { + model: Destination, + as: 'destination', + attributes: ['destination'], + }, + { + model: User, + as: 'user', + attributes: ['email'], + } + ], + }); + return tripRequests; + } catch (error) { + return { + errors: error + }; + } +}; diff --git a/src/test/rating.test.js b/src/test/rating.test.js new file mode 100644 index 0000000..da6781a --- /dev/null +++ b/src/test/rating.test.js @@ -0,0 +1,196 @@ +import chai from 'chai'; +import chaiHttp from 'chai-http'; +import app from '../index'; + +const { expect } = chai; +chai.use(chaiHttp); + +const ratingUrl = '/api/v1/accommodations'; + +describe('POST ACCOMMODATION RATING', () => { + let request; + let token; + beforeEach(async () => { + request = chai.request(app); + const res = await chai.request(app) + .post('/api/v1/auth/signin') + .send({ + email: 'alexiwobi@nomad.com', + password: '123456', + }); + token = res.body.payload.token; + }); + + it('it should post a rating for an accommodation', async () => { + const res = await request + .post(`${ratingUrl}/1/rating`) + .set('Authorization', token) + .send({ rating: 5 }); + expect(res).to.have.status(201); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(true); + }); + + it('it should return an error for empty rating', async () => { + const res = await request + .post(`${ratingUrl}/1/rating`) + .set('Authorization', token) + .send({ rating: '' }); + expect(res).to.have.status(400); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for empty rating field', async () => { + const res = await request + .post(`${ratingUrl}/1/rating`) + .set('Authorization', token); + expect(res).to.have.status(400); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for empty token', async () => { + const res = await request + .post(`${ratingUrl}/1/rating`) + .send({ rating: 5 }); + expect(res).to.have.status(401); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid token', async () => { + const res = await request + .post(`${ratingUrl}/1/rating`) + .set('Authorization', 'jcbjebcekbcjebcjebkcebk') + .send({ rating: 5 }); + expect(res).to.have.status(401); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid accommodation id', async () => { + const res = await request + .post(`${ratingUrl}/100/rating`) + .set('Authorization', token) + .send({ rating: 5 }); + expect(res).to.have.status(404); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid accommodation id params', async () => { + const res = await request + .post(`${ratingUrl}/'1'/rating`) + .set('Authorization', token) + .send({ rating: 5 }); + expect(res).to.have.status(400); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); +}); + +describe('GET ACCOMMODATION RATINGS', () => { + let request; + let token; + beforeEach(async () => { + request = chai.request(app); + const res = await chai.request(app) + .post('/api/v1/auth/signin') + .send({ + email: 'alexiwobi@nomad.com', + password: '123456', + }); + token = res.body.payload.token; + }); + + it('it should post a rating for an accommodation', async () => { + const res = await request + .get(`${ratingUrl}/1/ratings`) + .set('Authorization', token); + expect(res).to.have.status(200); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(true); + }); + + it('it should return an error for empty token', async () => { + const res = await request + .get(`${ratingUrl}/1/ratings`); + expect(res).to.have.status(401); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid token', async () => { + const res = await request + .get(`${ratingUrl}/1/ratings`) + .set('Authorization', 'jcbjebcekbcjebcjebkcebk'); + expect(res).to.have.status(401); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid accommodation id', async () => { + const res = await request + .get(`${ratingUrl}/100/ratings`) + .set('Authorization', token); + expect(res).to.have.status(404); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid accommodation id params', async () => { + const res = await request + .get(`${ratingUrl}/'1'/ratings`) + .set('Authorization', token); + expect(res).to.have.status(400); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid accommodation id params two', async () => { + const res = await request + .get(`${ratingUrl}/rr/ratings`) + .set('Authorization', token); + expect(res).to.have.status(400); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should return an error for invalid accommodation id params two', async () => { + const res = await request + .get(`${ratingUrl}/2/ratings`) + .set('Authorization', token); + expect(res).to.have.status(404); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); +}); diff --git a/src/test/search.test.js b/src/test/search.test.js new file mode 100644 index 0000000..9dd6e9c --- /dev/null +++ b/src/test/search.test.js @@ -0,0 +1,78 @@ +import chai from 'chai'; +import chaiHttp from 'chai-http'; +import app from '../index'; + +const { expect } = chai; +chai.use(chaiHttp); + +const searchUrl = '/api/v1/trips/search'; + +describe('SEARCH TRIP REQUESTS', () => { + let request; + let token; + beforeEach(async () => { + request = chai.request(app); + const res = await chai.request(app) + .post('/api/v1/auth/signin') + .send({ + email: 'alexiwobi@nomad.com', + password: '123456', + }); + token = res.body.payload.token; + }); + + it('it should retrieve search result for Lagos', async () => { + const res = await request + .get(`${searchUrl}/?key=Lagos`) + .set('Authorization', token); + expect(res).to.have.status(200); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(true); + }); + + it('it should throw an error for empty search key', async () => { + const res = await request + .get(`${searchUrl}/?key=''`) + .set('Authorization', token); + expect(res).to.have.status(404); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should throw an error for integer search key', async () => { + const res = await request + .get(`${searchUrl}/?key=123`) + .set('Authorization', token); + expect(res).to.have.status(404); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should throw an error for empty search key', async () => { + const res = await request + .get(`${searchUrl}/?key=`) + .set('Authorization', token); + expect(res).to.have.status(400); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); + + it('it should throw an error for invalid token', async () => { + const res = await request + .get(`${searchUrl}/?key=Lagos`) + .set('Authorization', 'bjebjcbecbjebchbejcbjw'); + expect(res).to.have.status(401); + expect(res.body).to.have.property('success'); + expect(res.body).to.have.property('message'); + expect(res.body).to.have.property('payload'); + expect(res.body.success).to.equal(false); + }); +});