diff --git a/api/src/app.ts b/api/src/app.ts index 4428dbdc0..0ea5f882a 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -55,11 +55,11 @@ app.use('/', countryRoutes) i18n.locale = env.DEFAULT_LANGUAGE -helper.mkdir(env.CDN_USERS) -helper.mkdir(env.CDN_TEMP_USERS) -helper.mkdir(env.CDN_CARS) -helper.mkdir(env.CDN_TEMP_CARS) -helper.mkdir(env.CDN_LOCATIONS) -helper.mkdir(env.CDN_TEMP_LOCATIONS) +await helper.mkdir(env.CDN_USERS) +await helper.mkdir(env.CDN_TEMP_USERS) +await helper.mkdir(env.CDN_CARS) +await helper.mkdir(env.CDN_TEMP_CARS) +await helper.mkdir(env.CDN_LOCATIONS) +await helper.mkdir(env.CDN_TEMP_LOCATIONS) export default app diff --git a/api/src/config/env.config.ts b/api/src/config/env.config.ts index 1d179b3cd..69bc5bff7 100644 --- a/api/src/config/env.config.ts +++ b/api/src/config/env.config.ts @@ -260,7 +260,7 @@ export const BACKEND_HOST = __env__('BC_BACKEND_HOST', true) export const FRONTEND_HOST = __env__('BC_FRONTEND_HOST', true) /** - * Default language. Default is en. Available options: en, fr. + * Default language. Default is en. Available options: en, fr, es. * * @type {string} */ diff --git a/api/src/controllers/bookingController.ts b/api/src/controllers/bookingController.ts index abd30191c..c51c99602 100644 --- a/api/src/controllers/bookingController.ts +++ b/api/src/controllers/bookingController.ts @@ -969,9 +969,6 @@ export const cancelBooking = async (req: Request, res: Response) => { booking.cancelRequest = true await booking.save() - // Notify supplier - await notify(booking.driver, booking._id.toString(), booking.supplier, i18n.t('CANCEL_BOOKING_NOTIFICATION')) - // Notify supplier const supplier = await User.findById(booking.supplier) if (!supplier) { @@ -979,15 +976,13 @@ export const cancelBooking = async (req: Request, res: Response) => { return res.sendStatus(204) } i18n.locale = supplier.language - let message = i18n.t('CANCEL_BOOKING_NOTIFICATION') - await notify(booking.driver, booking._id.toString(), supplier, message) + await notify(booking.driver, booking.id, supplier, i18n.t('CANCEL_BOOKING_NOTIFICATION')) // Notify admin const admin = !!env.ADMIN_EMAIL && await User.findOne({ email: env.ADMIN_EMAIL, type: bookcarsTypes.UserType.Admin }) if (admin) { i18n.locale = admin.language - message = i18n.t('CANCEL_BOOKING_NOTIFICATION') - await notify(booking.driver, booking._id.toString(), admin, message) + await notify(booking.driver, booking.id, admin, i18n.t('CANCEL_BOOKING_NOTIFICATION')) } return res.sendStatus(200) diff --git a/api/src/controllers/countryController.ts b/api/src/controllers/countryController.ts index 513f544e1..1fec7768e 100644 --- a/api/src/controllers/countryController.ts +++ b/api/src/controllers/countryController.ts @@ -381,6 +381,9 @@ export const getCountryId = async (req: Request, res: Response) => { const { name, language } = req.params try { + if (language.length !== 2) { + throw new Error('Language not valid') + } const lv = await LocationValue.findOne({ language, value: { $regex: new RegExp(`^${escapeStringRegexp(helper.trim(name, ' '))}$`, 'i') } }) if (lv) { const country = await Country.findOne({ values: lv.id }) diff --git a/api/src/controllers/locationController.ts b/api/src/controllers/locationController.ts index 72c1c4ccd..c2a37ff4b 100644 --- a/api/src/controllers/locationController.ts +++ b/api/src/controllers/locationController.ts @@ -471,6 +471,9 @@ export const getLocations = async (req: Request, res: Response) => { export const getLocationsWithPosition = async (req: Request, res: Response) => { try { const { language } = req.params + if (language.length !== 2) { + throw new Error('Language not valid') + } const keyword = escapeStringRegexp(String(req.query.s || '')) const options = 'i' @@ -563,6 +566,9 @@ export const getLocationId = async (req: Request, res: Response) => { const { name, language } = req.params try { + if (language.length !== 2) { + throw new Error('Language not valid') + } const lv = await LocationValue.findOne({ language, value: { $regex: new RegExp(`^${escapeStringRegexp(helper.trim(name, ' '))}$`, 'i') } }) if (lv) { const location = await Location.findOne({ values: lv.id }) @@ -662,6 +668,9 @@ export const deleteImage = async (req: Request, res: Response) => { const { id } = req.params try { + if (!helper.isValidObjectId(id)) { + throw new Error('Location Id not valid') + } const location = await Location.findById(id) if (location) { diff --git a/api/src/controllers/userController.ts b/api/src/controllers/userController.ts index ae1728567..6bf8287a8 100644 --- a/api/src/controllers/userController.ts +++ b/api/src/controllers/userController.ts @@ -1517,6 +1517,9 @@ export const sendEmail = async (req: Request, res: Response) => { export const hasPassword = async (req: Request, res: Response) => { const { id } = req.params try { + if (!helper.isValidObjectId(id)) { + throw new Error('User id not valid') + } const passwordExists = await User.exists({ _id: id, password: { $ne: null } }) if (passwordExists) { diff --git a/api/tests/booking.test.ts b/api/tests/booking.test.ts index 6eccbdd23..51a9121ea 100644 --- a/api/tests/booking.test.ts +++ b/api/tests/booking.test.ts @@ -642,6 +642,19 @@ describe('POST /api/cancel-booking/:id', () => { booking = await Booking.findById(BOOKING_ID) expect(booking?.cancelRequest).toBeTruthy() + // test failure (supplier not found) + booking = await Booking.findById(BOOKING_ID) + booking!.cancelRequest = false + const supplierId = booking!.supplier + booking!.supplier = testHelper.GetRandromObjectId() + await booking!.save() + res = await request(app) + .post(`/api/cancel-booking/${BOOKING_ID}`) + .set(env.X_ACCESS_TOKEN, token) + expect(res.statusCode).toBe(204) + booking!.supplier = supplierId + await booking!.save() + res = await request(app) .post(`/api/cancel-booking/${testHelper.GetRandromObjectIdAsString()}`) .set(env.X_ACCESS_TOKEN, token) diff --git a/api/tests/car.test.ts b/api/tests/car.test.ts index e1124c7e3..6408e4ab6 100644 --- a/api/tests/car.test.ts +++ b/api/tests/car.test.ts @@ -91,7 +91,7 @@ describe('POST /api/create-car', () => { available: false, type: bookcarsTypes.CarType.Diesel, gearbox: bookcarsTypes.GearboxType.Automatic, - aircon: true, + aircon: false, image: IMAGE1, seats: 5, doors: 4, @@ -153,7 +153,7 @@ describe('PUT /api/update-car', () => { available: true, type: bookcarsTypes.CarType.Gasoline, gearbox: bookcarsTypes.GearboxType.Manual, - aircon: false, + aircon: true, seats: 6, doors: 5, fuelPolicy: bookcarsTypes.FuelPolicy.LikeForLike, @@ -183,7 +183,7 @@ describe('PUT /api/update-car', () => { expect(car.available).toBeTruthy() expect(car.type).toBe(bookcarsTypes.CarType.Gasoline) expect(car.gearbox).toBe(bookcarsTypes.GearboxType.Manual) - expect(car.aircon).toBe(false) + expect(car.aircon).toBeTruthy() expect(car.seats).toBe(6) expect(car.doors).toBe(5) expect(car.fuelPolicy).toBe(bookcarsTypes.FuelPolicy.LikeForLike) @@ -398,7 +398,7 @@ describe('POST /api/cars/:page/:size', () => { rating: 4, seats: 6, carSpecs: { - aircon: false, + aircon: true, moreThanFiveSeats: true, moreThanFourDoors: true, }, @@ -544,7 +544,7 @@ describe('POST /api/frontend-cars/:page/:size', () => { rating: 4, seats: 6, carSpecs: { - aircon: false, + aircon: true, moreThanFiveSeats: true, moreThanFourDoors: true, }, diff --git a/api/tests/country.test.ts b/api/tests/country.test.ts index 3c7d8f4fc..d486ec761 100644 --- a/api/tests/country.test.ts +++ b/api/tests/country.test.ts @@ -249,6 +249,14 @@ describe('GET /api/countries-with-locations/:language/:imageRequired/:minLocatio .get(`/api/countries-with-locations/${language}/false/1`) expect(res.statusCode).toBe(200) expect(res.body.find((country: bookcarsTypes.Country) => country._id === COUNTRY_ID)).toBeUndefined() + + // test failure + await databaseHelper.close() + res = await request(app) + .get(`/api/countries-with-locations/${language}/false/1`) + expect(res.statusCode).toBe(400) + const connRes = await databaseHelper.connect(env.DB_URI, false, false) + expect(connRes).toBeTruthy() }) }) @@ -268,6 +276,11 @@ describe('GET /api/country-id/:name/:language', () => { .set(env.X_ACCESS_TOKEN, token) expect(res.statusCode).toBe(204) + res = await request(app) + .get('/api/country-id/unknown/english') + .set(env.X_ACCESS_TOKEN, token) + expect(res.statusCode).toBe(400) + await testHelper.signout(token) }) }) diff --git a/api/tests/database.test.ts b/api/tests/database.test.ts index f2c342ac8..25a134292 100644 --- a/api/tests/database.test.ts +++ b/api/tests/database.test.ts @@ -33,7 +33,7 @@ describe('Test database initialization', () => { const lv1 = new LocationValue({ language: 'en', value: 'location' }) await lv1.save() - const lv2 = new LocationValue({ language: 'es', value: 'localización' }) + const lv2 = new LocationValue({ language: 'pt', value: 'localização' }) await lv2.save() const l1 = new Location({ country: testHelper.GetRandromObjectIdAsString(), values: [lv1.id, lv2.id] }) await l1.save() @@ -42,7 +42,7 @@ describe('Test database initialization', () => { const cv1 = new LocationValue({ language: 'en', value: 'country' }) await cv1.save() - const cv2 = new LocationValue({ language: 'es', value: 'país' }) + const cv2 = new LocationValue({ language: 'pt', value: 'país' }) await cv2.save() const c1 = new Country({ values: [cv1.id, cv2.id] }) await c1.save() @@ -51,7 +51,7 @@ describe('Test database initialization', () => { const pv1 = new LocationValue({ language: 'en', value: 'parking' }) await pv1.save() - const pv2 = new LocationValue({ language: 'es', value: 'aparcamiento' }) + const pv2 = new LocationValue({ language: 'pt', value: 'estacionamento' }) await pv2.save() const ps1 = new ParkingSpot({ latitude: 1, longitude: 1, values: [pv1.id, pv2.id] }) await ps1.save() diff --git a/api/tests/helper.test.ts b/api/tests/helper.test.ts index 91c6620fb..616787201 100644 --- a/api/tests/helper.test.ts +++ b/api/tests/helper.test.ts @@ -29,3 +29,10 @@ describe('Test trim', () => { expect(helper.trim(' xxxxxxxx ', ' ')).toBe('xxxxxxxx') }) }) + +describe('Test getStripeLocale', () => { + it('should test getStripeLocale', () => { + expect(helper.getStripeLocale('en')).toBe('en') + expect(helper.getStripeLocale('')).toBe('auto') + }) +}) diff --git a/api/tests/location.test.ts b/api/tests/location.test.ts index b176f64b6..86055d869 100644 --- a/api/tests/location.test.ts +++ b/api/tests/location.test.ts @@ -254,6 +254,9 @@ describe('PUT /api/update-location/:id', () => { expect(res.body?.longitude).toBe(payload.longitude) expect(res.body?.parkingSpots.length).toBe(0) + const loc = await Location.findById(LOCATION_ID) + loc!.parkingSpots = null + await loc!.save() payload.parkingSpots = [ { latitude: 28.1268755, @@ -300,6 +303,10 @@ describe('GET /api/location-id/:name/:language', () => { res = await request(app) .get(`/api/location-id/unknown/${language}`) expect(res.statusCode).toBe(204) + + res = await request(app) + .get('/api/location-id/unknown/english') + expect(res.statusCode).toBe(400) }) }) @@ -405,6 +412,11 @@ describe('POST /api/delete-location-image/:id', () => { .set(env.X_ACCESS_TOKEN, token) expect(res.statusCode).toBe(204) + res = await request(app) + .post('/api/delete-location-image/invalid-id') + .set(env.X_ACCESS_TOKEN, token) + expect(res.statusCode).toBe(400) + await testHelper.signout(token) }) }) @@ -477,9 +489,13 @@ describe('GET /api/locations-with-position/:language', () => { expect(res.body.length).toBeGreaterThanOrEqual(1) res = await request(app) - .get('/api/locations-with-position/unknown') + .get('/api/locations-with-position/pt') expect(res.statusCode).toBe(200) expect(res.body.length).toBe(0) + + res = await request(app) + .get('/api/locations-with-position/english') + expect(res.statusCode).toBe(400) }) }) diff --git a/api/tests/logger.test.ts b/api/tests/logger.test.ts index 1cdf6bfde..8f86bad91 100644 --- a/api/tests/logger.test.ts +++ b/api/tests/logger.test.ts @@ -1,7 +1,7 @@ import 'dotenv/config' import * as logger from '../src/common/logger' -describe('Tes logging', () => { +describe('Test logging', () => { it('should test logging', () => { let res = true try { diff --git a/api/tests/supplier.test.ts b/api/tests/supplier.test.ts index 9d3b459d1..fb3e0b5a7 100644 --- a/api/tests/supplier.test.ts +++ b/api/tests/supplier.test.ts @@ -67,6 +67,10 @@ beforeAll(async () => { fullInsurance: 200, additionalDriver: 200, range: bookcarsTypes.CarRange.Midi, + rating: 4, + multimedia: [ + bookcarsTypes.CarMultimedia.AndroidAuto, + ], }) await car1.save() CAR1_ID = car1.id @@ -83,8 +87,8 @@ beforeAll(async () => { gearbox: bookcarsTypes.GearboxType.Automatic, aircon: true, image: undefined, - seats: 5, - doors: 4, + seats: 6, + doors: 5, fuelPolicy: bookcarsTypes.FuelPolicy.FreeTank, mileage: -1, cancellation: 0, @@ -94,6 +98,10 @@ beforeAll(async () => { fullInsurance: 200, additionalDriver: 200, range: bookcarsTypes.CarRange.Midi, + rating: 4, + multimedia: [ + bookcarsTypes.CarMultimedia.AndroidAuto, + ], }) await car2.save() CAR2_ID = car2.id @@ -459,6 +467,17 @@ describe('POST /api/frontend-suppliers', () => { mileage: [bookcarsTypes.Mileage.Limited, bookcarsTypes.Mileage.Unlimited], fuelPolicy: [bookcarsTypes.FuelPolicy.FreeTank, bookcarsTypes.FuelPolicy.FreeTank], deposit: -1, + multimedia: [ + bookcarsTypes.CarMultimedia.AndroidAuto, + ], + rating: 4, + seats: 6, + carSpecs: { + aircon: true, + moreThanFiveSeats: true, + moreThanFourDoors: true, + }, + ranges: [bookcarsTypes.CarRange.Midi], } let res = await request(app) @@ -502,16 +521,12 @@ describe('POST /api/frontend-suppliers', () => { expect(res.statusCode).toBe(200) expect(res.body.length).toBe(2) - // carSpecs - payload.carSpecs = {} - payload.carSpecs.aircon = true - payload.carSpecs.moreThanFiveSeats = true - payload.carSpecs.moreThanFourDoors = true + payload.seats = 3 res = await request(app) .post('/api/frontend-suppliers') .send(payload) expect(res.statusCode).toBe(200) - expect(res.body.length).toBe(1) + expect(res.body.length).toBe(0) }) }) @@ -526,6 +541,17 @@ describe('POST /api/backend-suppliers', () => { mileage: [bookcarsTypes.Mileage.Limited, bookcarsTypes.Mileage.Unlimited], fuelPolicy: [bookcarsTypes.FuelPolicy.FreeTank, bookcarsTypes.FuelPolicy.FreeTank], deposit: -1, + multimedia: [ + bookcarsTypes.CarMultimedia.AndroidAuto, + ], + rating: 4, + seats: 6, + carSpecs: { + aircon: true, + moreThanFiveSeats: true, + moreThanFourDoors: true, + }, + ranges: [bookcarsTypes.CarRange.Midi], } let res = await request(app) @@ -573,17 +599,14 @@ describe('POST /api/backend-suppliers', () => { expect(res.statusCode).toBe(200) expect(res.body.length).toBeGreaterThan(0) - // carSpecs - payload.carSpecs = {} - payload.carSpecs.aircon = true - payload.carSpecs.moreThanFiveSeats = true - payload.carSpecs.moreThanFourDoors = true + payload.seats = 3 res = await request(app) .post('/api/backend-suppliers') .set(env.X_ACCESS_TOKEN, token) .send(payload) expect(res.statusCode).toBe(200) - expect(res.body.length).toBeGreaterThan(0) + expect(res.body.length).toBe(0) + payload.seats = 6 payload.availability = [bookcarsTypes.Availablity.Available] res = await request(app) diff --git a/api/tests/user.test.ts b/api/tests/user.test.ts index 90907a158..5784713a7 100644 --- a/api/tests/user.test.ts +++ b/api/tests/user.test.ts @@ -499,6 +499,14 @@ describe('POST /api/sign-in/:type', () => { const token = testHelper.getToken(cookies[1]) expect(token).toBeDefined() + // test failure (email not found) + payload.email = '' + res = await request(app) + .post(`/api/sign-in/${bookcarsTypes.AppType.Frontend}`) + .send(payload) + expect(res.statusCode).toBe(400) + payload.email = USER1_EMAIL + payload.password = 'wrong-password' res = await request(app) .post(`/api/sign-in/${bookcarsTypes.AppType.Frontend}`) @@ -532,6 +540,89 @@ describe('POST /api/sign-in/:type', () => { }) }) +describe('POST /api/social-sign-in/:type', () => { + it('should sign in', async () => { + const payload: bookcarsTypes.SignInPayload = { + email: USER1_EMAIL, + socialSignInType: bookcarsTypes.SocialSignInType.Google, + accessToken: testHelper.GetRandromObjectIdAsString(), + } + + let res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(400) + + payload.socialSignInType = bookcarsTypes.SocialSignInType.Facebook + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(400) + + payload.socialSignInType = bookcarsTypes.SocialSignInType.Apple + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(400) + + // test success (mobile) + payload.mobile = true + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(200) + + // test success (mobile stay connected) + payload.mobile = true + payload.stayConnected = true + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(200) + + // test success (mobile new user) + payload.email = testHelper.GetRandomEmail() + payload.fullName = 'Random user' + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(200) + await User.deleteOne({ email: payload.email }) + payload.mobile = false + + payload.email = undefined + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(400) + + payload.email = 'not-valid-email' + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(400) + payload.email = USER1_EMAIL + + payload.socialSignInType = undefined + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(400) + payload.socialSignInType = bookcarsTypes.SocialSignInType.Google + + payload.accessToken = undefined + res = await request(app) + .post('/api/social-sign-in') + .send(payload) + expect(res.statusCode).toBe(400) + payload.accessToken = testHelper.GetRandromObjectIdAsString() + + res = await request(app) + .post('/api/social-sign-in') + expect(res.statusCode).toBe(400) + }) +}) + describe('POST /api/sign-out', () => { it('should sign out', async () => { const res = await request(app) @@ -1147,6 +1238,29 @@ describe('POST /api/users/:page/:size', () => { }) }) +describe('GET /api/has-password/:id', () => { + it('should get users', async () => { + const token = await testHelper.signinAsAdmin() + + let res = await request(app) + .get(`/api/has-password/${USER1_ID}`) + .set(env.X_ACCESS_TOKEN, token) + expect(res.statusCode).toBe(200) + + res = await request(app) + .get(`/api/has-password/${testHelper.GetRandromObjectIdAsString()}`) + .set(env.X_ACCESS_TOKEN, token) + expect(res.statusCode).toBe(204) + + res = await request(app) + .get('/api/has-password/wrong-id') + .set(env.X_ACCESS_TOKEN, token) + expect(res.statusCode).toBe(400) + + await testHelper.signout(token) + }) +}) + describe('POST /api/delete-users', () => { it('should delete users', async () => { const token = await testHelper.signinAsAdmin()