From fce2de7d14eb5358d3586a9d55870ca005242a11 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 1 Mar 2023 22:17:30 +0000 Subject: [PATCH 01/30] Refactored auth tests --- src/models/ExampleUser.js | 15 - test/end-to-end/application.js | 185 ------- test/end-to-end/apply/company.js | 182 +++++++ test/end-to-end/auth.js | 477 ------------------ test/end-to-end/auth/login.js | 127 +++++ test/end-to-end/auth/me.js | 99 ++++ .../end-to-end/auth/recover/:token/confirm.js | 188 +++++++ test/end-to-end/auth/recover/request.js | 76 +++ test/end-to-end/auth/register.js | 90 ++++ 9 files changed, 762 insertions(+), 677 deletions(-) delete mode 100644 src/models/ExampleUser.js delete mode 100644 test/end-to-end/application.js create mode 100644 test/end-to-end/apply/company.js delete mode 100644 test/end-to-end/auth.js create mode 100644 test/end-to-end/auth/login.js create mode 100644 test/end-to-end/auth/me.js create mode 100644 test/end-to-end/auth/recover/:token/confirm.js create mode 100644 test/end-to-end/auth/recover/request.js create mode 100644 test/end-to-end/auth/register.js diff --git a/src/models/ExampleUser.js b/src/models/ExampleUser.js deleted file mode 100644 index d66d3db1..00000000 --- a/src/models/ExampleUser.js +++ /dev/null @@ -1,15 +0,0 @@ -import mongoose from "mongoose"; -const { Schema } = mongoose; - -// First we create our schema -const ExampleUserSchema = new Schema({ - username: { type: String, unique: true }, - // The schemas can have very flexible type definition options, see https://mongoosejs.com/docs/schematypes.html - age: { type: Number, default: 420 }, -}); - -// Then we enable using it by converting it into a model -const ExampleUser = mongoose.model("ExampleUser", ExampleUserSchema); - -// Which we can then export -export default ExampleUser; diff --git a/test/end-to-end/application.js b/test/end-to-end/application.js deleted file mode 100644 index 28fb316f..00000000 --- a/test/end-to-end/application.js +++ /dev/null @@ -1,185 +0,0 @@ -import EmailService from "../../src/lib/emailService"; -import { StatusCodes as HTTPStatus } from "http-status-codes"; -import CompanyApplication, { CompanyApplicationRules } from "../../src/models/CompanyApplication"; -import Account from "../../src/models/Account"; -import ValidatorTester from "../utils/ValidatorTester"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; -import CompanyApplicationConstants from "../../src/models/constants/CompanyApplication"; -import AccountConstants from "../../src/models/constants/Account"; -import CompanyConstants from "../../src/models/constants/Company"; -import { NEW_COMPANY_APPLICATION_ADMINS, NEW_COMPANY_APPLICATION_COMPANY } from "../../src/email-templates/companyApplicationApproval"; -import config from "../../src/config/env"; - - -describe("Company application endpoint test", () => { - describe("POST /application", () => { - describe("Input Validation (unsuccessful application)", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/apply/company").send(params)); - const BodyValidatorTester = EndpointValidatorTester("body"); - describe("email", () => { - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - describe("password", () => { - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); - FieldValidatorTester.hasNumber(); - }); - - describe("motivation", () => { - const FieldValidatorTester = BodyValidatorTester("motivation"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(CompanyApplicationConstants.motivation.min_length); - FieldValidatorTester.hasMaxLength(CompanyApplicationConstants.motivation.max_length); - }); - - describe("companyName", () => { - const FieldValidatorTester = BodyValidatorTester("companyName"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(CompanyConstants.companyName.min_length); - FieldValidatorTester.hasMaxLength(CompanyConstants.companyName.max_length); - }); - }); - - describe("Without any existing application and accounts", () => { - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - await Account.deleteMany({}); - }); - - const RealDateNow = Date.now; - const mockCurrentDate = new Date("2019-11-23"); - - beforeEach(() => { - Date.now = () => mockCurrentDate.getTime(); - }); - - afterEach(() => { - Date.now = RealDateNow; - }); - - test("Valid creation", async () => { - const application = { - email: "test@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", - }; - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.OK); - const created_application_id = res.body._id; - - const created_application = await CompanyApplication.findById(created_application_id); - - expect(created_application).toBeDefined(); - expect(created_application).toHaveProperty("email", application.email); - expect(created_application).toHaveProperty("companyName", application.companyName); - expect(created_application).toHaveProperty("motivation", application.motivation); - expect(created_application).toHaveProperty("submittedAt", mockCurrentDate); - }); - - test("Should send an email to admin and to company user", async () => { - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", - }; - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.OK); - - const adminEmailOptions = NEW_COMPANY_APPLICATION_ADMINS( - application.email, application.companyName, application.motivation); - const companyEmailOptions = NEW_COMPANY_APPLICATION_COMPANY( - application.companyName, res.body._id); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: adminEmailOptions.subject, - to: config.mail_from, - template: adminEmailOptions.template, - context: adminEmailOptions.context, - })); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: companyEmailOptions.subject, - to: application.email, - template: companyEmailOptions.template, - context: { ...companyEmailOptions.context }, - })); - }); - - describe("Invalid input", () => { - test("Should fail while using an email with an associated Account", async () => { - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - }; - - await Account.create({ - email: application.email, - password: application.password, - isAdmin: true, - }); - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.ALREADY_EXISTS("email"), - "param": "email", - "value": application.email, - }); - }); - - test("Should fail while using an email with an associated application that was not rejected", async () => { - - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - }; - - await CompanyApplication.deleteMany({}); - // Guarantees that the company application will succeed regarding account rules - await Account.deleteOne({ email: application.email }); - - - // Existing Application - Default `Pending` state - await CompanyApplication.create({ - ...application, - submittedAt: Date.now(), - }); - - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": CompanyApplicationRules.ONLY_ONE_APPLICATION_ACTIVE_PER_EMAIL.msg, - "param": "email", - "value": application.email, - }); - }); - }); - }); - }); -}); diff --git a/test/end-to-end/apply/company.js b/test/end-to-end/apply/company.js new file mode 100644 index 00000000..aa00a7ea --- /dev/null +++ b/test/end-to-end/apply/company.js @@ -0,0 +1,182 @@ +import EmailService from "../../../src/lib/emailService"; +import { StatusCodes } from "http-status-codes"; +import CompanyApplication, { CompanyApplicationRules } from "../../../src/models/CompanyApplication"; +import Account from "../../../src/models/Account"; +import ValidatorTester from "../../utils/ValidatorTester"; +import ValidationReasons from "../../../src/api/middleware/validators/validationReasons"; +import CompanyApplicationConstants from "../../../src/models/constants/CompanyApplication"; +import AccountConstants from "../../../src/models/constants/Account"; +import CompanyConstants from "../../../src/models/constants/Company"; +import { NEW_COMPANY_APPLICATION_ADMINS, NEW_COMPANY_APPLICATION_COMPANY } from "../../../src/email-templates/companyApplicationApproval"; +import config from "../../../src/config/env"; + +describe("POST /apply/company", () => { + describe("Input Validation", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/apply/company").send(params)); + const BodyValidatorTester = EndpointValidatorTester("body"); + + describe("email", () => { + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + + describe("password", () => { + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); + FieldValidatorTester.hasNumber(); + }); + + describe("motivation", () => { + const FieldValidatorTester = BodyValidatorTester("motivation"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(CompanyApplicationConstants.motivation.min_length); + FieldValidatorTester.hasMaxLength(CompanyApplicationConstants.motivation.max_length); + }); + + describe("companyName", () => { + const FieldValidatorTester = BodyValidatorTester("companyName"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(CompanyConstants.companyName.min_length); + FieldValidatorTester.hasMaxLength(CompanyConstants.companyName.max_length); + }); + }); + + describe("Without any existing application and accounts", () => { + + const RealDateNow = Date.now; + const mockCurrentDate = new Date("2019-11-23"); + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + await Account.deleteMany({}); + + Date.now = () => mockCurrentDate.getTime(); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + await Account.deleteMany({}); + + Date.now = RealDateNow; + }); + + test("Valid creation", async () => { + const application = { + email: "test@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", + }; + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.OK); + + // eslint-disable-next-line no-unused-vars + const { password, ...rest } = application; + expect(res.body).toMatchObject(rest); + }); + + test("Should send an email to admin and to company user", async () => { + const application = { + email: "test2@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", + }; + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.OK); + + const adminEmailOptions = NEW_COMPANY_APPLICATION_ADMINS( + application.email, application.companyName, application.motivation); + const companyEmailOptions = NEW_COMPANY_APPLICATION_COMPANY( + application.companyName, res.body._id); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: adminEmailOptions.subject, + to: config.mail_from, + template: adminEmailOptions.template, + context: adminEmailOptions.context, + })); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: companyEmailOptions.subject, + to: application.email, + template: companyEmailOptions.template, + context: { ...companyEmailOptions.context }, + })); + }); + + describe("Invalid input", () => { + + const application = { + email: "test2@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + }; + + beforeAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + test("Should fail while using an email with an associated Account", async () => { + + await Account.create({ + email: application.email, + password: application.password, + isAdmin: true, + }); + + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.ALREADY_EXISTS("email"), + "param": "email", + "value": application.email, + }); + }); + + test("Should fail while using an email with an associated application that was not rejected", async () => { + + // Guarantees that the company application will succeed regarding account rules + await Account.deleteOne({ email: application.email }); + + // Existing Application - Default `Pending` state + await CompanyApplication.create({ + ...application, + submittedAt: Date.now(), + }); + + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": CompanyApplicationRules.ONLY_ONE_APPLICATION_ACTIVE_PER_EMAIL.msg, + "param": "email", + "value": application.email, + }); + }); + }); + }); +}); diff --git a/test/end-to-end/auth.js b/test/end-to-end/auth.js deleted file mode 100644 index 3d005b60..00000000 --- a/test/end-to-end/auth.js +++ /dev/null @@ -1,477 +0,0 @@ -import { StatusCodes as HTTPStatus } from "http-status-codes"; -import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import Account from "../../src/models/Account"; -import ValidatorTester from "../utils/ValidatorTester"; -import withGodToken from "../utils/GodToken"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; -import hash from "../../src/lib/passwordHashing"; -import AccountConstants, { RECOVERY_LINK_EXPIRATION } from "../../src/models/constants/Account"; -import Company from "../../src/models/Company"; -import EmailService from "../../src/lib/emailService"; -import * as token from "../../src/lib/token"; -import { REQUEST_ACCOUNT_RECOVERY } from "../../src/email-templates/accountManagement"; -import env from "../../src/config/env"; -import { SECOND_IN_MS } from "../../src/models/constants/TimeConstants"; - -const generateTokenSpy = jest.spyOn(token, "generateToken"); -jest.spyOn(token, "verifyAndDecodeToken"); - -describe("Register endpoint test", () => { - describe("Input Validation (unsuccessful registration)", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/register").send(withGodToken(params))); - const BodyValidatorTester = EndpointValidatorTester("body"); - describe("email", () => { - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - describe("password", () => { - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); - FieldValidatorTester.hasNumber(); - }); - }); - - describe("Without pre-existing users", () => { - beforeAll(async () => { - await Account.deleteMany({}); - }); - - test("Should return forbidden", async () => { - const user = { - email: "user@email.com", - password: "password123", - }; - - const res = await request() - .post("/auth/register") - .send(user); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - - test("Should make a successful registration", async () => { - const user = { - email: "user@email.com", - password: "password123", - }; - - const res = await request() - .post("/auth/register") - .send(withGodToken(user)); - - expect(res.status).toBe(HTTPStatus.OK); - - const registered_user = await Account.findOne({ email: user.email }); - expect(registered_user).toBeDefined(); - expect(registered_user).toHaveProperty("email", user.email); - }); - }); - -}); - -describe("Login endpoint test", () => { - describe("Input Validation", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(withGodToken(params))); - const BodyValidatorTester = EndpointValidatorTester("body"); - describe("email", () => { - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - describe("password", () => { - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - }); - }); - - describe("Using already resgistered user", () => { - const test_agent = agent(); - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company = { - email: "company@email.com", - password: "password123", - }; - let test_company; - - beforeAll(async () => { - await Account.deleteMany({}); - await Account.create({ email: test_user_admin.email, password: await hash(test_user_admin.password), isAdmin: true }); - test_company = await Company.create({ name: "test comapny" }); - await Account.create({ - email: test_user_company.email, - password: await hash(test_user_admin.password), - company: test_company._id }); - }); - - test("should return an error when registering with an already existing email", async () => { - const res = await request() - .post("/auth/register") - .send(withGodToken(test_user_admin)); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.ALREADY_EXISTS("email"), - "param": "email", - "value": test_user_admin.email, - }); - }); - - test("should return forbidden when retrieving the information of the logged in user", - async () => { - const res = await request() - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - } - ); - - test("should unsuccessfully login with registered account (wrong password)", async () => { - const res = await test_agent - .post("/auth/login") - .send({ - email: "user@gmail.com", - password: "password", - }); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - - - test("should successfully login with registered account", async () => { - const res = await test_agent - .post("/auth/login") - .send(test_user_admin); - - // TODO: Reimplement res.should.have.cookie("connect.sid"); - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should return the informations of the logged in user (admin)", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin); - - const res = await test_agent - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body).toHaveProperty("data.email", test_user_admin.email); - expect(res.body).toHaveProperty("data.isAdmin", true); - expect(res.body).not.toHaveProperty("data.company"); - }); - - test("should return the informations of the logged in user (company)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company); - - const res = await test_agent - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body).toHaveProperty("data.email", test_user_company.email); - expect(res.body).toHaveProperty("data.isAdmin", false); - expect(res.body).toHaveProperty("data.company", expect.objectContaining( - JSON.parse(JSON.stringify(test_company.toObject())) // Necessary since mongoose objects don't play well with jest... - )); - }); - - test("should be successful when loging out the current user", async () => { - const res = await test_agent - .delete("/auth/login") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should return an error since no user is logged in", async () => { - const res = await test_agent - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - }); - - describe("Using logged out user", () => { - const logged_out_agent = agent(); - - test("should return OK since the logout is idempotent", async () => { - const res = await logged_out_agent - .delete("/auth/login") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - }); - }); -}); - -describe("Password recovery endpoint test", () => { - const test_account = { - email: "recover_email@gmail.com", - password: "password123", - }; - - const newPassword = "new_password_123"; - - beforeEach(async () => { - await Account.deleteMany({ email: test_account.email }); - await Account.create({ - email: test_account.email, - password: await hash(test_account.password), - isAdmin: true, - }); - jest.clearAllMocks(); - }); - - describe("POST /auth/recover/request", () => { - describe("email", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/recover/request").send(params)); - const BodyValidatorTester = EndpointValidatorTester("body"); - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - test("should return ok and not send email nor generate a token if account not found", async () => { - const res = await request() - .post("/auth/recover/request") - .send({ email: "not_valid_email@email.com" }); - - expect(EmailService.sendMail).not.toHaveBeenCalled(); - expect(token.generateToken).not.toHaveBeenCalled(); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should generate token and send email if account found", async () => { - const res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - expect(res.status).toBe(HTTPStatus.OK); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - - const emailOptions = REQUEST_ACCOUNT_RECOVERY(`${env.password_recovery_link}/${generatedToken}`); - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_account.email, - template: emailOptions.template, - context: emailOptions.context, - })); - - expect(res.status).toBe(HTTPStatus.OK); - }); - }); - - describe("GET /auth/recover/:token/confirm", () => { - test("should fail if invalid token", async () => { - const res = await request() - .get("/auth/recover/token/confirm"); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - }); - - test("should accept if valid token", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - res = await request() - .get(`/auth/recover/${generatedToken}/confirm`); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should fail if valid token expired", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - - const realTime = Date.now; - const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); - Date.now = () => mockDate; - - res = await request() - .get(`/auth/recover/${generatedToken}/confirm`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - - Date.now = realTime; - }); - }); - - describe("POST /auth/recover/:token/confirm", () => { - test("should fail if invalid token", async () => { - const res = await request() - .post("/auth/recover/token/confirm") - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - }); - - test("should accept if valid token", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should fail if valid token expired", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - - const realTime = Date.now; - const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); - Date.now = () => mockDate; - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - - Date.now = realTime; - }); - - describe("password", () => { - let generatedToken; - - beforeAll(async () => { - await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - generatedToken = generateTokenSpy.mock.results[0].value; - }); - - const EndpointValidatorTester = - ValidatorTester((params) => request().post(`/auth/recover/${generatedToken}/confirm`).send(params)); - const BodyValidatorTester = EndpointValidatorTester("body"); - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); - FieldValidatorTester.hasNumber(); - }); - - test("should succeed to complete the whole password recovery process", async () => { - const test_agent = agent(); - let res = await test_agent - .post("/auth/login") - .send(test_account); - - expect(res.status).toBe(HTTPStatus.OK); - - await test_agent.delete("/auth/login"); - - res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(res.status).toBe(HTTPStatus.OK); - const generatedToken = generateTokenSpy.mock.results[0].value; - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - - res = await test_agent - .post("/auth/login") - .send({ email: test_account.email, password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - - }); - - test("should change password in database after whole password recovery process", async () => { - const oldPassword = (await Account.findOne({ email: test_account.email })).password; - - const test_agent = agent(); - let res = await test_agent - .post("/auth/login") - .send(test_account); - - expect(res.status).toBe(HTTPStatus.OK); - - await test_agent.delete("/auth/login"); - - res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(res.status).toBe(HTTPStatus.OK); - const generatedToken = generateTokenSpy.mock.results[0].value; - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - - const password = (await Account.findOne({ email: test_account.email })).password; - - expect(password).not.toBe(oldPassword); - - }); - }); - -}); diff --git a/test/end-to-end/auth/login.js b/test/end-to-end/auth/login.js new file mode 100644 index 00000000..3c625b7c --- /dev/null +++ b/test/end-to-end/auth/login.js @@ -0,0 +1,127 @@ +import { StatusCodes } from "http-status-codes"; +import Account from "../../../src/models/Account"; +import Company from "../../../src/models/Company"; +import ValidatorTester from "../../utils/ValidatorTester"; +import withGodToken from "../../utils/GodToken"; +import hash from "../../../src/lib/passwordHashing"; + +describe("POST /auth/login", () => { + describe("Input Validation", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(withGodToken(params))); + const BodyValidatorTester = EndpointValidatorTester("body"); + describe("email", () => { + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + + describe("password", () => { + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + }); + }); + + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const test_user_company = { + email: "company@email.com", + password: "password123", + }; + + let test_company; + + beforeAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + test_company = await Company.create({ name: "test company" }); + + await Account.create({ + email: test_user_company.email, + password: await hash(test_user_company.password), + company: test_company._id + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + test("should fail to login if password is wrong", async () => { + const res = await test_agent + .post("/auth/login") + .send({ + email: "user@gmail.com", + password: "password", + }); + + expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + }); + + + test("should successfully login with registered account", async () => { + const res = await test_agent + .post("/auth/login") + .send(test_user_admin); + + expect(res.status).toBe(StatusCodes.OK); + }); +}); + +describe("DELETE /auth/login", () => { + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + beforeAll(async () => { + await Account.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + }); + + test("should return OK since the logout is idempotent", async () => { + const res = await test_agent + .delete("/auth/login") + .send(); + + expect(res.status).toBe(StatusCodes.OK); + }); + + test("should be successful when logging out the current user", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .delete("/auth/login") + .send(); + + expect(res.status).toBe(StatusCodes.OK); + }); +}); diff --git a/test/end-to-end/auth/me.js b/test/end-to-end/auth/me.js new file mode 100644 index 00000000..e9b2b60b --- /dev/null +++ b/test/end-to-end/auth/me.js @@ -0,0 +1,99 @@ +import { StatusCodes } from "http-status-codes"; +import Account from "../../../src/models/Account"; +import Company from "../../../src/models/Company"; +import hash from "../../../src/lib/passwordHashing"; + +describe("GET /auth/me", () => { + + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123" + }; + + const test_user_company = { + email: "company@email.com", + password: "password123", + }; + + let test_company; + + beforeAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + test_company = await Company.create({ name: "test company" }); + + await Account.create({ + email: test_user_company.email, + password: await hash(test_user_company.password), + company: test_company._id + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + + afterEach(async () => { + await test_agent + .delete("/auth/login") + .send() + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + test("should return the information of the logged in user (admin)", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin); + + const res = await test_agent + .get("/auth/me") + .send(); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body).toHaveProperty("data.email", test_user_admin.email); + expect(res.body).toHaveProperty("data.isAdmin", true); + expect(res.body).not.toHaveProperty("data.company"); + }); + + test("should return the information of the logged in user (company)", async () => { + await test_agent + .post("/auth/login") + .send(test_user_company); + + const res = await test_agent + .get("/auth/me") + .send(); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body).toHaveProperty("data.email", test_user_company.email); + expect(res.body).toHaveProperty("data.isAdmin", false); + expect(res.body).toHaveProperty("data.company", expect.objectContaining( + JSON.parse(JSON.stringify(test_company.toObject()) // Necessary since mongoose objects don't play well with jest... + ))); + }); + + test("should return an error since no user is logged in", async () => { + const res = await test_agent + .get("/auth/me") + .send(); + + expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + }); +}); diff --git a/test/end-to-end/auth/recover/:token/confirm.js b/test/end-to-end/auth/recover/:token/confirm.js new file mode 100644 index 00000000..236d3340 --- /dev/null +++ b/test/end-to-end/auth/recover/:token/confirm.js @@ -0,0 +1,188 @@ +import { StatusCodes } from "http-status-codes"; +import Account from "../../../../../src/models/Account"; +import ValidatorTester from "../../../../utils/ValidatorTester"; +import ValidationReasons from "../../../../../src/api/middleware/validators/validationReasons"; +import hash from "../../../../../src/lib/passwordHashing"; +import AccountConstants, { RECOVERY_LINK_EXPIRATION } from "../../../../../src/models/constants/Account"; +import * as token from "../../../../../src/lib/token"; +import env from "../../../../../src/config/env"; +import { SECOND_IN_MS } from "../../../../../src/models/constants/TimeConstants"; +import { generateToken } from "../../../../../src/lib/token"; + +const generateTokenSpy = jest.spyOn(token, "generateToken"); +jest.spyOn(token, "verifyAndDecodeToken"); + +describe("GET /auth/recover/:token/confirm", () => { + + const test_account = { + email: "recover_email@gmail.com", + password: "password123", + }; + + beforeEach(async () => { + await Account.deleteMany({ email: test_account.email }); + + await Account.create({ + email: test_account.email, + password: await hash(test_account.password), + isAdmin: true, + }); + + jest.clearAllMocks(); + }); + + test("should fail if invalid token", async () => { + const res = await request() + .get("/auth/recover/token/confirm"); + + expect(res.status).toBe(StatusCodes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); + }); + + test("should accept if valid token", async () => { + let res = await request() + .post("/auth/recover/request") + .send({ email: test_account.email }); + + expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + const generatedToken = generateTokenSpy.mock.results[0].value; + expect(res.status).toBe(StatusCodes.OK); + + res = await request() + .get(`/auth/recover/${generatedToken}/confirm`); + + expect(res.status).toBe(StatusCodes.OK); + }); + + test("should fail if valid token expired", async () => { + let res = await request() + .post("/auth/recover/request") + .send({ email: test_account.email }); + + expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + const generatedToken = generateTokenSpy.mock.results[0].value; + expect(res.status).toBe(StatusCodes.OK); + + + const realTime = Date.now; + const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); + Date.now = () => mockDate; + + res = await request() + .get(`/auth/recover/${generatedToken}/confirm`); + + expect(res.status).toBe(StatusCodes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); + + Date.now = realTime; + }); +}); + +describe("POST /auth/recover/:token/confirm", () => { + + const test_account = { + email: "recover_email@gmail.com", + password: "password123", + }; + + const newPassword = "new_password_123"; + + beforeEach(async () => { + await Account.deleteMany({ email: test_account.email }); + + await Account.create({ + email: test_account.email, + password: await hash(test_account.password), + isAdmin: true, + }); + + jest.clearAllMocks(); + }); + + afterAll(async () => { + await Account.deleteMany({}); + }); + + describe("Input Validation", () => { + describe("password", () => { + let generatedToken; + + beforeAll(async () => { + await request() + .post("/auth/recover/request") + .send({ email: test_account.email }); + + expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + generatedToken = generateTokenSpy.mock.results[0].value; + }); + + const EndpointValidatorTester = + ValidatorTester((params) => request().post(`/auth/recover/${generatedToken}/confirm`).send(params)); + const BodyValidatorTester = EndpointValidatorTester("body"); + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); + FieldValidatorTester.hasNumber(); + }); + }); + + test("should fail if invalid token", async () => { + const res = await request() + .post("/auth/recover/token/confirm") + .send({ password: newPassword }); + + expect(res.status).toBe(StatusCodes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); + }); + + test("should accept if valid token", async () => { + const generatedToken = generateToken({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + const res = await request() + .post(`/auth/recover/${generatedToken}/confirm`) + .send({ password: newPassword }); + + expect(res.status).toBe(StatusCodes.OK); + }); + + test("should fail if valid token expired", async () => { + + const generatedToken = generateToken({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + const realTime = Date.now; + const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); + Date.now = () => mockDate; + + const res = await request() + .post(`/auth/recover/${generatedToken}/confirm`) + .send({ password: newPassword }); + + expect(res.status).toBe(StatusCodes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); + + Date.now = realTime; + }); + + test("should succeed to complete the whole password recovery process", async () => { + const generatedToken = generateToken({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + await request() + .post(`/auth/recover/${generatedToken}/confirm`) + .send({ password: newPassword }) + .expect(StatusCodes.OK); + + await request() + .post("/auth/login") + .send({ email: test_account.email, password: newPassword }) + .expect(StatusCodes.OK); + + }); +}); diff --git a/test/end-to-end/auth/recover/request.js b/test/end-to-end/auth/recover/request.js new file mode 100644 index 00000000..23341fe5 --- /dev/null +++ b/test/end-to-end/auth/recover/request.js @@ -0,0 +1,76 @@ +import { StatusCodes } from "http-status-codes"; +import ValidatorTester from "../../../utils/ValidatorTester"; +import { RECOVERY_LINK_EXPIRATION } from "../../../../src/models/constants/Account"; +import EmailService from "../../../../src/lib/emailService"; +import * as token from "../../../../src/lib/token"; +import { REQUEST_ACCOUNT_RECOVERY } from "../../../../src/email-templates/accountManagement"; +import env from "../../../../src/config/env"; +import Account from "../../../../src/models/Account"; +import hash from "../../../../src/lib/passwordHashing"; + +const generateTokenSpy = jest.spyOn(token, "generateToken"); +jest.spyOn(token, "verifyAndDecodeToken"); + +describe("POST /recover/request", () => { + + const test_account = { + email: "recover_email@gmail.com", + password: "password123", + }; + + beforeEach(async () => { + await Account.deleteMany({ email: test_account.email }); + + await Account.create({ + email: test_account.email, + password: await hash(test_account.password), + isAdmin: true, + }); + + jest.clearAllMocks(); + }); + + afterAll(async () => { + await Account.deleteMany({}); + }); + + describe("Input Validation", () => { + describe("email", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/recover/request").send(params)); + const BodyValidatorTester = EndpointValidatorTester("body"); + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + }); + + test("should return ok and not send email nor generate a token if account not found", async () => { + const res = await request() + .post("/auth/recover/request") + .send({ email: "not_valid_email@email.com" }); + + expect(EmailService.sendMail).not.toHaveBeenCalled(); + expect(token.generateToken).not.toHaveBeenCalled(); + + expect(res.status).toBe(StatusCodes.OK); + }); + + test("should generate token and send email if account found", async () => { + await request() + .post("/auth/recover/request") + .send({ email: test_account.email }) + .expect(StatusCodes.OK); + + expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + const generatedToken = generateTokenSpy.mock.results[0].value; + + const emailOptions = REQUEST_ACCOUNT_RECOVERY(`${env.password_recovery_link}/${generatedToken}`); + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_account.email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); +}); diff --git a/test/end-to-end/auth/register.js b/test/end-to-end/auth/register.js new file mode 100644 index 00000000..047ae3cb --- /dev/null +++ b/test/end-to-end/auth/register.js @@ -0,0 +1,90 @@ +import { StatusCodes } from "http-status-codes"; +import Account from "../../../src/models/Account"; +import ValidatorTester from "../../utils/ValidatorTester"; +import withGodToken from "../../utils/GodToken"; +import AccountConstants from "../../../src/models/constants/Account"; +import { ErrorTypes } from "../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../src/api/middleware/validators/validationReasons"; + +describe("POST /auth/register", () => { + + describe("Input Validation", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/register").send(withGodToken(params))); + const BodyValidatorTester = EndpointValidatorTester("body"); + describe("email", () => { + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + + describe("password", () => { + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); + FieldValidatorTester.hasNumber(); + }); + }); + + describe("Without pre-existing users", () => { + beforeAll(async () => { + await Account.deleteMany({}); + }); + + afterAll(async () => { + await Account.deleteMany({}); + }); + + test("Should return forbidden if attempting to register an account while not providing the god token", async () => { + const user = { + email: "user@email.com", + password: "password123", + }; + + const res = await request() + .post("/auth/register") + .send(user); + + expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + }); + + test("Should make a successful registration if god token given and email not in use", async () => { + const user = { + email: "user@email.com", + password: "password123", + }; + + const res = await request() + .post("/auth/register") + .send(withGodToken(user)); + + expect(res.status).toBe(StatusCodes.OK); + + const registered_user = await Account.findOne({ email: user.email }); + expect(registered_user).toBeDefined(); + expect(registered_user).toHaveProperty("email", user.email); + }); + + test("should return an error when registering with an already existing email", async () => { + + const test_user = { + email: "user@email.com", + password: "password123", + }; + + const res = await request() + .post("/auth/register") + .send(withGodToken(test_user)); + + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.ALREADY_EXISTS("email"), + "param": "email", + "value": test_user.email, + }); + }); + }); +}); From 1bde8de8a313facb48786c23775a02920d835420 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 28 Mar 2023 17:44:04 +0100 Subject: [PATCH 02/30] Finished input validation for company application search --- src/api/middleware/validators/application.js | 34 +- src/api/middleware/validators/offer.js | 9 +- .../applications/company/:id/approve.js | 3 + .../applications/company/:id/reject.js | 3 + .../end-to-end/applications/company/search.js | 299 +++++++++++ test/end-to-end/review.js | 492 +++++------------- test/utils/ValidatorTester.js | 48 +- 7 files changed, 503 insertions(+), 385 deletions(-) create mode 100644 test/end-to-end/applications/company/:id/approve.js create mode 100644 test/end-to-end/applications/company/:id/reject.js create mode 100644 test/end-to-end/applications/company/search.js diff --git a/src/api/middleware/validators/application.js b/src/api/middleware/validators/application.js index 59cd85c9..38b3f1d7 100644 --- a/src/api/middleware/validators/application.js +++ b/src/api/middleware/validators/application.js @@ -60,6 +60,13 @@ export const reject = useExpressValidators([ .withMessage(ValidationReasons.TOO_SHORT(CompanyApplicationConstants.rejectReason.min_length)), ]); +const isAfterSubmissionDateFrom = (submissionDateTo, { req }) => { + + const { submissionDateFrom } = req.body; + + return submissionDateFrom <= submissionDateTo; +}; + const sortByParamValidator = (val) => { const regex = /^(\w+(:(desc|asc))?)(,\w+(:(desc|asc))?)*$/; @@ -84,31 +91,38 @@ const parseSortByField = (val) => val.split(","); export const search = useExpressValidators([ query("limit", ValidationReasons.DEFAULT) .optional() - .isInt({ min: 1, max: MAX_LIMIT_RESULTS }) - .withMessage(ValidationReasons.MAX(MAX_LIMIT_RESULTS)), + .isInt().withMessage(ValidationReasons.INT).bail() + .toInt() + .isInt({ min: 1 }).withMessage(ValidationReasons.MIN(1)).bail() + .isInt({ max: MAX_LIMIT_RESULTS }).withMessage(ValidationReasons.MAX(MAX_LIMIT_RESULTS)).bail() + .toInt(), query("offset", ValidationReasons.DEFAULT) .optional() - .isInt({ min: 0 }) - .withMessage(ValidationReasons.MIN(0)), + .isInt().withMessage(ValidationReasons.INT).bail() + .toInt() + .isInt({ min: 0 }).withMessage(ValidationReasons.MIN(0)).bail() + .toInt(), query("companyName", ValidationReasons.DEFAULT) .optional() - .isString().withMessage(ValidationReasons.STRING), + .isString().withMessage(ValidationReasons.STRING).bail(), query("state", ValidationReasons.DEFAULT) .optional() - .customSanitizer(ensureArray) .isArray().withMessage(ValidationReasons.ARRAY).bail() + .customSanitizer(ensureArray) .custom(valuesInSet(Object.keys(ApplicationStatus))), query("submissionDateFrom", ValidationReasons.DEFAULT) .optional() - .toDate() - .isISO8601().withMessage(ValidationReasons.DATE), + .isISO8601().withMessage(ValidationReasons.DATE).bail() + .toDate(), query("submissionDateTo", ValidationReasons.DEFAULT) .optional() + .isISO8601().withMessage(ValidationReasons.DATE).bail() .toDate() - .isISO8601().withMessage(ValidationReasons.DATE), + .if((submissionDateTo, { req }) => req.query.submissionDateFrom !== undefined) + .custom(isAfterSubmissionDateFrom).withMessage(ValidationReasons.MUST_BE_AFTER("submissionDateFrom")), query("sortBy", ValidationReasons.DEFAULT) .optional() - .isString().withMessage(ValidationReasons.STRING) + .isString().withMessage(ValidationReasons.STRING).bail() .custom(sortByParamValidator) .customSanitizer(parseSortByField), ]); diff --git a/src/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js index c306f099..ad76ce25 100644 --- a/src/api/middleware/validators/offer.js +++ b/src/api/middleware/validators/offer.js @@ -80,7 +80,6 @@ export const create = useExpressValidators([ .custom(publishEndDateAfterPublishDate) .custom(publishEndDateLimit), - body("jobMinDuration", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT), @@ -119,7 +118,7 @@ export const create = useExpressValidators([ body("jobType", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(JobTypes).withMessage(ValidationReasons.IN_ARRAY(JobTypes)), + .isIn(JobTypes).withMessage((value) => ValidationReasons.IN_ARRAY(JobTypes, value)), body("fields", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() @@ -388,7 +387,7 @@ export const edit = useExpressValidators([ body("jobType", ValidationReasons.DEFAULT) .optional() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(JobTypes).withMessage(ValidationReasons.IN_ARRAY(JobTypes)), + .isIn(JobTypes).withMessage((value) => ValidationReasons.IN_ARRAY(JobTypes, value)), body("fields", ValidationReasons.DEFAULT) .optional() @@ -495,7 +494,7 @@ export const get = useExpressValidators([ query("jobType") .optional() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(JobTypes).withMessage(ValidationReasons.IN_ARRAY(JobTypes)), + .isIn(JobTypes).withMessage((value) => ValidationReasons.IN_ARRAY(JobTypes, value)), query("jobMinDuration", ValidationReasons.DEFAULT) .optional() @@ -522,7 +521,7 @@ export const get = useExpressValidators([ query("sortBy", ValidationReasons.DEFAULT) .optional() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(OfferConstants.SortableFields).withMessage(ValidationReasons.IN_ARRAY(OfferConstants.SortableFields)), + .isIn(OfferConstants.SortableFields).withMessage((value) => ValidationReasons.IN_ARRAY(OfferConstants.SortableFields, value)), query("descending", ValidationReasons.DEFAULT) .optional() diff --git a/test/end-to-end/applications/company/:id/approve.js b/test/end-to-end/applications/company/:id/approve.js new file mode 100644 index 00000000..8fc357bf --- /dev/null +++ b/test/end-to-end/applications/company/:id/approve.js @@ -0,0 +1,3 @@ +test("should return true", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/applications/company/:id/reject.js b/test/end-to-end/applications/company/:id/reject.js new file mode 100644 index 00000000..8fc357bf --- /dev/null +++ b/test/end-to-end/applications/company/:id/reject.js @@ -0,0 +1,3 @@ +test("should return true", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/applications/company/search.js b/test/end-to-end/applications/company/search.js new file mode 100644 index 00000000..16627df0 --- /dev/null +++ b/test/end-to-end/applications/company/search.js @@ -0,0 +1,299 @@ +import { StatusCodes } from "http-status-codes"; +import CompanyApplication, { CompanyApplicationProps } from "../../../../src/models/CompanyApplication"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import ApplicationStatus from "../../../../src/models/constants/ApplicationStatus"; + +import { MAX_LIMIT_RESULTS } from "../../../../src/api/middleware/validators/application"; + +import ValidatorTester from "../../../utils/ValidatorTester"; +// import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; + +describe("GET /applications/company/search", () => { + + const test_agent = agent(); + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const pendingApplication = { + email: "test2@test.com", + password: "password123", + companyName: "testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25").toISOString(), + }; + + const approvedApplication = { + ...pendingApplication, + submittedAt: new Date("2019-11-24").toISOString(), + approvedAt: new Date(Date.parse(pendingApplication.submittedAt) + (24 * 60 * 60 * 1000)).toISOString(), + companyName: "approved Testing company", + email: `approved${pendingApplication.email}`, + }; + + const rejectedApplication = { + ...pendingApplication, + submittedAt: new Date("2019-11-23").toISOString(), + rejectedAt: new Date(Date.parse(pendingApplication.submittedAt) + (24 * 60 * 60 * 1000)).toISOString(), + companyName: "rejected Testing company", + email: `rejected${pendingApplication.email}`, + rejectReason: "2bad4nij0bs", + }; + + beforeAll(async () => { + await Account.deleteMany({}); + await Account.create({ email: test_user_admin.email, password: await hash(test_user_admin.password), isAdmin: true }); + }); + + beforeEach(async () => { + await CompanyApplication.deleteMany({}); + + // Login by default + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + describe("Input Validation", () => { + + const EndpointValidatorTester = ValidatorTester( + (params) => test_agent.get("/applications/company/search").query(params) + ); + const QueryValidatorTester = EndpointValidatorTester("query"); + + describe("limit", () => { + const FieldValidatorTester = QueryValidatorTester("limit"); + FieldValidatorTester.mustBeNumber(); + FieldValidatorTester.mustBeGreaterThanOrEqualTo(1); + FieldValidatorTester.mustBeLessThanOrEqualTo(MAX_LIMIT_RESULTS); + }); + + describe("offset", () => { + const FieldValidatorTester = QueryValidatorTester("offset"); + FieldValidatorTester.mustBeNumber(); + FieldValidatorTester.mustBeGreaterThanOrEqualTo(0); + }); + + describe("companyName", () => { + // the only validation that could be done on this is to test if the value is a string. + // However, since this is coming from the query, it is always parsed as a string, so this check would never be exercised + }); + + describe("state", () => { + const FieldValidatorTester = QueryValidatorTester("state"); + FieldValidatorTester.mustBeArray(); + }); + + describe("submissionDateFrom", () => { + const FieldValidatorTester = QueryValidatorTester("submissionDateFrom"); + FieldValidatorTester.mustBeDate(); + }); + + describe("submissionDateTo", () => { + const FieldValidatorTester = QueryValidatorTester("submissionDateTo"); + FieldValidatorTester.mustBeDate(); + FieldValidatorTester.mustBeAfter("submissionDateFrom"); + }); + + describe("sortBy", () => { + const FieldValidatorTester = QueryValidatorTester("sortBy"); + // FieldValidatorTester.mustBeString(); Same reason as above + + // Validation for this is harder to perform since there is custom validation employed + // Perhaps we could employ a custom test, leaving as TODO + FieldValidatorTester.mustBeInArray(Object.keys(CompanyApplicationProps)); + }); + }); + + test("Should fail to search company applications if not logged in", async () => { + + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + + const res = await request() + .get("/applications/company/search"); + + expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + }); + + test("Should return empty list if no applications exist", async () => { + const emptyRes = await test_agent + .get("/applications/company/search"); + + expect(emptyRes.status).toBe(StatusCodes.OK); + expect(emptyRes.body.applications).toEqual([]); + }); + + test("Should list existing applications", async () => { + const application = { + email: "test2@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + }; + + await CompanyApplication.create({ + ...application, + submittedAt: Date.now(), + }); + + const nonEmptyRes = await test_agent + .get("/applications/company/search"); + + expect(nonEmptyRes.status).toBe(StatusCodes.OK); + expect(nonEmptyRes.body.applications.length).toBe(1); + expect(nonEmptyRes.body.applications[0]).toHaveProperty("email", application.email); + + }); + + describe("Filter application results", () => { + + beforeEach(async () => { + await CompanyApplication.create([pendingApplication, approvedApplication, rejectedApplication]); + }); + + afterEach(async () => { + await CompanyApplication.deleteMany({}); + }); + + test("Should filter by company name", async () => { + const fullNameQuery = await test_agent + .get(`/applications/company/search?companyName=${"approved Testing company"}`); + + expect(fullNameQuery.status).toBe(StatusCodes.OK); + expect(fullNameQuery.body.applications).toHaveLength(1); + expect(fullNameQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + + const partialNameQuery = await test_agent + .get(`/applications/company/search?companyName=${"Testing company"}`); + + expect(partialNameQuery.status).toBe(StatusCodes.OK); + expect(partialNameQuery.body.applications).toHaveLength(3); + expect(partialNameQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(partialNameQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(partialNameQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); + + test("Should filter by state", async () => { + + const wrongFormatQuery = await test_agent + .get(`/applications/company/search?state[]=<["${ApplicationStatus.APPROVED}"]`); + + expect(wrongFormatQuery.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(wrongFormatQuery.body.errors[0]).toStrictEqual({ + location: "query", + msg: "must-be-in:[PENDING,APPROVED,REJECTED]", // FIXME: ValidationReasons.IN_ARRAY(ApplicationStatus), + param: "state", + value: [`<["${ApplicationStatus.APPROVED}"]`] + }); + + const singleStateQuery = await test_agent + .get(`/applications/company/search?state[]=${ApplicationStatus.APPROVED}`) + .expect(StatusCodes.OK); + + expect(singleStateQuery.body.applications.length).toBe(1); + expect(singleStateQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + + const multiStateQuery = await test_agent + .get("/applications/company/search?").query({ state: [ApplicationStatus.APPROVED, ApplicationStatus.PENDING] }); + + expect(multiStateQuery.status).toBe(StatusCodes.OK); + expect(multiStateQuery.body.applications.length).toBe(2); + expect(multiStateQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(multiStateQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should filter by date", async () => { + + const afterQuery = await test_agent + .get(`/applications/company/search?submissionDateFrom=${approvedApplication.submittedAt}`) + .expect(StatusCodes.OK); + + expect(afterQuery.body.applications.length).toBe(2); + expect(afterQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(afterQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + + const untilQuery = await test_agent + .get(`/applications/company/search?submissionDateTo=${approvedApplication.submittedAt}`) + .expect(StatusCodes.OK); + + expect(untilQuery.body.applications.length).toBe(2); + expect(untilQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(untilQuery.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + + const intervalQuery = await test_agent + .get("/applications/company/search?" + + `submissionDateFrom=${approvedApplication.submittedAt}&` + + `submissionDateTo=${approvedApplication.submittedAt}`); + + console.info(intervalQuery.body); + + expect(intervalQuery.status).toBe(StatusCodes.OK); + expect(intervalQuery.body.applications.length).toBe(1); + expect(intervalQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + + }); + }); + + describe("Sort application results", () => { + + beforeEach(async () => { + await CompanyApplication.create([pendingApplication, approvedApplication, rejectedApplication]); + }); + + afterEach(async () => { + await CompanyApplication.deleteMany({}); + }); + + test("Should sort by company name ascending", async () => { + const query = await test_agent + .get("/applications/company/search?sortBy=companyName:asc"); + + expect(query.status).toBe(StatusCodes.OK); + expect(query.body.applications.length).toBe(3); + expect(query.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(query.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); + }); + + test("Should sort by company name descending", async () => { + const query = await test_agent + .get("/applications/company/search?sortBy=companyName:desc"); + + expect(query.status).toBe(StatusCodes.OK); + expect(query.body.applications.length).toBe(3); + expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(query.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should sort by submissionDate descending", async () => { + const defaultQuery = await test_agent + .get("/applications/company/search"); + + expect(defaultQuery.status).toBe(StatusCodes.OK); + expect(defaultQuery.body.applications.length).toBe(3); + expect(defaultQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(defaultQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(defaultQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + + const query = await test_agent + .get("/applications/company/search?sortBy=submittedAt:desc"); + + expect(query.status).toBe(StatusCodes.OK); + expect(query.body.applications.length).toBe(3); + expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(query.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(query.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); + }); +}); diff --git a/test/end-to-end/review.js b/test/end-to-end/review.js index aab25cdc..5405af92 100644 --- a/test/end-to-end/review.js +++ b/test/end-to-end/review.js @@ -1,454 +1,212 @@ jest.mock("../../src/lib/emailService"); import EmailService, { EmailService as EmailServiceClass } from "../../src/lib/emailService"; jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); -import { StatusCodes as HTTPStatus } from "http-status-codes"; +import { StatusCodes } from "http-status-codes"; import CompanyApplication, { CompanyApplicationRules } from "../../src/models/CompanyApplication"; -import hash from "../../src/lib/passwordHashing"; import Account from "../../src/models/Account"; import { ErrorTypes } from "../../src/api/middleware/errorHandler"; import ApplicationStatus from "../../src/models/constants/ApplicationStatus"; import { APPROVAL_NOTIFICATION, REJECTION_NOTIFICATION } from "../../src/email-templates/companyApplicationApproval"; import mongoose from "mongoose"; +import hash from "../../src/lib/passwordHashing"; const { ObjectId } = mongoose.Types; describe("Company application review endpoint test", () => { - describe("/applications/company", () => { - - describe("Without Auth", () => { - beforeEach(async () => { - await CompanyApplication.deleteMany({}); - }); + const test_agent = agent(); + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; - test("Should return HTTP 401 error", async () => { - const emptyRes = await request() - .get("/applications/company/search"); + beforeAll(async () => { + await Account.deleteMany({}); + await Account.create({ email: test_user_admin.email, password: await hash(test_user_admin.password), isAdmin: true }); + }); - expect(emptyRes.status).toBe(HTTPStatus.UNAUTHORIZED); - }); + beforeEach(async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); - }); + describe("/applications/company", () => { - describe("With Auth", () => { - const test_agent = agent(); - const test_user = { - email: "user@email.com", + describe("Approval/Rejection", () => { + let application; + const pendingApplication = { + email: "test2@test.com", password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), }; - beforeAll(async () => { - await Account.deleteMany({}); - await Account.create({ email: test_user.email, password: await hash(test_user.password), isAdmin: true }); - - // Login - await test_agent - .post("/auth/login") - .send(test_user) - .expect(200); - }); - - beforeEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should list existing applications", async () => { - const emptyRes = await test_agent - .get("/applications/company/search"); - - expect(emptyRes.status).toBe(HTTPStatus.OK); - expect(emptyRes.body.applications).toEqual([]); - - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - }; - - await CompanyApplication.create({ - ...application, - submittedAt: Date.now(), - }); - - const nonEmptyRes = await test_agent - .get("/applications/company/search"); - - expect(nonEmptyRes.status).toBe(HTTPStatus.OK); - expect(nonEmptyRes.body.applications.length).toBe(1); - expect(nonEmptyRes.body.applications[0]).toHaveProperty("email", application.email); - }); - - describe("Filter application results", () => { - - const pendingApplication = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - const approvedApplication = { - ...pendingApplication, - submittedAt: new Date("2019-11-24"), - approvedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "approved Testing company", - email: `approved${pendingApplication.email}`, - }; - const rejectedApplication = { ...pendingApplication, - submittedAt: new Date("2019-11-23"), - rejectedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "rejected Testing company", - email: `rejected${pendingApplication.email}`, - rejectReason: "2bad4nij0bs", - }; + describe("Approve application", () => { beforeEach(async () => { - await CompanyApplication.create(pendingApplication); - await CompanyApplication.create(approvedApplication); - await CompanyApplication.create(rejectedApplication); + await Account.deleteMany({ email: pendingApplication.email }); + application = await CompanyApplication.create(pendingApplication); }); afterEach(async () => { await CompanyApplication.deleteMany({}); }); - test("Should filter by company name", async () => { - const fullNameQuery = await test_agent - .get(`/applications/company/search?companyName=${"approved Testing company"}`); - - expect(fullNameQuery.status).toBe(HTTPStatus.OK); - expect(fullNameQuery.body.applications.length).toBe(1); - expect(fullNameQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + test("Should approve pending application", async () => { - const partialNameQuery = await test_agent - .get(`/applications/company/search?companyName=${"Testing company"}`); + const res = await test_agent + .post(`/applications/company/${application._id}/approve`); - expect(partialNameQuery.status).toBe(HTTPStatus.OK); - expect(partialNameQuery.body.applications.length).toBe(3); - expect(partialNameQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(partialNameQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(partialNameQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.email).toBe(pendingApplication.email); + expect(res.body.companyName).toBe(pendingApplication.companyName); }); - test("Should filter by state", async () => { + test("Should send approval email to company email", async () => { - const wrongFormatQuery = await test_agent - .get(`/applications/company/search?state=<["${ApplicationStatus.APPROVED}"]`); + const res = await test_agent + .post(`/applications/company/${application._id}/approve`); - expect(wrongFormatQuery.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(wrongFormatQuery.body.errors[0]).toStrictEqual({ - location: "query", - msg: "must-be-in:[PENDING,APPROVED,REJECTED]", - param: "state", - value: [`<["${ApplicationStatus.APPROVED}"]`] - }); + expect(res.status).toBe(StatusCodes.OK); + + const emailOptions = APPROVAL_NOTIFICATION(application.companyName); + expect(EmailService.sendMail).toHaveBeenCalledWith({ + subject: emailOptions.subject, + to: application.email, + template: emailOptions.template, + context: emailOptions.context, + }); - const singleStateQuery = await test_agent - .get("/applications/company/search").query({ state: [ApplicationStatus.APPROVED] }); + }); - expect(singleStateQuery.status).toBe(HTTPStatus.OK); - expect(singleStateQuery.body.applications.length).toBe(1); - expect(singleStateQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + test("Should fail if trying to approve inexistent application", async () => { - const multiStateQuery = await test_agent - .get("/applications/company/search?").query({ state: [ApplicationStatus.APPROVED, ApplicationStatus.PENDING] }); + const res = await test_agent + .post(`/applications/company/${new ObjectId()}/approve`); - expect(multiStateQuery.status).toBe(HTTPStatus.OK); - expect(multiStateQuery.body.applications.length).toBe(2); - expect(multiStateQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(multiStateQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.status).toBe(StatusCodes.NOT_FOUND); }); - test("Should filter by date", async () => { + test("Should fail if trying to approve already approved application", async () => { + await test_agent + .post(`/applications/company/${application._id}/approve`); + + const res = await test_agent + .post(`/applications/company/${application._id}/approve`); + + expect(res.status).toBe(StatusCodes.CONFLICT); + }); - const afterQuery = await test_agent - .get(`/applications/company/search?submissionDateFrom=${approvedApplication.submittedAt}`); + test("Should fail if trying to approve already rejected application", async () => { + await test_agent + .post(`/applications/company/${application._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }); - expect(afterQuery.status).toBe(HTTPStatus.OK); - expect(afterQuery.body.applications.length).toBe(2); - expect(afterQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(afterQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - const untilQuery = await test_agent - .get(`/applications/company/search?submissionDateTo=${approvedApplication.submittedAt}`); + const res = await test_agent + .post(`/applications/company/${application._id}/approve`); - expect(untilQuery.status).toBe(HTTPStatus.OK); - expect(untilQuery.body.applications.length).toBe(2); - expect(untilQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(untilQuery.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.status).toBe(StatusCodes.CONFLICT); + }); - const intervalQuery = await test_agent - .get("/applications/company/search?" + - `submissionDateFrom=${approvedApplication.submittedAt}&` + - `submissionDateTo=${approvedApplication.submittedAt}`); + test("Should fail if approving application with an existing account with same email, and then rollback", async () => { + await Account.create({ email: application.email, password: "passwordHashedButNotReally", isAdmin: true }); + const res = await test_agent + .post(`/applications/company/${application._id}/approve`); - expect(intervalQuery.status).toBe(HTTPStatus.OK); - expect(intervalQuery.body.applications.length).toBe(1); - expect(intervalQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.status).toBe(StatusCodes.CONFLICT); + expect(res.body.error_code).toBe(ErrorTypes.VALIDATION_ERROR); + expect(res.body.errors[0].msg).toBe(CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg); + const result_application = await CompanyApplication.findById(application._id); + expect(result_application.state).toBe(ApplicationStatus.PENDING); }); - }); - describe("Sort application results", () => { - - const pendingApplication = { - email: "test2@test.com", - password: "password123", - companyName: "testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - const approvedApplication = { - ...pendingApplication, - submittedAt: new Date("2019-11-24"), - approvedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "approved Testing company", - email: `approved${pendingApplication.email}`, - }; - const rejectedApplication = { ...pendingApplication, - submittedAt: new Date("2019-11-23"), - rejectedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "rejected Testing company", - email: `rejected${pendingApplication.email}`, - rejectReason: "2bad4nij0bs", - }; + describe("Reject application", () => { beforeEach(async () => { - await CompanyApplication.create(pendingApplication); - await CompanyApplication.create(approvedApplication); - await CompanyApplication.create(rejectedApplication); + await Account.deleteMany({ email: pendingApplication.email }); + application = await CompanyApplication.create(pendingApplication); }); afterEach(async () => { await CompanyApplication.deleteMany({}); }); - test("Should sort by company name ascending", async () => { - const query = await test_agent - .get("/applications/company/search?sortBy=companyName:asc"); - - expect(query.status).toBe(HTTPStatus.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); - }); - - test("Should sort by company name descending", async () => { - const query = await test_agent - .get("/applications/company/search?sortBy=companyName:desc"); + test("Should fail if no rejectReason provided", async () => { + const res = await test_agent + .post(`/applications/company/${application._id}/reject`); + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body.errors[0]).toStrictEqual({ location: "body", msg: "required", param: "rejectReason" }); - expect(query.status).toBe(HTTPStatus.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); }); - test("Should sort by submissionDate descending", async () => { - const defaultQuery = await test_agent - .get("/applications/company/search"); + test("Should reject pending application", async () => { + const res = await test_agent + .post(`/applications/company/${application._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }); - expect(defaultQuery.status).toBe(HTTPStatus.OK); - expect(defaultQuery.body.applications.length).toBe(3); - expect(defaultQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(defaultQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(defaultQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); - - const query = await test_agent - .get("/applications/company/search?sortBy=submittedAt:desc"); - - expect(query.status).toBe(HTTPStatus.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.email).toBe(pendingApplication.email); + expect(res.body.companyName).toBe(pendingApplication.companyName); }); - }); - - describe("Approval/Rejection", () => { - let application; - const pendingApplication = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; + test("Should send rejection email to company email", async () => { + const res = await test_agent + .post(`/applications/company/${application._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }); - describe("Approve application", () => { + expect(res.status).toBe(StatusCodes.OK); - beforeEach(async () => { - await Account.deleteMany({ email: pendingApplication.email }); - application = await CompanyApplication.create(pendingApplication); - }); + const emailOptions = REJECTION_NOTIFICATION(application.companyName); - afterEach(async () => { - await CompanyApplication.deleteMany({}); + expect(EmailService.sendMail).toHaveBeenCalledWith({ + subject: emailOptions.subject, + to: application.email, + template: emailOptions.template, + context: emailOptions.context, }); - test("Should approve pending application", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.email).toBe(pendingApplication.email); - expect(res.body.companyName).toBe(pendingApplication.companyName); - }); - - test("Should send approval email to company email", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.OK); - - const emailOptions = APPROVAL_NOTIFICATION(application.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: application.email, - template: emailOptions.template, - context: emailOptions.context, - }); - - }); - - test("Should fail if trying to approve inexistent application", async () => { - - const res = await test_agent - .post(`/applications/company/${new ObjectId()}/approve`); - - expect(res.status).toBe(HTTPStatus.NOT_FOUND); - }); - - test("Should fail if trying to approve already approved application", async () => { - await test_agent - .post(`/applications/company/${application._id}/approve`); - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); - - test("Should fail if trying to approve already rejected application", async () => { - await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); - - test("Should fail if approving application with an existing account with same email, and then rollback", async () => { - await Account.create({ email: application.email, password: "passwordHashedButNotReally", isAdmin: true }); - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - expect(res.body.error_code).toBe(ErrorTypes.VALIDATION_ERROR); - expect(res.body.errors[0].msg).toBe(CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg); - - const result_application = await CompanyApplication.findById(application._id); - expect(result_application.state).toBe(ApplicationStatus.PENDING); - }); }); - describe("Reject application", () => { - - beforeEach(async () => { - await Account.deleteMany({ email: pendingApplication.email }); - application = await CompanyApplication.create(pendingApplication); - }); - - afterEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should fail if no rejectReason provided", async () => { - const res = await test_agent - .post(`/applications/company/${application._id}/reject`); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body.errors[0]).toStrictEqual({ location: "body", msg: "required", param: "rejectReason" }); - - }); - - test("Should reject pending application", async () => { - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.email).toBe(pendingApplication.email); - expect(res.body.companyName).toBe(pendingApplication.companyName); - }); - - test("Should send rejection email to company email", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); + test("Should fail if trying to reject inexistent application", async () => { + const res = await test_agent + .post(`/applications/company/${new ObjectId()}/reject`) + .send({ rejectReason: "Some reason which is valid" }); - expect(res.status).toBe(HTTPStatus.OK); - - const emailOptions = REJECTION_NOTIFICATION(application.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: application.email, - template: emailOptions.template, - context: emailOptions.context, - }); - - }); - - test("Should fail if trying to reject inexistent application", async () => { - const res = await test_agent - .post(`/applications/company/${new ObjectId()}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(HTTPStatus.NOT_FOUND); - }); + expect(res.status).toBe(StatusCodes.NOT_FOUND); + }); - test("Should fail if trying to reject already approved application", async () => { - await test_agent - .post(`/applications/company/${application._id}/approve`); + test("Should fail if trying to reject already approved application", async () => { + await test_agent + .post(`/applications/company/${application._id}/approve`); - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); + const res = await test_agent + .post(`/applications/company/${application._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }); - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); + expect(res.status).toBe(StatusCodes.CONFLICT); + }); - test("Should fail if trying to reject already rejected application", async () => { - await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); + test("Should fail if trying to reject already rejected application", async () => { + await test_agent + .post(`/applications/company/${application._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }); - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); + const res = await test_agent + .post(`/applications/company/${application._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }); - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); + expect(res.status).toBe(StatusCodes.CONFLICT); }); }); }); diff --git a/test/utils/ValidatorTester.js b/test/utils/ValidatorTester.js index f519c2a9..da875e47 100644 --- a/test/utils/ValidatorTester.js +++ b/test/utils/ValidatorTester.js @@ -88,7 +88,7 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ "location": location, "msg": ValidationReasons.DATE, "param": field_name, - "value": params[field_name], + "value": location === "query" ? params[field_name].toString() : params[field_name], }); }); }); @@ -172,10 +172,32 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ }); }, + mustBeArray: () => { + test("should be array", async () => { + const params = { + [field_name]: "not_an_array", + }; + const res = await requestEndpoint(params); + + executeValidatorTestWithContext({ requestEndpoint, location, field_name }, () => { + checkCommonErrorResponse(res); + expect(res.body.errors).toContainEqual({ + "location": location, + "msg": ValidationReasons.ARRAY, + "param": field_name, + "value": params[field_name], + }); + }); + }); + }, + mustBeInArray: (array) => { test(`should be one of: [${array}]`, async () => { + + const value = "not_in_array"; + const params = { - [field_name]: "not_in_array", + [field_name]: value, }; const res = await requestEndpoint(params); @@ -183,7 +205,7 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ checkCommonErrorResponse(res); expect(res.body.errors).toContainEqual({ "location": location, - "msg": ValidationReasons.IN_ARRAY(array), + "msg": ValidationReasons.IN_ARRAY(array, value), "param": field_name, "value": params[field_name], }); @@ -353,6 +375,26 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ }); }, + mustBeLessThanOrEqualTo: (max) => { + test(`should be less than or equal to ${max}`, async () => { + const params = { + [field_name]: max + 1, + }; + + const res = await requestEndpoint(params); + + executeValidatorTestWithContext({ requestEndpoint, location, field_name }, () => { + checkCommonErrorResponse(res); + expect(res.body.errors).toContainEqual({ + "location": location, + "msg": ValidationReasons.MAX(max), + "param": field_name, + "value": params[field_name], + }); + }); + }); + }, + mustBeGreaterThanOrEqualToField: (field_name2) => { test(`should be greater than or equal to ${field_name2}`, async () => { const params = { From 456e77a4083d7ce8e2b3462abcaab7c63bfa5dec Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sat, 1 Apr 2023 18:25:07 +0100 Subject: [PATCH 03/30] Fixed wrong validation, refactored some more tests --- src/api/middleware/validators/application.js | 12 +- .../end-to-end/applications/company/search.js | 201 +++++++++++------- 2 files changed, 136 insertions(+), 77 deletions(-) diff --git a/src/api/middleware/validators/application.js b/src/api/middleware/validators/application.js index 38b3f1d7..49df3afd 100644 --- a/src/api/middleware/validators/application.js +++ b/src/api/middleware/validators/application.js @@ -62,7 +62,10 @@ export const reject = useExpressValidators([ const isAfterSubmissionDateFrom = (submissionDateTo, { req }) => { - const { submissionDateFrom } = req.body; + const { submissionDateFrom } = req.query; + + console.info(req.body); + console.info("Dates:", `\n\tFrom: ${submissionDateFrom};\n\tTo: ${submissionDateTo};`); return submissionDateFrom <= submissionDateTo; }; @@ -93,6 +96,11 @@ export const search = useExpressValidators([ .optional() .isInt().withMessage(ValidationReasons.INT).bail() .toInt() + /* + Split validation checks in order to provide better error messages. + Another solution would be to return a "compound" error message, aka, one that contains both pieces of information. + The latter could help keep validation chains smaller. + */ .isInt({ min: 1 }).withMessage(ValidationReasons.MIN(1)).bail() .isInt({ max: MAX_LIMIT_RESULTS }).withMessage(ValidationReasons.MAX(MAX_LIMIT_RESULTS)).bail() .toInt(), @@ -118,7 +126,7 @@ export const search = useExpressValidators([ .optional() .isISO8601().withMessage(ValidationReasons.DATE).bail() .toDate() - .if((submissionDateTo, { req }) => req.query.submissionDateFrom !== undefined) + .if((_, { req }) => req.query.submissionDateFrom !== undefined) .custom(isAfterSubmissionDateFrom).withMessage(ValidationReasons.MUST_BE_AFTER("submissionDateFrom")), query("sortBy", ValidationReasons.DEFAULT) .optional() diff --git a/test/end-to-end/applications/company/search.js b/test/end-to-end/applications/company/search.js index 16627df0..a67db276 100644 --- a/test/end-to-end/applications/company/search.js +++ b/test/end-to-end/applications/company/search.js @@ -12,6 +12,7 @@ import ValidatorTester from "../../../utils/ValidatorTester"; describe("GET /applications/company/search", () => { const test_agent = agent(); + const test_user_admin = { email: "admin@email.com", password: "password123", @@ -44,12 +45,14 @@ describe("GET /applications/company/search", () => { beforeAll(async () => { await Account.deleteMany({}); - await Account.create({ email: test_user_admin.email, password: await hash(test_user_admin.password), isAdmin: true }); + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); }); beforeEach(async () => { - await CompanyApplication.deleteMany({}); - // Login by default await test_agent .post("/auth/login") @@ -119,30 +122,25 @@ describe("GET /applications/company/search", () => { .delete("/auth/login") .expect(StatusCodes.OK); - const res = await request() - .get("/applications/company/search"); - - expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + await request() + .get("/applications/company/search") + .expect(StatusCodes.UNAUTHORIZED); }); test("Should return empty list if no applications exist", async () => { + + await CompanyApplication.deleteMany({}); + const emptyRes = await test_agent - .get("/applications/company/search"); + .get("/applications/company/search") + .expect(StatusCodes.OK); - expect(emptyRes.status).toBe(StatusCodes.OK); expect(emptyRes.body.applications).toEqual([]); }); test("Should list existing applications", async () => { - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - }; - await CompanyApplication.create({ - ...application, + ...pendingApplication, submittedAt: Date.now(), }); @@ -151,12 +149,19 @@ describe("GET /applications/company/search", () => { expect(nonEmptyRes.status).toBe(StatusCodes.OK); expect(nonEmptyRes.body.applications.length).toBe(1); - expect(nonEmptyRes.body.applications[0]).toHaveProperty("email", application.email); - + expect(nonEmptyRes.body.applications[0]).toHaveProperty("email", pendingApplication.email); }); describe("Filter application results", () => { + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + beforeEach(async () => { await CompanyApplication.create([pendingApplication, approvedApplication, rejectedApplication]); }); @@ -165,87 +170,133 @@ describe("GET /applications/company/search", () => { await CompanyApplication.deleteMany({}); }); - test("Should filter by company name", async () => { - const fullNameQuery = await test_agent - .get(`/applications/company/search?companyName=${"approved Testing company"}`); + describe("Should filter by company name", () => { - expect(fullNameQuery.status).toBe(StatusCodes.OK); - expect(fullNameQuery.body.applications).toHaveLength(1); - expect(fullNameQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + test("Should filter by company name with full name query", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + companyName: approvedApplication.companyName + }) + .expect(StatusCodes.OK); - const partialNameQuery = await test_agent - .get(`/applications/company/search?companyName=${"Testing company"}`); + expect(res.body.applications).toHaveLength(1); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + }); - expect(partialNameQuery.status).toBe(StatusCodes.OK); - expect(partialNameQuery.body.applications).toHaveLength(3); - expect(partialNameQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(partialNameQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(partialNameQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + test("Should filter by company name with partial name query", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + companyName: "Testing company" + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); }); - test("Should filter by state", async () => { + describe("Should filter by state", () => { - const wrongFormatQuery = await test_agent - .get(`/applications/company/search?state[]=<["${ApplicationStatus.APPROVED}"]`); + test("Should fail with badly formatted query", async () => { - expect(wrongFormatQuery.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); - expect(wrongFormatQuery.body.errors[0]).toStrictEqual({ - location: "query", - msg: "must-be-in:[PENDING,APPROVED,REJECTED]", // FIXME: ValidationReasons.IN_ARRAY(ApplicationStatus), - param: "state", - value: [`<["${ApplicationStatus.APPROVED}"]`] + const wrongFormatQuery = await test_agent + // FIXME: having only one element makes it so that state is parsed as a single value + .get(`/applications/company/search?state[]=<["${ApplicationStatus.APPROVED}"]`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(wrongFormatQuery.body.errors[0]).toStrictEqual({ + location: "query", + msg: "must-be-in:[PENDING,APPROVED,REJECTED]", // FIXME: ValidationReasons.IN_ARRAY(ApplicationStatus), + param: "state", + value: [`<["${ApplicationStatus.APPROVED}"]`] + }); }); - const singleStateQuery = await test_agent - .get(`/applications/company/search?state[]=${ApplicationStatus.APPROVED}`) - .expect(StatusCodes.OK); + test("Should succeed with single state query", async () => { - expect(singleStateQuery.body.applications.length).toBe(1); - expect(singleStateQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + const singleStateQuery = await test_agent + .get(`/applications/company/search?state[]=${ApplicationStatus.APPROVED}`) + .expect(StatusCodes.OK); - const multiStateQuery = await test_agent - .get("/applications/company/search?").query({ state: [ApplicationStatus.APPROVED, ApplicationStatus.PENDING] }); + expect(singleStateQuery.body.applications.length).toBe(1); + expect(singleStateQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(multiStateQuery.status).toBe(StatusCodes.OK); - expect(multiStateQuery.body.applications.length).toBe(2); - expect(multiStateQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(multiStateQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - }); - - test("Should filter by date", async () => { + }); - const afterQuery = await test_agent - .get(`/applications/company/search?submissionDateFrom=${approvedApplication.submittedAt}`) - .expect(StatusCodes.OK); + test("Should succeed with multi state query", async () => { + const multiStateQuery = await test_agent + .get("/applications/company/search") + .query({ + state: [ + ApplicationStatus.APPROVED, + ApplicationStatus.PENDING + ] + }) + .expect(StatusCodes.OK); + + expect(multiStateQuery.body.applications.length).toBe(2); + expect(multiStateQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(multiStateQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + }); + }); - expect(afterQuery.body.applications.length).toBe(2); - expect(afterQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(afterQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + describe("Should filter by date", () => { - const untilQuery = await test_agent - .get(`/applications/company/search?submissionDateTo=${approvedApplication.submittedAt}`) - .expect(StatusCodes.OK); + test("Should succeed when searching after date", async () => { + const afterQuery = await test_agent + .get("/applications/company/search") + .query({ + submissionDateFrom: approvedApplication.submittedAt + }) + .expect(StatusCodes.OK); - expect(untilQuery.body.applications.length).toBe(2); - expect(untilQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(untilQuery.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(afterQuery.body.applications.length).toBe(2); + expect(afterQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(afterQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + }); - const intervalQuery = await test_agent - .get("/applications/company/search?" + - `submissionDateFrom=${approvedApplication.submittedAt}&` + - `submissionDateTo=${approvedApplication.submittedAt}`); + test("Should succeed when searching before date", async () => { + const untilQuery = await test_agent + .get("/applications/company/search") + .query({ + submissionDateTo: approvedApplication.submittedAt + }) + .expect(StatusCodes.OK); + + expect(untilQuery.body.applications.length).toBe(2); + expect(untilQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(untilQuery.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + }); - console.info(intervalQuery.body); + test("Should succeed when searching between dates", async () => { + const intervalQuery = await test_agent + .get("/applications/company/search?" + + `submissionDateFrom=${approvedApplication.submittedAt}&` + + `submissionDateTo=${approvedApplication.submittedAt}`); - expect(intervalQuery.status).toBe(StatusCodes.OK); - expect(intervalQuery.body.applications.length).toBe(1); - expect(intervalQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + console.info(intervalQuery.body); // TODO: bruh ? + expect(intervalQuery.status).toBe(StatusCodes.OK); + expect(intervalQuery.body.applications.length).toBe(1); + expect(intervalQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + }); }); }); describe("Sort application results", () => { + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + beforeEach(async () => { await CompanyApplication.create([pendingApplication, approvedApplication, rejectedApplication]); }); From d2621178706f3a0cebbd5b918307c4e4001fd46e Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sun, 2 Apr 2023 14:23:27 +0100 Subject: [PATCH 04/30] Finished company application search tests --- .../end-to-end/applications/company/search.js | 158 ++++++++++++------ test/end-to-end/auth/login.js | 62 ++++--- test/end-to-end/auth/me.js | 7 +- test/end-to-end/auth/register.js | 1 + 4 files changed, 136 insertions(+), 92 deletions(-) diff --git a/test/end-to-end/applications/company/search.js b/test/end-to-end/applications/company/search.js index a67db276..92271eff 100644 --- a/test/end-to-end/applications/company/search.js +++ b/test/end-to-end/applications/company/search.js @@ -203,12 +203,12 @@ describe("GET /applications/company/search", () => { test("Should fail with badly formatted query", async () => { - const wrongFormatQuery = await test_agent + const res = await test_agent // FIXME: having only one element makes it so that state is parsed as a single value .get(`/applications/company/search?state[]=<["${ApplicationStatus.APPROVED}"]`) .expect(StatusCodes.UNPROCESSABLE_ENTITY); - expect(wrongFormatQuery.body.errors[0]).toStrictEqual({ + expect(res.body.errors[0]).toStrictEqual({ location: "query", msg: "must-be-in:[PENDING,APPROVED,REJECTED]", // FIXME: ValidationReasons.IN_ARRAY(ApplicationStatus), param: "state", @@ -218,17 +218,17 @@ describe("GET /applications/company/search", () => { test("Should succeed with single state query", async () => { - const singleStateQuery = await test_agent + const res = await test_agent .get(`/applications/company/search?state[]=${ApplicationStatus.APPROVED}`) .expect(StatusCodes.OK); - expect(singleStateQuery.body.applications.length).toBe(1); - expect(singleStateQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications.length).toBe(1); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); }); test("Should succeed with multi state query", async () => { - const multiStateQuery = await test_agent + const res = await test_agent .get("/applications/company/search") .query({ state: [ @@ -238,57 +238,57 @@ describe("GET /applications/company/search", () => { }) .expect(StatusCodes.OK); - expect(multiStateQuery.body.applications.length).toBe(2); - expect(multiStateQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(multiStateQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications.length).toBe(2); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); }); }); describe("Should filter by date", () => { test("Should succeed when searching after date", async () => { - const afterQuery = await test_agent + const res = await test_agent .get("/applications/company/search") .query({ submissionDateFrom: approvedApplication.submittedAt }) .expect(StatusCodes.OK); - expect(afterQuery.body.applications.length).toBe(2); - expect(afterQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(afterQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications.length).toBe(2); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); }); test("Should succeed when searching before date", async () => { - const untilQuery = await test_agent + const res = await test_agent .get("/applications/company/search") .query({ submissionDateTo: approvedApplication.submittedAt }) .expect(StatusCodes.OK); - expect(untilQuery.body.applications.length).toBe(2); - expect(untilQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(untilQuery.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications.length).toBe(2); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); }); test("Should succeed when searching between dates", async () => { - const intervalQuery = await test_agent + const res = await test_agent .get("/applications/company/search?" + `submissionDateFrom=${approvedApplication.submittedAt}&` + `submissionDateTo=${approvedApplication.submittedAt}`); - console.info(intervalQuery.body); // TODO: bruh ? - - expect(intervalQuery.status).toBe(StatusCodes.OK); - expect(intervalQuery.body.applications.length).toBe(1); - expect(intervalQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.applications.length).toBe(1); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); }); }); }); describe("Sort application results", () => { + const buildOrderingParam = (field, order = undefined) => `${field}${order ? `:${order}` : ""}`; + beforeAll(async () => { await CompanyApplication.deleteMany({}); }); @@ -305,46 +305,94 @@ describe("GET /applications/company/search", () => { await CompanyApplication.deleteMany({}); }); - test("Should sort by company name ascending", async () => { - const query = await test_agent - .get("/applications/company/search?sortBy=companyName:asc"); + describe("Should sort by company name", () => { - expect(query.status).toBe(StatusCodes.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); - }); + test("Should sort by company name using default ordering", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("companyName") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should sort by company name in descending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("companyName", "desc") + }) + .expect(StatusCodes.OK); - test("Should sort by company name descending", async () => { - const query = await test_agent - .get("/applications/company/search?sortBy=companyName:desc"); + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should sort by company name in ascending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("companyName", "asc") + }) + .expect(StatusCodes.OK); - expect(query.status).toBe(StatusCodes.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); + }); }); - test("Should sort by submissionDate descending", async () => { - const defaultQuery = await test_agent - .get("/applications/company/search"); + describe("Should sort by submission date", () => { - expect(defaultQuery.status).toBe(StatusCodes.OK); - expect(defaultQuery.body.applications.length).toBe(3); - expect(defaultQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(defaultQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(defaultQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + test("Should sort by submission date using default ordering", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("submittedAt") + }) + .expect(StatusCodes.OK); - const query = await test_agent - .get("/applications/company/search?sortBy=submittedAt:desc"); + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); - expect(query.status).toBe(StatusCodes.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + test("Should sort by submission date in descending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("submittedAt", "desc") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); + + test("Should sort by submission date in ascending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("submittedAt", "asc") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); + }); }); }); }); diff --git a/test/end-to-end/auth/login.js b/test/end-to-end/auth/login.js index 3c625b7c..2a9c3c52 100644 --- a/test/end-to-end/auth/login.js +++ b/test/end-to-end/auth/login.js @@ -6,22 +6,6 @@ import withGodToken from "../../utils/GodToken"; import hash from "../../../src/lib/passwordHashing"; describe("POST /auth/login", () => { - describe("Input Validation", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(withGodToken(params))); - const BodyValidatorTester = EndpointValidatorTester("body"); - describe("email", () => { - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - describe("password", () => { - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - }); - }); - const test_agent = agent(); const test_user_admin = { @@ -60,24 +44,38 @@ describe("POST /auth/login", () => { await Company.deleteMany({}); }); + describe("Input Validation", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(withGodToken(params))); + const BodyValidatorTester = EndpointValidatorTester("body"); + + describe("email", () => { + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + + describe("password", () => { + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + }); + }); + test("should fail to login if password is wrong", async () => { - const res = await test_agent + await test_agent .post("/auth/login") .send({ email: "user@gmail.com", password: "password", - }); - - expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + }) + .expect(StatusCodes.UNAUTHORIZED); }); - test("should successfully login with registered account", async () => { - const res = await test_agent + await test_agent .post("/auth/login") - .send(test_user_admin); - - expect(res.status).toBe(StatusCodes.OK); + .send(test_user_admin) + .expect(StatusCodes.OK); }); }); @@ -104,11 +102,10 @@ describe("DELETE /auth/login", () => { }); test("should return OK since the logout is idempotent", async () => { - const res = await test_agent + await test_agent .delete("/auth/login") - .send(); - - expect(res.status).toBe(StatusCodes.OK); + .send() + .expect(StatusCodes.OK); }); test("should be successful when logging out the current user", async () => { @@ -118,10 +115,9 @@ describe("DELETE /auth/login", () => { .send(test_user_admin) .expect(StatusCodes.OK); - const res = await test_agent + await test_agent .delete("/auth/login") - .send(); - - expect(res.status).toBe(StatusCodes.OK); + .send() + .expect(StatusCodes.OK); }); }); diff --git a/test/end-to-end/auth/me.js b/test/end-to-end/auth/me.js index e9b2b60b..24e6977d 100644 --- a/test/end-to-end/auth/me.js +++ b/test/end-to-end/auth/me.js @@ -90,10 +90,9 @@ describe("GET /auth/me", () => { }); test("should return an error since no user is logged in", async () => { - const res = await test_agent + await test_agent .get("/auth/me") - .send(); - - expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + .send() + .expect(StatusCodes.UNAUTHORIZED); }); }); diff --git a/test/end-to-end/auth/register.js b/test/end-to-end/auth/register.js index 047ae3cb..5e470e4d 100644 --- a/test/end-to-end/auth/register.js +++ b/test/end-to-end/auth/register.js @@ -11,6 +11,7 @@ describe("POST /auth/register", () => { describe("Input Validation", () => { const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/register").send(withGodToken(params))); const BodyValidatorTester = EndpointValidatorTester("body"); + describe("email", () => { const FieldValidatorTester = BodyValidatorTester("email"); FieldValidatorTester.isRequired(); From 97d33e23b1be8cb769cab051bf3009d3ed4fe433 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sat, 8 Apr 2023 17:14:05 +0100 Subject: [PATCH 05/30] Finished refactoring application approval tests --- .../applications/company/:id/approve.js | 194 +++++++++++++++++- 1 file changed, 192 insertions(+), 2 deletions(-) diff --git a/test/end-to-end/applications/company/:id/approve.js b/test/end-to-end/applications/company/:id/approve.js index 8fc357bf..089a9810 100644 --- a/test/end-to-end/applications/company/:id/approve.js +++ b/test/end-to-end/applications/company/:id/approve.js @@ -1,3 +1,193 @@ -test("should return true", () => { - expect(true).toBe(true); +jest.mock("../../../../../src/lib/emailService"); +import EmailService, { EmailService as EmailServiceClass } from "../../../../../src/lib/emailService"; +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); +import { StatusCodes } from "http-status-codes"; +import Account from "../../../../../src/models/Account"; +import CompanyApplication, { CompanyApplicationRules } from "../../../../../src/models/CompanyApplication"; +import hash from "../../../../../src/lib/passwordHashing"; +import { ErrorTypes } from "../../../../../src/api/middleware/errorHandler"; +import ApplicationStatus from "../../../../../src/models/constants/ApplicationStatus"; +import { APPROVAL_NOTIFICATION } from "../../../../../src/email-templates/companyApplicationApproval"; + +describe("POST /applications/company/:id/approve", () => { + + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + + await Account.deleteMany({}); + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + beforeEach(async () => { + // default login + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); + + test("Should fail if trying to approve inexistent application", async () => { + + const id = "111111111111111111111111"; + + await test_agent + .post(`/applications/company/${id}/approve`) + .expect(StatusCodes.NOT_FOUND); + + }); + + describe("Without previous applications", () => { + + const pendingApplication1Data = { + email: "pending1@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + }; + const pendingApplication2Data = { + email: "pending2@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + }; + + let pendingApplication1, pendingApplication2; + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + + [ + pendingApplication1, + pendingApplication2, + ] = await CompanyApplication.create([ + pendingApplication1Data, + pendingApplication2Data, + ]); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + test("Should approve pending application", async () => { + + const res = await test_agent + .post(`/applications/company/${pendingApplication1._id}/approve`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("email", pendingApplication1Data.email); + expect(res.body).toHaveProperty("companyName", pendingApplication1Data.companyName); + }); + + test("Should send approval email to company email", async () => { + + await test_agent + .post(`/applications/company/${pendingApplication2._id}/approve`) + .expect(StatusCodes.OK); + + const emailOptions = APPROVAL_NOTIFICATION(pendingApplication2.companyName); + + expect(EmailService.sendMail).toHaveBeenCalledWith({ + subject: emailOptions.subject, + to: pendingApplication2.email, + template: emailOptions.template, + context: emailOptions.context, + }); + }); + }); + + describe("With previous applications", () => { + + const approvedApplicationData = { + email: "approved@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + approvedAt: new Date("2019-11-26"), + rejectReason: null + }; + const rejectedApplicationData = { + email: "rejected@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + rejectedAt: new Date("2019-11-26"), + rejectReason: "test-reason" + }; + + const sameEmail = "some@email.com"; + const sameEmailApplicationData = { + email: sameEmail, + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + }; + + let approvedApplication, rejectedApplication, sameEmailApplication; + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + + [ + approvedApplication, + rejectedApplication, + sameEmailApplication, + ] = await CompanyApplication.create([ + approvedApplicationData, + rejectedApplicationData, + sameEmailApplicationData, + ]); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + test("Should fail if trying to approve already approved application", async () => { + await test_agent + .post(`/applications/company/${approvedApplication._id}/approve`) + .expect(StatusCodes.CONFLICT); + }); + + test("Should fail if trying to approve already rejected application", async () => { + await test_agent + .post(`/applications/company/${rejectedApplication._id}/approve`) + .expect(StatusCodes.CONFLICT); + }); + + test("Should fail if approving application with an existing account with same email, and then rollback", async () => { + await Account.create({ email: sameEmail, password: "passwordHashedButNotReally", isAdmin: true }); + + const res = await test_agent + .post(`/applications/company/${sameEmailApplication._id}/approve`); + + expect(res.status).toBe(StatusCodes.CONFLICT); + expect(res.body.error_code).toBe(ErrorTypes.VALIDATION_ERROR); + expect(res.body.errors[0].msg).toBe(CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg); + + const result_application = await CompanyApplication.findById(sameEmailApplication._id); + expect(result_application.state).toBe(ApplicationStatus.PENDING); + }); + }); }); From 057f58abf9ff5d2363b82682d913e31b00ec90d5 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sat, 8 Apr 2023 17:34:51 +0100 Subject: [PATCH 06/30] Finished refactoring application rejection tests --- .../applications/company/:id/approve.js | 16 +- .../applications/company/:id/reject.js | 183 ++++++++++++++- test/end-to-end/review.js | 214 ------------------ 3 files changed, 191 insertions(+), 222 deletions(-) delete mode 100644 test/end-to-end/review.js diff --git a/test/end-to-end/applications/company/:id/approve.js b/test/end-to-end/applications/company/:id/approve.js index 089a9810..78c7351d 100644 --- a/test/end-to-end/applications/company/:id/approve.js +++ b/test/end-to-end/applications/company/:id/approve.js @@ -1,13 +1,17 @@ jest.mock("../../../../../src/lib/emailService"); -import EmailService, { EmailService as EmailServiceClass } from "../../../../../src/lib/emailService"; -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); import { StatusCodes } from "http-status-codes"; +import { ErrorTypes } from "../../../../../src/api/middleware/errorHandler"; +import { APPROVAL_NOTIFICATION } from "../../../../../src/email-templates/companyApplicationApproval"; +import EmailService, { EmailService as EmailServiceClass } from "../../../../../src/lib/emailService"; +import hash from "../../../../../src/lib/passwordHashing"; import Account from "../../../../../src/models/Account"; import CompanyApplication, { CompanyApplicationRules } from "../../../../../src/models/CompanyApplication"; -import hash from "../../../../../src/lib/passwordHashing"; -import { ErrorTypes } from "../../../../../src/api/middleware/errorHandler"; import ApplicationStatus from "../../../../../src/models/constants/ApplicationStatus"; -import { APPROVAL_NOTIFICATION } from "../../../../../src/email-templates/companyApplicationApproval"; +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); + +import mongoose from "mongoose"; + +const { ObjectId } = mongoose.Types; describe("POST /applications/company/:id/approve", () => { @@ -44,7 +48,7 @@ describe("POST /applications/company/:id/approve", () => { test("Should fail if trying to approve inexistent application", async () => { - const id = "111111111111111111111111"; + const id = new ObjectId(); await test_agent .post(`/applications/company/${id}/approve`) diff --git a/test/end-to-end/applications/company/:id/reject.js b/test/end-to-end/applications/company/:id/reject.js index 8fc357bf..2a147c47 100644 --- a/test/end-to-end/applications/company/:id/reject.js +++ b/test/end-to-end/applications/company/:id/reject.js @@ -1,3 +1,182 @@ -test("should return true", () => { - expect(true).toBe(true); +jest.mock("../../../../../src/lib/emailService"); +import EmailService, { EmailService as EmailServiceClass } from "../../../../../src/lib/emailService"; +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); +import { StatusCodes } from "http-status-codes"; +import Account from "../../../../../src/models/Account"; +import CompanyApplication from "../../../../../src/models/CompanyApplication"; +import hash from "../../../../../src/lib/passwordHashing"; +import { REJECTION_NOTIFICATION } from "../../../../../src/email-templates/companyApplicationApproval"; + +import mongoose from "mongoose"; + +const { ObjectId } = mongoose.Types; + +describe("POST /applications/company/:id/reject", () => { + + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + + await Account.deleteMany({}); + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + beforeEach(async () => { + // default login + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); + + test("Should fail if trying to reject inexistent application", async () => { + + const id = new ObjectId(); + + await test_agent + .post(`/applications/company/${id}/reject`) + .send({ rejectReason: "Some reason which is valid" }) + .expect(StatusCodes.NOT_FOUND); + }); + + describe("Without previous applications", () => { + + const pendingApplication1Data = { + email: "pending1@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + }; + const pendingApplication2Data = { + email: "pending2@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + }; + + let pendingApplication1, pendingApplication2; + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + + [ + pendingApplication1, + pendingApplication2, + ] = await CompanyApplication.create([ + pendingApplication1Data, + pendingApplication2Data, + ]); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + test("Should fail if no rejectReason provided", async () => { + const res = await test_agent + .post(`/applications/company/${pendingApplication1._id}/reject`); + + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body.errors[0]).toStrictEqual({ location: "body", msg: "required", param: "rejectReason" }); + }); + + test("Should reject pending application", async () => { + + const res = await test_agent + .post(`/applications/company/${pendingApplication1._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("email", pendingApplication1Data.email); + expect(res.body).toHaveProperty("companyName", pendingApplication1Data.companyName); + }); + + test("Should send rejection email to company email", async () => { + + await test_agent + .post(`/applications/company/${pendingApplication2._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }) + .expect(StatusCodes.OK); + + const emailOptions = REJECTION_NOTIFICATION(pendingApplication2.companyName); + + expect(EmailService.sendMail).toHaveBeenCalledWith({ + subject: emailOptions.subject, + to: pendingApplication2.email, + template: emailOptions.template, + context: emailOptions.context, + }); + }); + }); + + describe("With previous applications", () => { + + const approvedApplicationData = { + email: "approved@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + approvedAt: new Date("2019-11-26"), + rejectReason: null + }; + const rejectedApplicationData = { + email: "rejected@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25"), + rejectedAt: new Date("2019-11-26"), + rejectReason: "test-reason" + }; + + let approvedApplication, rejectedApplication; + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + + [ + approvedApplication, + rejectedApplication, + ] = await CompanyApplication.create([ + approvedApplicationData, + rejectedApplicationData, + ]); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + test("Should fail if trying to reject already approved application", async () => { + await test_agent + .post(`/applications/company/${approvedApplication._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }) + .expect(StatusCodes.CONFLICT); + }); + + test("Should fail if trying to reject already rejected application", async () => { + await test_agent + .post(`/applications/company/${rejectedApplication._id}/reject`) + .send({ rejectReason: "Some reason which is valid" }) + .expect(StatusCodes.CONFLICT); + }); + }); }); diff --git a/test/end-to-end/review.js b/test/end-to-end/review.js deleted file mode 100644 index 5405af92..00000000 --- a/test/end-to-end/review.js +++ /dev/null @@ -1,214 +0,0 @@ -jest.mock("../../src/lib/emailService"); -import EmailService, { EmailService as EmailServiceClass } from "../../src/lib/emailService"; -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); -import { StatusCodes } from "http-status-codes"; -import CompanyApplication, { CompanyApplicationRules } from "../../src/models/CompanyApplication"; -import Account from "../../src/models/Account"; -import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import ApplicationStatus from "../../src/models/constants/ApplicationStatus"; -import { APPROVAL_NOTIFICATION, REJECTION_NOTIFICATION } from "../../src/email-templates/companyApplicationApproval"; -import mongoose from "mongoose"; -import hash from "../../src/lib/passwordHashing"; - -const { ObjectId } = mongoose.Types; - -describe("Company application review endpoint test", () => { - - const test_agent = agent(); - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeAll(async () => { - await Account.deleteMany({}); - await Account.create({ email: test_user_admin.email, password: await hash(test_user_admin.password), isAdmin: true }); - }); - - beforeEach(async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - }); - - describe("/applications/company", () => { - - describe("Approval/Rejection", () => { - let application; - const pendingApplication = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - - describe("Approve application", () => { - - beforeEach(async () => { - await Account.deleteMany({ email: pendingApplication.email }); - application = await CompanyApplication.create(pendingApplication); - }); - - afterEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should approve pending application", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.email).toBe(pendingApplication.email); - expect(res.body.companyName).toBe(pendingApplication.companyName); - }); - - test("Should send approval email to company email", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(StatusCodes.OK); - - const emailOptions = APPROVAL_NOTIFICATION(application.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: application.email, - template: emailOptions.template, - context: emailOptions.context, - }); - - }); - - test("Should fail if trying to approve inexistent application", async () => { - - const res = await test_agent - .post(`/applications/company/${new ObjectId()}/approve`); - - expect(res.status).toBe(StatusCodes.NOT_FOUND); - }); - - test("Should fail if trying to approve already approved application", async () => { - await test_agent - .post(`/applications/company/${application._id}/approve`); - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(StatusCodes.CONFLICT); - }); - - test("Should fail if trying to approve already rejected application", async () => { - await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(StatusCodes.CONFLICT); - }); - - test("Should fail if approving application with an existing account with same email, and then rollback", async () => { - await Account.create({ email: application.email, password: "passwordHashedButNotReally", isAdmin: true }); - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(StatusCodes.CONFLICT); - expect(res.body.error_code).toBe(ErrorTypes.VALIDATION_ERROR); - expect(res.body.errors[0].msg).toBe(CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg); - - const result_application = await CompanyApplication.findById(application._id); - expect(result_application.state).toBe(ApplicationStatus.PENDING); - }); - }); - - describe("Reject application", () => { - - beforeEach(async () => { - await Account.deleteMany({ email: pendingApplication.email }); - application = await CompanyApplication.create(pendingApplication); - }); - - afterEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should fail if no rejectReason provided", async () => { - const res = await test_agent - .post(`/applications/company/${application._id}/reject`); - - expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); - expect(res.body.errors[0]).toStrictEqual({ location: "body", msg: "required", param: "rejectReason" }); - - }); - - test("Should reject pending application", async () => { - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.email).toBe(pendingApplication.email); - expect(res.body.companyName).toBe(pendingApplication.companyName); - }); - - test("Should send rejection email to company email", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(StatusCodes.OK); - - const emailOptions = REJECTION_NOTIFICATION(application.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: application.email, - template: emailOptions.template, - context: emailOptions.context, - }); - - }); - - test("Should fail if trying to reject inexistent application", async () => { - const res = await test_agent - .post(`/applications/company/${new ObjectId()}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(StatusCodes.NOT_FOUND); - }); - - test("Should fail if trying to reject already approved application", async () => { - await test_agent - .post(`/applications/company/${application._id}/approve`); - - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(StatusCodes.CONFLICT); - }); - - test("Should fail if trying to reject already rejected application", async () => { - await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(StatusCodes.CONFLICT); - }); - }); - }); - }); -}); From ce439faba6e500391a98bb34ae67279e0ceb9cea Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sat, 8 Apr 2023 18:09:49 +0100 Subject: [PATCH 07/30] Moved Schema tests to relevant folder --- test/end-to-end/applications/company/:id/reject.js | 2 +- test/{account_schema.js => schema/AccountSchema.js} | 2 +- .../CompanyApplicationSchema.js} | 8 ++++---- test/{company_schema.js => schema/CompanySchema.js} | 10 +++++----- test/{offer_schema.js => schema/OfferSchema.js} | 10 +++++----- 5 files changed, 16 insertions(+), 16 deletions(-) rename test/{account_schema.js => schema/AccountSchema.js} (98%) rename test/{company_application_schema.js => schema/CompanyApplicationSchema.js} (90%) rename test/{company_schema.js => schema/CompanySchema.js} (91%) rename test/{offer_schema.js => schema/OfferSchema.js} (98%) diff --git a/test/end-to-end/applications/company/:id/reject.js b/test/end-to-end/applications/company/:id/reject.js index 2a147c47..a365d0e8 100644 --- a/test/end-to-end/applications/company/:id/reject.js +++ b/test/end-to-end/applications/company/:id/reject.js @@ -179,4 +179,4 @@ describe("POST /applications/company/:id/reject", () => { .expect(StatusCodes.CONFLICT); }); }); -}); +}); \ No newline at end of file diff --git a/test/account_schema.js b/test/schema/AccountSchema.js similarity index 98% rename from test/account_schema.js rename to test/schema/AccountSchema.js index d8ccb392..9fd97123 100644 --- a/test/account_schema.js +++ b/test/schema/AccountSchema.js @@ -1,4 +1,4 @@ -import Account from "../src/models/Account.js"; +import Account from "../../src/models/Account.js"; describe("# Account schema tests", () => { describe("Testing required fields", () => { diff --git a/test/company_application_schema.js b/test/schema/CompanyApplicationSchema.js similarity index 90% rename from test/company_application_schema.js rename to test/schema/CompanyApplicationSchema.js index 2331eed7..0bf25736 100644 --- a/test/company_application_schema.js +++ b/test/schema/CompanyApplicationSchema.js @@ -1,7 +1,7 @@ -import CompanyApplication from "../src/models/CompanyApplication"; -import SchemaTester from "./utils/SchemaTester"; -import ApplicationStatus from "../src/models/constants/ApplicationStatus"; -import CompanyApplicationConstants from "../src/models/constants/CompanyApplication"; +import CompanyApplication from "../../src/models/CompanyApplication"; +import SchemaTester from "../utils/SchemaTester"; +import ApplicationStatus from "../../src/models/constants/ApplicationStatus"; +import CompanyApplicationConstants from "../../src/models/constants/CompanyApplication"; const companyApplicationTester = SchemaTester(CompanyApplication); describe("# CompanyApplication schema tests", () => { diff --git a/test/company_schema.js b/test/schema/CompanySchema.js similarity index 91% rename from test/company_schema.js rename to test/schema/CompanySchema.js index adc80d99..730b0cbe 100644 --- a/test/company_schema.js +++ b/test/schema/CompanySchema.js @@ -1,8 +1,8 @@ -import Company from "../src/models/Company"; -import SchemaTester from "./utils/SchemaTester"; -import CompanyConstants from "../src/models/constants/Company"; -import { DAY_TO_MS } from "./utils/TimeConstants"; -import Offer from "../src/models/Offer"; +import Company from "../../src/models/Company"; +import SchemaTester from "../utils/SchemaTester"; +import CompanyConstants from "../../src/models/constants/Company"; +import { DAY_TO_MS } from "../utils/TimeConstants"; +import Offer from "../../src/models/Offer"; const CompanySchemaTester = SchemaTester(Company); diff --git a/test/offer_schema.js b/test/schema/OfferSchema.js similarity index 98% rename from test/offer_schema.js rename to test/schema/OfferSchema.js index 30436f88..fc8ca3a2 100644 --- a/test/offer_schema.js +++ b/test/schema/OfferSchema.js @@ -1,8 +1,8 @@ -import Offer from "../src/models/Offer"; -import JobTypes from "../src/models/constants/JobTypes"; -import { MIN_FIELDS, MAX_FIELDS, FieldTypes } from "../src/models/constants/FieldTypes"; -import { MIN_TECHNOLOGIES, MAX_TECHNOLOGIES, TechnologyTypes } from "../src/models/constants/TechnologyTypes"; -import { OFFER_MAX_LIFETIME_MONTHS } from "../src/models/constants/TimeConstants"; +import Offer from "../../src/models/Offer"; +import JobTypes from "../../src/models/constants/JobTypes"; +import { MIN_FIELDS, MAX_FIELDS, FieldTypes } from "../../src/models/constants/FieldTypes"; +import { MIN_TECHNOLOGIES, MAX_TECHNOLOGIES, TechnologyTypes } from "../../src/models/constants/TechnologyTypes"; +import { OFFER_MAX_LIFETIME_MONTHS } from "../../src/models/constants/TimeConstants"; describe("# Offer Schema tests", () => { describe("Required and bound (between min and max elements) properties tests", () => { From b751df25a9a97ceec8a776c10fa136891a6ce362 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Mon, 10 Apr 2023 02:12:00 +0100 Subject: [PATCH 08/30] Finished company registration completion tests --- src/api/middleware/validators/application.js | 3 - src/api/middleware/validators/company.js | 2 + src/api/routes/company.js | 2 +- .../applications/company/:id/approve.js | 20 +- .../applications/company/:id/reject.js | 16 +- test/end-to-end/auth/login.js | 9 +- test/end-to-end/company.js | 262 ------------- test/end-to-end/company/:id/block.js | 1 + test/end-to-end/company/:id/delete.js | 1 + test/end-to-end/company/:id/disable.js | 1 + test/end-to-end/company/:id/edit.js | 1 + test/end-to-end/company/:id/enable.js | 1 + ...sReachedMaxConcurrentOffersBetweenDates.js | 353 ++++++++++++++++++ test/end-to-end/company/:id/index.js | 1 + test/end-to-end/company/:id/unblock.js | 1 + test/end-to-end/company/application/finish.js | 339 +++++++++++++++++ test/end-to-end/company/index.js | 1 + 17 files changed, 728 insertions(+), 286 deletions(-) create mode 100644 test/end-to-end/company/:id/block.js create mode 100644 test/end-to-end/company/:id/delete.js create mode 100644 test/end-to-end/company/:id/disable.js create mode 100644 test/end-to-end/company/:id/edit.js create mode 100644 test/end-to-end/company/:id/enable.js create mode 100644 test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js create mode 100644 test/end-to-end/company/:id/index.js create mode 100644 test/end-to-end/company/:id/unblock.js create mode 100644 test/end-to-end/company/application/finish.js create mode 100644 test/end-to-end/company/index.js diff --git a/src/api/middleware/validators/application.js b/src/api/middleware/validators/application.js index 49df3afd..5f5b4698 100644 --- a/src/api/middleware/validators/application.js +++ b/src/api/middleware/validators/application.js @@ -64,9 +64,6 @@ const isAfterSubmissionDateFrom = (submissionDateTo, { req }) => { const { submissionDateFrom } = req.query; - console.info(req.body); - console.info("Dates:", `\n\tFrom: ${submissionDateFrom};\n\tTo: ${submissionDateTo};`); - return submissionDateFrom <= submissionDateTo; }; diff --git a/src/api/middleware/validators/company.js b/src/api/middleware/validators/company.js index 82fe9fa4..207a2495 100644 --- a/src/api/middleware/validators/company.js +++ b/src/api/middleware/validators/company.js @@ -10,6 +10,8 @@ export const MAX_LIMIT_RESULTS = 100; const DEFAULT_PUBLISH_DATE = new Date(Date.now()).toISOString(); export const finish = useExpressValidators([ + /* body("logo", ValidationReasons.DEFAULT) + .exists().withMessage(ValidationReasons.REQUIRED).bail(), */ body("bio", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() .isString().withMessage(ValidationReasons.STRING) diff --git a/src/api/routes/company.js b/src/api/routes/company.js index 01db8ab2..e7d346ad 100644 --- a/src/api/routes/company.js +++ b/src/api/routes/company.js @@ -13,7 +13,7 @@ import { concurrentOffersNotExceeded } from "../middleware/validators/validatorU import { or } from "../middleware/utils.js"; -import * as fileMiddleware from "../middleware/files.js"; +import * as fileMiddleware from "../middleware/files.js"; import OfferService from "../../services/offer.js"; import AccountService from "../../services/account.js"; import Offer from "../../models/Offer.js"; diff --git a/test/end-to-end/applications/company/:id/approve.js b/test/end-to-end/applications/company/:id/approve.js index 78c7351d..96a9e088 100644 --- a/test/end-to-end/applications/company/:id/approve.js +++ b/test/end-to-end/applications/company/:id/approve.js @@ -46,14 +46,16 @@ describe("POST /applications/company/:id/approve", () => { .expect(StatusCodes.OK); }); - test("Should fail if trying to approve inexistent application", async () => { + describe("ID Validation", () => { + test("Should fail if trying to approve inexistent application", async () => { - const id = new ObjectId(); + const id = new ObjectId(); - await test_agent - .post(`/applications/company/${id}/approve`) - .expect(StatusCodes.NOT_FOUND); + await test_agent + .post(`/applications/company/${id}/approve`) + .expect(StatusCodes.NOT_FOUND); + }); }); describe("Without previous applications", () => { @@ -188,7 +190,13 @@ describe("POST /applications/company/:id/approve", () => { expect(res.status).toBe(StatusCodes.CONFLICT); expect(res.body.error_code).toBe(ErrorTypes.VALIDATION_ERROR); - expect(res.body.errors[0].msg).toBe(CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg); + expect(res.body.errors).toEqual(expect.arrayContaining( + [ + expect.objectContaining({ + msg: CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg + }) + ] + )); const result_application = await CompanyApplication.findById(sameEmailApplication._id); expect(result_application.state).toBe(ApplicationStatus.PENDING); diff --git a/test/end-to-end/applications/company/:id/reject.js b/test/end-to-end/applications/company/:id/reject.js index a365d0e8..21b8e6c6 100644 --- a/test/end-to-end/applications/company/:id/reject.js +++ b/test/end-to-end/applications/company/:id/reject.js @@ -44,14 +44,16 @@ describe("POST /applications/company/:id/reject", () => { .expect(StatusCodes.OK); }); - test("Should fail if trying to reject inexistent application", async () => { + describe("ID Validation", () => { + test("Should fail if trying to reject inexistent application", async () => { - const id = new ObjectId(); + const id = new ObjectId(); - await test_agent - .post(`/applications/company/${id}/reject`) - .send({ rejectReason: "Some reason which is valid" }) - .expect(StatusCodes.NOT_FOUND); + await test_agent + .post(`/applications/company/${id}/reject`) + .send({ rejectReason: "Some reason which is valid" }) + .expect(StatusCodes.NOT_FOUND); + }); }); describe("Without previous applications", () => { @@ -179,4 +181,4 @@ describe("POST /applications/company/:id/reject", () => { .expect(StatusCodes.CONFLICT); }); }); -}); \ No newline at end of file +}); diff --git a/test/end-to-end/auth/login.js b/test/end-to-end/auth/login.js index 2a9c3c52..2251e887 100644 --- a/test/end-to-end/auth/login.js +++ b/test/end-to-end/auth/login.js @@ -2,7 +2,6 @@ import { StatusCodes } from "http-status-codes"; import Account from "../../../src/models/Account"; import Company from "../../../src/models/Company"; import ValidatorTester from "../../utils/ValidatorTester"; -import withGodToken from "../../utils/GodToken"; import hash from "../../../src/lib/passwordHashing"; describe("POST /auth/login", () => { @@ -18,8 +17,6 @@ describe("POST /auth/login", () => { password: "password123", }; - let test_company; - beforeAll(async () => { await Account.deleteMany({}); await Company.deleteMany({}); @@ -30,7 +27,7 @@ describe("POST /auth/login", () => { isAdmin: true }); - test_company = await Company.create({ name: "test company" }); + const test_company = await Company.create({ name: "test company" }); await Account.create({ email: test_user_company.email, @@ -45,7 +42,7 @@ describe("POST /auth/login", () => { }); describe("Input Validation", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(withGodToken(params))); + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(params)); const BodyValidatorTester = EndpointValidatorTester("body"); describe("email", () => { @@ -104,7 +101,6 @@ describe("DELETE /auth/login", () => { test("should return OK since the logout is idempotent", async () => { await test_agent .delete("/auth/login") - .send() .expect(StatusCodes.OK); }); @@ -117,7 +113,6 @@ describe("DELETE /auth/login", () => { await test_agent .delete("/auth/login") - .send() .expect(StatusCodes.OK); }); }); diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index 3bad03e0..f6949efc 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -2245,268 +2245,6 @@ describe("Company endpoint", () => { }); }); - describe("GET /company/:companyId/hasReachedMaxConcurrentOffersBetweenDates", () => { - let test_company_1, test_company_2; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - - const test_agent = agent(); - - const publishDate = (new Date(Date.now())).toISOString(); - const publishEndDate = (new Date(Date.now() + (2 * DAY_TO_MS))).toISOString(); - - beforeEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [test_company_1, test_company_2] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true, - logo: "http://oniebuedafixe.com/wow.png" - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }); - - const testOffers = Array(CompanyConstants.offers.max_concurrent) - .fill(generateTestOffer({ - owner: test_company_2._id, - ownerName: test_company_2.name, - ownerLogo: test_company_2.logo, - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - })); - - await Offer.deleteMany({}); - await Offer.create(testOffers); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using an invalid id", async () => { - - const res = await test_agent - .get("/company/123/hasReachedMaxConcurrentOffersBetweenDates") - .send(withGodToken({ publishDate, publishEndDate })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .get(`/company/${id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - describe("Date validation", () => { - test("Should succeed if publishDate is not specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishEndDate })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if publishEndDate is not specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if neither publishDate or publishEndDate are specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should fail if publishDate is after publishEndDate", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ - publishDate: publishEndDate, - publishEndDate: publishDate, - })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_AFTER("publishDate")); - }); - - test("Should fail if publishDate doesn't have a date format", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate: "123", publishEndDate })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); - }); - - test("Should fail if publishEndDate doesn't have a date format", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate: "123" })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); - }); - }); - - test("Should fail if not logged in", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("Should fail if logged as a different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS); - }); - - test("Should succeed if god token is sent", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if logged as an admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if logged as the same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should return true if the company has reached max offers in the time interval", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_2._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", true); - }); - }); - describe("PUT /company/edit", () => { let test_companies; let test_company, test_company_blocked, test_company_disabled; diff --git a/test/end-to-end/company/:id/block.js b/test/end-to-end/company/:id/block.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/:id/block.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); diff --git a/test/end-to-end/company/:id/delete.js b/test/end-to-end/company/:id/delete.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/:id/delete.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); diff --git a/test/end-to-end/company/:id/disable.js b/test/end-to-end/company/:id/disable.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/:id/disable.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); diff --git a/test/end-to-end/company/:id/edit.js b/test/end-to-end/company/:id/edit.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/:id/edit.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); diff --git a/test/end-to-end/company/:id/enable.js b/test/end-to-end/company/:id/enable.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/:id/enable.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); diff --git a/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js b/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js new file mode 100644 index 00000000..277c3a0b --- /dev/null +++ b/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js @@ -0,0 +1,353 @@ +import { StatusCodes } from "http-status-codes"; +import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import CompanyConstants from "../../../../src/models/constants/Company"; +import withGodToken from "../../../utils/GodToken"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; +import ValidatorTester from "../../../utils/ValidatorTester"; + +describe("GET /company/:companyId/hasReachedMaxConcurrentOffersBetweenDates", () => { + + const test_agent = agent(); + + const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date()).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: false, + requirements: ["The candidate must be tested", "Fluent in testJS"], + ...params, + }); + + let test_company_1, test_company_2; + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + const test_user_company_1 = { + email: "company1@email.com", + password: "password123", + }; + const test_user_company_2 = { + email: "company2@email.com", + password: "password123", + }; + + const publishDate = (new Date(Date.now())).toISOString(); + const publishEndDate = (new Date(Date.now() + (2 * DAY_TO_MS))).toISOString(); + + beforeAll(async () => { + await Account.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + await Company.deleteMany({}); + [test_company_1, test_company_2] = await Company.create([ + { + name: "test-company-1", + hasFinishedRegistration: true + }, { + name: "test-company-2", + hasFinishedRegistration: true, + logo: "http://oniebuedafixe.com/wow.png" + } + ]); + + await Account.create({ + email: test_user_company_1.email, + password: await hash(test_user_company_1.password), + company: test_company_1._id + }); + await Account.create({ + email: test_user_company_2.email, + password: await hash(test_user_company_2.password), + company: test_company_2._id + }); + + const testOffers = Array(CompanyConstants.offers.max_concurrent) + .fill(generateTestOffer({ + owner: test_company_2._id, + ownerName: test_company_2.name, + ownerLogo: test_company_2.logo, + "publishDate": (new Date(Date.now())).toISOString(), + "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() + })); + + await Offer.deleteMany({}); + await Offer.create(testOffers); + }); + + beforeEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + await Offer.deleteMany({}); + }); + + describe("Id validation", () => { + test("Should fail if using an invalid id", async () => { + + const res = await test_agent + .get("/company/123/hasReachedMaxConcurrentOffersBetweenDates") + .send(withGodToken({ publishDate, publishEndDate })) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining( + [ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.OBJECT_ID + }) + ] + )); + }); + + test("Should fail if company does not exist", async () => { + + const id = "111111111111111111111111"; + const res = await test_agent + .get(`/company/${id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken({ publishDate, publishEndDate })) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND(id) + }) + ])); + }); + }); + + describe("Input validation", () => { + + const testValidationUser = { + email: "validation@email.com", + password: "password123", + }; + let validationTestCompany; + const testValidationCompanyData = { + name: "validation-test-company", + hasFinishedRegistration: true + }; + + const EndpointValidatorTester = ValidatorTester( + (params) => test_agent.get(`/company/${validationTestCompany._id}/hasReachedMaxConcurrentOffersBetweenDates`).send(params) + ); + const BodyValidatorTester = EndpointValidatorTester("body"); + + beforeAll(async () => { + validationTestCompany = await Company.create(testValidationCompanyData); + + await Account.create({ + email: testValidationUser.email, + password: await hash(testValidationUser.password), + company: validationTestCompany._id + }); + }); + + afterAll(async () => { + await Account.deleteMany({ email: testValidationUser.email }); + await Company.deleteMany({ name: testValidationCompanyData.name }); + }); + + beforeEach(async () => { + await test_agent + .post("/auth/login") + .send(testValidationUser) + .expect(StatusCodes.OK); + }); + + describe("publishDate", () => { + const FieldValidatorTester = BodyValidatorTester("publishDate"); + FieldValidatorTester.mustBeDate(); + }); + + describe("publishEndDate", () => { + const FieldValidatorTester = BodyValidatorTester("publishEndDate"); + FieldValidatorTester.mustBeDate(); + FieldValidatorTester.mustBeAfter("publishDate"); + }); + }); + + describe("Auth", () => { + test("Should fail if not logged in", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send({ publishDate, publishEndDate }) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); + }); + + test("Should fail if logged as a different company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_2) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send({ publishDate, publishEndDate }) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS); + }); + + test("Should succeed if god token is sent", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken({ publishDate, publishEndDate })) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("maxOffersReached", false); + }); + + test("Should succeed if logged as an admin", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send({ publishDate, publishEndDate }) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("maxOffersReached", false); + }); + + test("Should succeed if logged as the same company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_1) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send({ publishDate, publishEndDate }) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("maxOffersReached", false); + }); + }); + + test("Should succeed if publishDate is not specified", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken({ publishEndDate })) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("maxOffersReached", false); + }); + + test("Should succeed if publishEndDate is not specified", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken({ publishDate })) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("maxOffersReached", false); + }); + + test("Should succeed if neither publishDate or publishEndDate are specified", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("maxOffersReached", false); + }); + + test("Should fail if publishDate is after publishEndDate", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken({ + publishDate: publishEndDate, + publishEndDate: publishDate, + })) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_AFTER("publishDate")); + }); + + test("Should fail if publishDate doesn't have a date format", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken({ publishDate: "123", publishEndDate })) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("param", "publishDate"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); + }); + + test("Should fail if publishEndDate doesn't have a date format", async () => { + + const res = await test_agent + .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send(withGodToken({ publishDate, publishEndDate: "123" })) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); + }); + + test("Should return true if the company has reached max offers in the time interval", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_2) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_company_2._id}/hasReachedMaxConcurrentOffersBetweenDates`) + .send({ publishDate, publishEndDate }) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("maxOffersReached", true); + }); +}); diff --git a/test/end-to-end/company/:id/index.js b/test/end-to-end/company/:id/index.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/:id/index.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); diff --git a/test/end-to-end/company/:id/unblock.js b/test/end-to-end/company/:id/unblock.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/:id/unblock.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); diff --git a/test/end-to-end/company/application/finish.js b/test/end-to-end/company/application/finish.js new file mode 100644 index 00000000..204983e0 --- /dev/null +++ b/test/end-to-end/company/application/finish.js @@ -0,0 +1,339 @@ +import fs from "fs"; +import { StatusCodes } from "http-status-codes"; +import path from "path"; +import { fileURLToPath } from "url"; +import { MAX_FILE_SIZE_MB } from "../../../../src/api/middleware/utils"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import config from "../../../../src/config/env"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import CompanyConstants from "../../../../src/models/constants/Company"; +import withGodToken from "../../../utils/GodToken"; +import ValidatorTester from "../../../utils/ValidatorTester"; + +describe("POST /company/application/finish", () => { + + const test_agent = agent(); + + const testUserAdmin = { + email: "admin@email.com", + password: "password123", + }; + + const testUser = { + email: "user@email.com", + password: "password123", + }; + const nonFinishedCompanyData = { + name: "Company Ltd", + }; + let testCompany; + + const testSingleContactUser = { + email: "userSingleContact@email.com", + password: "password123", + }; + const nonFinishedSingleContactCompanyData = { + name: "Company Ltd2", + }; + let testSingleContactCompany; + + const testFinishedUser = { + email: "finishedUsser@email.com", + password: "password123", + }; + const finishedCompanyData = { + name: "Company Ltd", + hasFinishedRegistration: true, + }; + + beforeAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + + await Account.create({ + email: testUserAdmin.email, + password: await hash(testUserAdmin.password), + isAdmin: true, + }); + + const [ + _testCompany, + testFinishedCompany, + _testSingleContactCompany, + ] = await Company.create([ + nonFinishedCompanyData, + finishedCompanyData, + nonFinishedSingleContactCompanyData, + ]); + testCompany = _testCompany; + testSingleContactCompany = _testSingleContactCompany; + + await Account.create({ + email: testUser.email, + password: await hash(testUser.password), + company: testCompany._id + }); + + await Account.create({ + email: testFinishedUser.email, + password: await hash(testFinishedUser.password), + company: testFinishedCompany._id + }); + + await Account.create({ + email: testSingleContactUser.email, + password: await hash(testSingleContactUser.password), + company: testSingleContactCompany._id + }); + }); + + afterAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + }); + + beforeEach(async () => { + // Login + await test_agent + .post("/auth/login") + .send(testUser) + .expect(StatusCodes.OK); + }); + + afterEach(async () => { + // Logout + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + describe("Input Validation", () => { + + const validationUser = { + email: "validation@email.com", + password: "password123", + }; + const validationCompany = { + name: "Validation Company", + }; + + beforeAll(async () => { + const test_company = await Company.create(validationCompany); + await Account.create({ + email: validationUser.email, + password: await hash(validationUser.password), + company: test_company._id + }); + }); + + beforeEach(async () => { + await test_agent + .post("/auth/login") + .send(validationUser) + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Company.deleteMany({ name: validationCompany.name }); + await Account.deleteMany({ email: validationUser.email }); + }); + + const endpointRequest = async (params) => { + + let requestBuilder = test_agent + .post("/company/application/finish") + .attach("logo", params.logo || "test/data/logo-niaefeup.png"); + + if (params.bio) { + requestBuilder = requestBuilder + .field("bio", params.bio); + } + + if (params.contacts) { + requestBuilder = requestBuilder + .field("contacts", params.contacts); + } + + const res = await requestBuilder; + + return res; + }; + + const EndpointValidator = ValidatorTester(endpointRequest); + const BodyValidator = EndpointValidator("body"); + + describe("contacts", () => { + const FieldValidator = BodyValidator("contacts"); + FieldValidator.isRequired(); + + // Can't exactly test this unless we increase the min length + // This is due to the fact that empty arrays are treated as an empty string in multipart form data + // FieldValidator.mustHaveAtLeast(CompanyConstants.contacts.min_length); + + FieldValidator.mustBeArrayBetween(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length); + }); + + describe("bio", () => { + const FieldValidator = BodyValidator("bio"); + FieldValidator.isRequired(); + FieldValidator.hasMaxLength(CompanyConstants.bio.max_length); + }); + + describe("logo", () => { + // test this manually since there are no image specific validator testers + test("should fail if file size is too large", async () => { + const res = await endpointRequest({ + logo: "test/data/logo-niaefeup-10mb.png", + contacts: ["Some test contact"], + bio: "some tet bio" + }); + + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), + "param": "logo", + }); + }); + + test("should fail if file is invalid format", async () => { + const res = await endpointRequest({ + logo: fileURLToPath(import.meta.url), + contacts: ["Some test contact"], + bio: "some tet bio" + }); + + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.IMAGE_FORMAT, + "param": "logo", + }); + }); + }); + }); + + test("should fail if making unauthenticated request", async () => { + + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + + await request() + .post("/company/application/finish") + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should fail if authenticated as admin", async () => { + // Login + await test_agent + .post("/auth/login") + .send(testUserAdmin) + .expect(StatusCodes.OK); + + await request() + .post("/company/application/finish") + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should fail if sending god token", async () => { + + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + + await test_agent + .post("/company/application/finish") + .send(withGodToken()) + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should fail if company has already finished registration", async () => { + // Login + await test_agent + .post("/auth/login") + .send(testFinishedUser) + .expect(StatusCodes.OK); + + const res = await test_agent + .post("/company/application/finish") + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining( + [ + expect.objectContaining({ + "msg": ValidationReasons.REGISTRATION_FINISHED, + }), + ] + )); + }); + + test("should finish the application with multiple contacts", async () => { + + const contacts = ["contact1", "contact2"]; + + await test_agent + .post("/company/application/finish") + .attach("logo", "test/data/logo-niaefeup.png") + .field("bio", "A very interesting and compelling bio") + .field("contacts", contacts) + .expect(StatusCodes.OK); + + const test_companies = await Company.find({ hasFinishedRegistration: true }); + expect(test_companies).toHaveLength(2); + expect(test_companies).toEqual(expect.arrayContaining( + [ + expect.objectContaining({ + name: testCompany.name, + hasFinishedRegistration: true, + bio: "A very interesting and compelling bio", + contacts, + }), + ] + )); + + const filename = path.join(`${config.upload_folder}/${testCompany.id}.png`); + expect(fs.existsSync(filename)).toBe(true); // TODO: change to async + + // clean up file created + await fs.promises.unlink(filename); + }); + + test("should finish the application with single contact", async () => { + + await test_agent + .post("/auth/login") + .send(testSingleContactUser) + .expect(StatusCodes.OK); + + const contacts = ["contact1"]; + + await test_agent + .post("/company/application/finish") + .attach("logo", "test/data/logo-niaefeup.png") + .field("bio", "A very interesting and compelling bio") + .field("contacts", contacts) + .expect(StatusCodes.OK); + + const test_companies = await Company.find({ hasFinishedRegistration: true }); + expect(test_companies).toHaveLength(3); + expect(test_companies).toEqual(expect.arrayContaining( + [ + expect.objectContaining({ + name: testSingleContactCompany.name, + hasFinishedRegistration: true, + bio: "A very interesting and compelling bio", + contacts, + }), + ] + )); + + const filename = path.join(`${config.upload_folder}/${testSingleContactCompany.id}.png`); + expect(fs.existsSync(filename)).toBe(true); // TODO: change to async + + // clean up file created + await fs.promises.unlink(filename); + }); +}); diff --git a/test/end-to-end/company/index.js b/test/end-to-end/company/index.js new file mode 100644 index 00000000..3db9e2ea --- /dev/null +++ b/test/end-to-end/company/index.js @@ -0,0 +1 @@ +test("should be true", () => expect(true).toBe(true)); From d5ac1a2ff4212459591be02ab01fa66e7afa23bf Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sun, 16 Apr 2023 21:56:34 +0100 Subject: [PATCH 09/30] Re-made GET /company tests --- package-lock.json | 20 +- src/api/middleware/validators/company.js | 4 +- test/end-to-end/company.js | 244 ++--------------------- test/end-to-end/company/index.js | 192 +++++++++++++++++- 4 files changed, 222 insertions(+), 238 deletions(-) diff --git a/package-lock.json b/package-lock.json index 276254f8..72629667 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4256,9 +4256,10 @@ "license": "MIT" }, "node_modules/cookiejar": { - "version": "2.1.3", - "dev": true, - "license": "MIT" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true }, "node_modules/core-js": { "version": "3.6.5", @@ -6650,8 +6651,9 @@ "license": "MIT" }, "node_modules/json5": { - "version": "2.2.1", - "license": "MIT", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -11824,7 +11826,9 @@ "version": "1.0.6" }, "cookiejar": { - "version": "2.1.3", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "core-js": { @@ -13338,7 +13342,9 @@ "dev": true }, "json5": { - "version": "2.2.1" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { "version": "4.0.0", diff --git a/src/api/middleware/validators/company.js b/src/api/middleware/validators/company.js index 207a2495..80bf09be 100644 --- a/src/api/middleware/validators/company.js +++ b/src/api/middleware/validators/company.js @@ -27,7 +27,9 @@ export const finish = useExpressValidators([ export const list = useExpressValidators([ query("limit", ValidationReasons.DEFAULT) .optional() - .isInt({ min: 1, max: MAX_LIMIT_RESULTS }) + .isInt({ min: 1 }) + .withMessage(ValidationReasons.MIN(1)).bail() + .isInt({ max: MAX_LIMIT_RESULTS }) .withMessage(ValidationReasons.MAX(MAX_LIMIT_RESULTS)), query("offset", ValidationReasons.DEFAULT) .optional() diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index f6949efc..9e866329 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -1,25 +1,22 @@ -import config from "../../src/config/env"; import { StatusCodes as HTTPStatus } from "http-status-codes"; +import { ErrorTypes } from "../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; +import { + COMPANY_BLOCKED_NOTIFICATION, + COMPANY_DELETED_NOTIFICATION, + COMPANY_DISABLED_NOTIFICATION, + COMPANY_ENABLED_NOTIFICATION, + COMPANY_UNBLOCKED_NOTIFICATION +} from "../../src/email-templates/companyManagement"; +import EmailService from "../../src/lib/emailService"; +import hash from "../../src/lib/passwordHashing"; import Account from "../../src/models/Account"; import Company from "../../src/models/Company"; import Offer from "../../src/models/Offer"; -import hash from "../../src/lib/passwordHashing"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; import CompanyConstants from "../../src/models/constants/Company"; import OfferConstants from "../../src/models/constants/Offer"; import withGodToken from "../utils/GodToken"; import { DAY_TO_MS } from "../utils/TimeConstants"; -import fs from "fs"; -import path from "path"; -import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import EmailService from "../../src/lib/emailService"; -import { COMPANY_UNBLOCKED_NOTIFICATION, - COMPANY_BLOCKED_NOTIFICATION, - COMPANY_ENABLED_NOTIFICATION, - COMPANY_DISABLED_NOTIFICATION, - COMPANY_DELETED_NOTIFICATION } from "../../src/email-templates/companyManagement"; -import { MAX_FILE_SIZE_MB } from "../../src/api/middleware/utils"; -import { fileURLToPath } from "url"; const getCompanies = async (options) => [...(await Company.find(options) @@ -804,219 +801,6 @@ describe("Company endpoint", () => { }); }); - describe("POST /company/application/finish", () => { - - describe("Without Auth", () => { - test("should respond with forbidden", async () => { - const emptyRes = await request() - .post("/company/application/finish"); - - expect(emptyRes.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - }); - - describe("With Auth", () => { - const test_agent = agent(); - const test_user = { - email: "user@email.com", - password: "password123", - }; - - const company_data = { - name: "Company Ltd", - }; - - beforeEach(async () => { - await Company.deleteMany({}); - const test_company = await Company.create({ name: company_data.name }); - await Account.deleteMany({}); - await Account.create({ email: test_user.email, password: await hash(test_user.password), company: test_company._id }); - - // Login - await test_agent - .post("/auth/login") - .send(test_user) - .expect(200); - }); - - test("should finish the application", async () => { - await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", ["contact1", "contact2"]) - .expect(HTTPStatus.OK); - - const test_company = [... await Company.find({})][0]; - expect([...test_company.contacts]).toEqual(["contact1", "contact2"]); - expect(test_company.hasFinishedRegistration).toBe(true); - expect(test_company.bio).toBe("A very interesting and compelling bio"); - const filename = path.join(`${config.upload_folder}/${test_company.id}.png`); - expect(fs.existsSync(filename)).toBe(true); - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", ["contact1", "contact2"]) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual( - { msg: ValidationReasons.REGISTRATION_FINISHED } - ); - - // clean up file created - fs.unlinkSync(filename); - }); - - test("should finish the application with single contact", async () => { - await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", "contact1") - .expect(HTTPStatus.OK); - - const test_company = [... await Company.find({})][0]; - expect([...test_company.contacts]).toEqual(["contact1"]); - expect(test_company.hasFinishedRegistration).toBe(true); - expect(test_company.bio).toBe("A very interesting and compelling bio"); - const filename = path.join(`${config.upload_folder}/${test_company.id}.png`); - expect(fs.existsSync(filename)).toBe(true); - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", "contact2") - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual( - { msg: ValidationReasons.REGISTRATION_FINISHED } - ); - - // clean up file created - fs.unlinkSync(filename); - }); - - describe("logo", () => { - - test("should return error when the logo is missing", async () => { - const res = await test_agent - .post("/company/application/finish") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.REQUIRED, - "param": "logo", - }); - }); - - test("should return error when the logo is missing", async () => { - const res = await test_agent - .post("/company/application/finish") - .attach("logo", fileURLToPath(import.meta.url)) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.IMAGE_FORMAT, - "param": "logo", - }); - }); - - test("should return an error when the image size is greater than the max size", async () => { - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup-10mb.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), - "param": "logo", - }); - }); - - }); - - describe("bio", () => { - test("should return an error because the bio is required", async () => { - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.REQUIRED, - "param": "bio", - }); - - }); - - - test("should return an error because the bio is too long", async () => { - const long_bio = "a".repeat(CompanyConstants.bio.max_length + 1); - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", long_bio) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.TOO_LONG(CompanyConstants.bio.max_length), - "param": "bio", - "value": long_bio - }); - - }); - }); - - describe("contacts", () => { - - test("should return an error because the contacts are required", async () => { - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.REQUIRED, - "param": "contacts", - }); - - }); - - - test("should return an error because the contacts is too long", async () => { - const contacts = new Array(CompanyConstants.contacts.max_length + 1) - .fill("contact"); - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("contacts", contacts) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.ARRAY_SIZE(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length), - "param": "contacts", - "value": contacts - }); - - }); - }); - - - }); - }); - describe("PUT /company/:companyId/block", () => { const test_agent = agent(); @@ -1204,7 +988,8 @@ describe("Company endpoint", () => { await Account.create({ email: company.email, password: await hash(company.password), - company: test_company._id }); + company: test_company._id + }); await Account.create({ email: test_user_admin.email, @@ -1445,7 +1230,8 @@ describe("Company endpoint", () => { await Account.create({ email: company.email, password: await hash(company.password), - company: test_company._id }); + company: test_company._id + }); await Account.create({ email: test_user_admin.email, diff --git a/test/end-to-end/company/index.js b/test/end-to-end/company/index.js index 3db9e2ea..aa67be1d 100644 --- a/test/end-to-end/company/index.js +++ b/test/end-to-end/company/index.js @@ -1 +1,191 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import hash from "../../../src/lib/passwordHashing"; +import Account from "../../../src/models/Account"; +import Company from "../../../src/models/Company"; +import withGodToken from "../../utils/GodToken"; + +describe("GET /company", () => { + + const sanitizeCompany = (company) => ({ ...company.toObject(), _id: company._id.toString() }); + + const test_agent = agent(); + + beforeAll(async () => { + await Company.deleteMany({}); + }); + + afterAll(async () => { + await Company.deleteMany({}); + }); + + test("should return an empty array if there are no companies", async () => { + const res = await request() + .get("/company").expect(StatusCodes.OK); + + expect(res.body.companies).toEqual([]); + expect(res.body.totalDocCount).toEqual(0); + }); + + describe("Without Auth", () => { + + const basicCompanyData = { + name: "Company", + }; + + let company, blockedCompany, disabledCompany; + + beforeAll(async () => { + await Company.deleteMany({}); + + [ + company, + blockedCompany, + disabledCompany + ] = await Company.create([ + basicCompanyData, + { ...basicCompanyData, isBlocked: true }, + { ...basicCompanyData, isDisabled: true }, + ]); + }); + + afterAll(async () => { + await Company.deleteMany({}); + }); + + test("should return \"valid\" company", async () => { + + const res = await request() + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).toContainEqual(sanitizeCompany(company)); + }); + + describe("Blocked companies", () => { + test("should not return blocked created company", async () => { + + const res = await request() + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).not.toContainEqual(sanitizeCompany(blockedCompany)); + }); + }); + + describe("Disabled companies", () => { + test("should not return disabled created company", async () => { + + const res = await request() + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).not.toContainEqual(sanitizeCompany(disabledCompany)); + }); + }); + }); + + describe("With Auth", () => { + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + const test_user_company = { + email: "user@email.com", + password: "password123" + }; + + const basicCompanyData = { + name: "Company", + }; + + let company, blockedCompany, disabledCompany; + + beforeAll(async () => { + + await Company.deleteMany({}); + await Account.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + [ + company, + blockedCompany, + disabledCompany + ] = await Company.create([ + basicCompanyData, + { ...basicCompanyData, isBlocked: true }, + { ...basicCompanyData, isDisabled: true }, + ]); + + await Account.create({ + email: test_user_company.email, + password: await hash(test_user_company.password), + company: company._id + }); + }); + + afterAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + }); + + test("should only return \"valid\" company if logged in as company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company) + .expect(StatusCodes.OK); + + const res = await test_agent + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).toEqual(expect.arrayContaining([ + sanitizeCompany(company), + ])); + + expect(res.body.totalDocCount).toEqual(1); + }); + + test("should return every company if logged in as admin", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).toEqual(expect.arrayContaining([ + sanitizeCompany(company), + sanitizeCompany(blockedCompany), + sanitizeCompany(disabledCompany), + ])); + + expect(res.body.totalDocCount).toEqual(3); + }); + + test("should return every company if god token is sent", async () => { + + const res = await test_agent + .get("/company") + .send(withGodToken({})) + .expect(StatusCodes.OK); + + expect(res.body.companies).toEqual(expect.arrayContaining([ + sanitizeCompany(company), + sanitizeCompany(blockedCompany), + sanitizeCompany(disabledCompany), + ])); + + expect(res.body.totalDocCount).toEqual(3); + }); + }); +}); From 10272972a7f1972ffc0b5d2597fccde633caf39b Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Mon, 17 Apr 2023 02:34:49 +0100 Subject: [PATCH 10/30] Finished tests for id company fetching --- test/end-to-end/company.js | 756 -------------------------- test/end-to-end/company/:id/index.js | 777 ++++++++++++++++++++++++++- 2 files changed, 776 insertions(+), 757 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index 9e866329..aab18248 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -13,20 +13,10 @@ import hash from "../../src/lib/passwordHashing"; import Account from "../../src/models/Account"; import Company from "../../src/models/Company"; import Offer from "../../src/models/Offer"; -import CompanyConstants from "../../src/models/constants/Company"; import OfferConstants from "../../src/models/constants/Offer"; import withGodToken from "../utils/GodToken"; import { DAY_TO_MS } from "../utils/TimeConstants"; -const getCompanies = async (options) => - [...(await Company.find(options) - .sort({ name: "asc" }) // sort them to match what gets returned by the service - .exec())] - .map((company) => ({ - ...company.toObject(), - _id: company._id.toString(), - })); - describe("Company endpoint", () => { const generateTestOffer = (params) => ({ @@ -55,752 +45,6 @@ describe("Company endpoint", () => { ...params, }); - - describe("GET /company", () => { - beforeAll(async () => { - await Company.deleteMany({}); - }); - - test("should return an empty array", async () => { - const res = await request() - .get("/company"); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual([]); - expect(res.body.totalDocCount).toEqual(0); - }); - - test("should return the newly created company", async () => { - await Company.create({ name: "Company" }); - const res = await request() - .get("/company"); - const companies = await getCompanies({}); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - - test("should not return blocked created company if not logged in", async () => { - await Company.deleteMany({}); - await Company.create({ name: "Company", isBlocked: true }); - const res = await request() - .get("/company"); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual([]); - expect(res.body.totalDocCount).toEqual(0); - }); - - - describe("With Auth", () => { - const test_agent = agent(); - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeEach(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - test("should return blocked created company logged in as admin", async () => { - await Company.create({ name: "Company", isBlocked: true }); - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(200); - - const res = await test_agent - .get("/company"); - const companies = await getCompanies({}); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - - }); - - describe("Disabled companies", () => { - let test_company, disabled_test_company; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company = { - email: "company@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeAll(async () => { - - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - test_company = { - name: "test-company" - }; - - disabled_test_company = { - name: "disabled-test-company", - isDisabled: true - }; - - const companies = await Company.create([test_company, disabled_test_company]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company.email, - password: await hash(test_user_company.password), - company: companies[0]._id - }); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - test("should return both companies if god token is sent", async () => { - - const res = await test_agent - .get("/company") - .send(withGodToken()); - - const companies = await getCompanies({}); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(2); - - }); - - test("should return both companies if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get("/company"); - - const companies = await getCompanies({}); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(2); - - }); - - test("should return only the enabled company if logged as unprivileged user", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get("/company"); - - const companies = await getCompanies({ isDisabled: { $ne: true } }); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - - test("should return only the enabled company if not logged", async () => { - - const res = await test_agent - .get("/company"); - - const companies = await getCompanies({ isDisabled: { $ne: true } }); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - }); - }); - - describe("GET /company/:companyId", () => { - let test_company_without_offers, - test_company_with_offers_below_limit, - test_company_with_offers_at_limit, - test_company_with_offers_above_limit, - test_company_with_hidden_offer, - test_offer_hidden, - test_company_disabled, - test_company_blocked, - test_company_with_unfinished_registration; - - const test_user_with_unfinished_registration = { - email: "unfinished@email.com", - password: "password123", - }; - const test_user_with_hidden_offer = { - email: "hidden@email.com", - password: "password123", - }; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - isAdmin: true, - }; - const test_user_disabled_company = { - email: "disabled@email.com", - password: "password123", - }; - const test_user_blocked_company = { - email: "blocked@email.com", - password: "password123", - }; - const test_company_data = { - name: "test-company", - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg", - }; - - const test_agent = agent(); - - const createTestOffers = (length, company) => - Promise.all( - Array.from({ length }, () => - Offer.create( - generateTestOffer({ - publishDate: new Date( - Date.now() - DAY_TO_MS - ).toISOString(), - publishEndDate: new Date( - Date.now() + DAY_TO_MS - ).toISOString(), - owner: company._id, - ownerName: company.name, - ownerLogo: company.logo, - }) - ) - ) - ); - - beforeAll(async () => { - await Company.deleteMany({}); - - [ - test_company_without_offers, - test_company_with_offers_below_limit, - test_company_with_offers_at_limit, - test_company_with_offers_above_limit, - test_company_with_hidden_offer, - test_company_disabled, - test_company_blocked, - test_company_with_unfinished_registration, - ] = await Company.create([ - test_company_data, - test_company_data, - test_company_data, - test_company_data, - test_company_data, - { ...test_company_data, isDisabled: true }, - { ...test_company_data, isBlocked: true }, - { ...test_company_data, hasFinishedRegistration: false }, - ]); - - await Account.deleteMany({}); - - await Promise.all([ - Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true, - }), - Account.create({ - email: test_user_with_unfinished_registration.email, - password: await hash( - test_user_with_unfinished_registration.password - ), - company: test_company_with_unfinished_registration._id, - }), - Account.create({ - email: test_user_with_hidden_offer.email, - password: await hash(test_user_with_hidden_offer.password), - company: test_company_with_hidden_offer._id, - }), - Account.create({ - email: test_user_disabled_company.email, - password: await hash(test_user_disabled_company.password), - company: test_company_disabled._id, - }), - Account.create({ - email: test_user_blocked_company.email, - password: await hash(test_user_blocked_company.password), - company: test_company_blocked._id, - }), - ]); - - test_offer_hidden = await Offer.create( - generateTestOffer({ - isHidden: true, - owner: test_company_with_hidden_offer._id.toString(), - ownerName: test_company_with_hidden_offer.name, - ownerLogo: test_company_with_hidden_offer.logo, - }) - ); - }); - - afterEach(async () => { - await test_agent.delete("/auth/login").expect(HTTPStatus.OK); - }); - - describe("Without Auth", () => { - test("should fail if invalid id", async () => { - const res = await test_agent - .get("/company/123") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.OBJECT_ID, - }), - ]) - ); - }); - - test("should fail if company does not exist", async () => { - const id = "111111111111111111111111"; - const res = await test_agent - .get(`/company/${id}`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND(id), - }), - ]) - ); - }); - - test("should succeed when the company has no offers", async () => { - const res = await test_agent - .get(`/company/${test_company_without_offers.id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", []); - expect(res.body).toHaveProperty( - "company._id", - test_company_without_offers._id.toString() - ); - }); - - test("should return all offers when below limit", async () => { - const offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible - 1, - test_company_with_offers_below_limit - ); - - const res = await test_agent - .get(`/company/${test_company_with_offers_below_limit._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - 1 - ); - expect(res.body.offers.map((x) => x._id).sort()).toEqual( - offers.map((x) => x._id.toString()).sort() - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_below_limit._id.toString() - ); - }); - - test("should return all offers when at limit", async () => { - const offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible, - test_company_with_offers_at_limit - ); - - const res = await test_agent - .get(`/company/${test_company_with_offers_at_limit._id}`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - ); - expect(res.body.offers.map((x) => x._id).sort()).toEqual( - offers.map((x) => x._id.toString()).sort() - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_at_limit._id.toString() - ); - }); - - test("should limit number of offers", async () => { - const offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible + 10, - test_company_with_offers_above_limit - ); - - const res = await test_agent - .get(`/company/${test_company_with_offers_above_limit._id}`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - ); - expect(offers.map((x) => x._id.toString())).toEqual( - expect.arrayContaining(res.body.offers.map((x) => x._id)) - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_above_limit._id.toString() - ); - }); - - test("should not return hidden offers", async () => { - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", []); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should fail if company is disabled", async () => { - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_company_disabled._id - ), - }), - ]) - ); - }); - - test("should fail if company is blocked", async () => { - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_company_blocked._id - ), - }), - ]) - ); - }); - - test("should fail if company hasn't finished registration", async () => { - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_company_with_unfinished_registration._id - ), - }), - ]) - ); - }); - }); - - describe("With Auth", () => { - test("should return hidden offers when user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_with_hidden_offer) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_offer_hidden._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should succeed if company is disabled and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_disabled_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_disabled._id.toString() - ); - }); - - test("should fail if company is blocked and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_blocked_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.COMPANY_BLOCKED, - }), - ]) - ); - }); - - test("should fail if company hasn't finished registration and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_with_unfinished_registration) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - - test("should return hidden offers when user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_offer_hidden._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should succeed if company is disabled and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_disabled._id.toString() - ); - }); - - test("should succeed if company is blocked and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_blocked._id.toString() - ); - }); - - test("should fail if company hasn't finished registration and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - - test("should return hidden offers when user is god", async () => { - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_offer_hidden._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should succeed if company is disabled and user is god", async () => { - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_disabled._id.toString() - ); - }); - - test("should succeed if company is blocked and user is god", async () => { - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_blocked._id.toString() - ); - }); - - test("should fail if company hasn't finished registration and user is god", async () => { - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .send(withGodToken()) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - }); - }); - describe("PUT /company/:companyId/block", () => { const test_agent = agent(); diff --git a/test/end-to-end/company/:id/index.js b/test/end-to-end/company/:id/index.js index 3db9e2ea..6621929a 100644 --- a/test/end-to-end/company/:id/index.js +++ b/test/end-to-end/company/:id/index.js @@ -1 +1,776 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import CompanyConstants from "../../../../src/models/constants/Company"; +import withGodToken from "../../../utils/GodToken"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; + +describe("GET /company/:companyId", () => { + + const test_agent = agent(); + + const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date()).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: false, + requirements: ["The candidate must be tested", "Fluent in testJS"], + ...params, + }); + + const test_company_data = { + name: "test-company", + hasFinishedRegistration: true, + logo: "http://awebsite.com/alogo.jpg", + }; + + beforeAll(async () => { + await Offer.deleteMany({}); + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + afterAll(async () => { + await Offer.deleteMany({}); + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + describe("Id Validation", () => { + test("should fail if invalid id", async () => { + const res = await test_agent + .get("/company/123") + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.VALIDATION_ERROR + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.OBJECT_ID, + }), + ]) + ); + }); + + test("should fail if company does not exist", async () => { + const id = "111111111111111111111111"; + + const res = await test_agent + .get(`/company/${id}`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.VALIDATION_ERROR + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND(id), + }), + ]) + ); + }); + }); + + describe("Without Auth", () => { + + describe("Without offers", () => { + + let test_company_without_offers; + + beforeAll(async () => { + test_company_without_offers = await Company.create(test_company_data); + }); + + afterAll(async () => { + await Company.deleteMany({ _id: test_company_without_offers._id }); + }); + + test("should succeed when the company has no offers", async () => { + const res = await test_agent + .get(`/company/${test_company_without_offers.id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers", []); + expect(res.body).toHaveProperty( + "company._id", + test_company_without_offers._id.toString() + ); + }); + }); + + describe("With offers", () => { + + const createTestOffers = (length, company) => + Promise.all( + Array.from({ length }, () => + Offer.create( + generateTestOffer({ + publishDate: new Date( + Date.now() - DAY_TO_MS + ).toISOString(), + publishEndDate: new Date( + Date.now() + DAY_TO_MS + ).toISOString(), + owner: company._id, + ownerName: company.name, + ownerLogo: company.logo, + }) + ) + ) + ); + + describe("Below limit", () => { + + let test_company_with_offers_below_limit; + let offers; + + beforeAll(async () => { + test_company_with_offers_below_limit = await Company.create(test_company_data); + + offers = await createTestOffers( + CompanyConstants.offers.max_profile_visible - 1, + test_company_with_offers_below_limit + ); + }); + + afterAll(async () => { + // await Offer.deleteMany({ _id: { $in: offers.map((x) => x._id) } }); prevent wasting time to compute the set of ids + await Offer.deleteMany({ owner: test_company_with_offers_below_limit._id }); + await Company.deleteMany({ _id: test_company_with_offers_below_limit._id }); + }); + + test("should return all offers when below limit", async () => { + const res = await test_agent + .get(`/company/${test_company_with_offers_below_limit._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers"); + expect(res.body.offers).toHaveLength( + CompanyConstants.offers.max_profile_visible - 1 + ); + expect(res.body.offers.map((x) => x._id).sort()).toEqual( + offers.map((x) => x._id.toString()).sort() + ); + + expect(res.body).toHaveProperty( + "company._id", + test_company_with_offers_below_limit._id.toString() + ); + }); + }); + + describe("At limit", () => { + + let test_company_with_offers_at_limit; + let offers; + + beforeAll(async () => { + test_company_with_offers_at_limit = await Company.create(test_company_data); + + offers = await createTestOffers( + CompanyConstants.offers.max_profile_visible, + test_company_with_offers_at_limit + ); + }); + + afterAll(async () => { + // await Offer.deleteMany({ _id: { $in: offers.map((x) => x._id) } }); prevent wasting time to compute the set of ids + await Offer.deleteMany({ owner: test_company_with_offers_at_limit._id }); + await Company.deleteMany({ _id: test_company_with_offers_at_limit._id }); + }); + + test("should return all offers when at limit", async () => { + const res = await test_agent + .get(`/company/${test_company_with_offers_at_limit._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers"); + expect(res.body.offers).toHaveLength( + CompanyConstants.offers.max_profile_visible + ); + expect(res.body.offers.map((x) => x._id).sort()).toEqual( + offers.map((x) => x._id.toString()).sort() + ); + + expect(res.body).toHaveProperty( + "company._id", + test_company_with_offers_at_limit._id.toString() + ); + }); + }); + + describe("Above limit", () => { + + let test_company_with_offers_above_limit; + let offers; + + beforeAll(async () => { + test_company_with_offers_above_limit = await Company.create(test_company_data); + + offers = await createTestOffers( + CompanyConstants.offers.max_profile_visible + 1, + test_company_with_offers_above_limit + ); + }); + + afterAll(async () => { + // await Offer.deleteMany({ _id: { $in: offers.map((x) => x._id) } }); prevent wasting time to compute the set of ids + await Offer.deleteMany({ owner: test_company_with_offers_above_limit._id }); + await Company.deleteMany({ _id: test_company_with_offers_above_limit._id }); + }); + + test("should limit number of offers", async () => { + + const res = await test_agent + .get(`/company/${test_company_with_offers_above_limit._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers"); + expect(res.body.offers).toHaveLength( + CompanyConstants.offers.max_profile_visible + ); + expect(offers.map((x) => x._id.toString())).toEqual( + expect.arrayContaining(res.body.offers.map((x) => x._id)) + ); + + expect(res.body).toHaveProperty( + "company._id", + test_company_with_offers_above_limit._id.toString() + ); + }); + }); + }); + + describe("With hidden offer", () => { + + let test_company_with_hidden_offer; + let test_hidden_offer; + + beforeAll(async () => { + test_company_with_hidden_offer = await Company.create({ ...test_company_data }); + + test_hidden_offer = await Offer.create( + generateTestOffer({ + isHidden: true, + owner: test_company_with_hidden_offer._id.toString(), + ownerName: test_company_with_hidden_offer.name, + ownerLogo: test_company_with_hidden_offer.logo, + }) + ); + }); + + afterAll(async () => { + await Offer.deleteMany({ _id: test_hidden_offer._id }); + await Company.deleteOne({ _id: test_company_with_hidden_offer._id }); + }); + + + test("should not return hidden offers", async () => { + const res = await test_agent + .get(`/company/${test_company_with_hidden_offer._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers", []); + expect(res.body).toHaveProperty( + "company._id", + test_company_with_hidden_offer._id.toString() + ); + }); + }); + + describe("With disabled company", () => { + + let test_disabled_company; + + beforeAll(async () => { + test_disabled_company = await Company.create({ ...test_company_data, isBlocked: true }); + }); + + afterAll(async () => { + await Company.deleteOne({ _id: test_disabled_company._id }); + }); + + test("should fail if company is disabled", async () => { + const res = await test_agent + .get(`/company/${test_disabled_company._id}`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.VALIDATION_ERROR + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND( + test_disabled_company._id + ), + }), + ]) + ); + }); + }); + + describe("With blocked company", () => { + + let test_blocked_company; + + beforeAll(async () => { + test_blocked_company = await Company.create({ ...test_company_data, isBlocked: true }); + }); + + afterAll(async () => { + await Company.deleteOne({ _id: test_blocked_company._id }); + }); + + test("should fail if company is blocked", async () => { + const res = await test_agent + .get(`/company/${test_blocked_company._id}`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.VALIDATION_ERROR + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND( + test_blocked_company._id + ), + }), + ]) + ); + }); + }); + + describe("With company that hasn't finished registration", () => { + + let test_registration_unfinished_company; + + beforeAll(async () => { + test_registration_unfinished_company = await Company.create({ ...test_company_data, hasFinishedRegistration: false }); + }); + + afterAll(async () => { + await Company.deleteOne({ _id: test_registration_unfinished_company._id }); + }); + + test("should fail if company hasn't finished registration", async () => { + const res = await test_agent + .get( + `/company/${test_registration_unfinished_company._id}` + ) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.VALIDATION_ERROR + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND( + test_registration_unfinished_company._id + ), + }), + ]) + ); + }); + }); + }); + + describe("With Auth", () => { + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + beforeAll(async () => { + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true, + }); + }); + + afterAll(async () => { + // since we only created one account, which happens to be an admin, this should be fine + await Account.deleteMany({ isAdmin: true }); + }); + + describe("With hidden offers", () => { + + const test_user_with_hidden_offer = { + email: "hidden@email.com", + password: "password123", + }; + + let test_company_with_hidden_offer; + let test_hidden_offer; + + beforeAll(async () => { + test_company_with_hidden_offer = await Company.create({ ...test_company_data }); + + await Account.create({ + email: test_user_with_hidden_offer.email, + password: await hash(test_user_with_hidden_offer.password), + company: test_company_with_hidden_offer._id, + }); + + test_hidden_offer = await Offer.create( + generateTestOffer({ + isHidden: true, + owner: test_company_with_hidden_offer._id.toString(), + ownerName: test_company_with_hidden_offer.name, + ownerLogo: test_company_with_hidden_offer.logo, + }) + ); + }); + + afterAll(async () => { + await Offer.deleteMany({ _id: test_hidden_offer._id }); + await Account.deleteOne({ email: test_user_with_hidden_offer.email }); + await Company.deleteOne({ _id: test_company_with_hidden_offer._id }); + }); + + test("should return hidden offers when user is owner", async () => { + await test_agent + .post("/auth/login") + .send(test_user_with_hidden_offer) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_company_with_hidden_offer._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers", [ + expect.objectContaining({ + _id: test_hidden_offer._id.toString(), + }), + ]); + expect(res.body).toHaveProperty( + "company._id", + test_company_with_hidden_offer._id.toString() + ); + }); + + test("should return hidden offers when user is admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_company_with_hidden_offer._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers", [ + expect.objectContaining({ + _id: test_hidden_offer._id.toString(), + }), + ]); + expect(res.body).toHaveProperty( + "company._id", + test_company_with_hidden_offer._id.toString() + ); + }); + + test("should return hidden offers when user is god", async () => { + const res = await test_agent + .get(`/company/${test_company_with_hidden_offer._id}`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("offers", expect.arrayContaining([ + expect.objectContaining({ + _id: test_hidden_offer._id.toString(), + }), + ])); + expect(res.body).toHaveProperty( + "company._id", + test_company_with_hidden_offer._id.toString() + ); + }); + }); + + describe("With disabled company", () => { + + const test_user_disabled_company = { + email: "disabled@email.com", + password: "password123", + }; + + let test_disabled_company; + + beforeAll(async () => { + test_disabled_company = await Company.create({ ...test_company_data, isDisabled: true }); + + await Account.create({ + email: test_user_disabled_company.email, + password: await hash(test_user_disabled_company.password), + company: test_disabled_company._id, + }); + }); + + afterAll(async () => { + await Account.deleteOne({ email: test_user_disabled_company.email }); + await Company.deleteOne({ _id: test_disabled_company._id }); + }); + + test("should succeed if company is disabled and user is owner", async () => { + await test_agent + .post("/auth/login") + .send(test_user_disabled_company) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_disabled_company._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty( + "company._id", + test_disabled_company._id.toString() + ); + }); + + test("should succeed if company is disabled and user is admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_disabled_company._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty( + "company._id", + test_disabled_company._id.toString() + ); + }); + + test("should succeed if company is disabled and user is god", async () => { + const res = await test_agent + .get(`/company/${test_disabled_company._id}`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty( + "company._id", + test_disabled_company._id.toString() + ); + }); + }); + + describe("With blocked company", () => { + + const test_user_blocked_company = { + email: "blocked@email.com", + password: "password123", + }; + + let test_blocked_company; + + beforeAll(async () => { + test_blocked_company = await Company.create({ ...test_company_data, isBlocked: true }); + + await Account.create({ + email: test_user_blocked_company.email, + password: await hash(test_user_blocked_company.password), + company: test_blocked_company._id, + }); + }); + + afterAll(async () => { + await Account.deleteOne({ email: test_user_blocked_company.email }); + await Company.deleteOne({ _id: test_blocked_company._id }); + }); + + test("should fail if company is blocked and user is owner", async () => { + await test_agent + .post("/auth/login") + .send(test_user_blocked_company) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_blocked_company._id}`) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.FORBIDDEN + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.COMPANY_BLOCKED, + }), + ]) + ); + }); + + test("should succeed if company is blocked and user is admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .get(`/company/${test_blocked_company._id}`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty( + "company._id", + test_blocked_company._id.toString() + ); + }); + + test("should succeed if company is blocked and user is god", async () => { + const res = await test_agent + .get(`/company/${test_blocked_company._id}`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty( + "company._id", + test_blocked_company._id.toString() + ); + }); + }); + + describe("With company that hasn't finished registration", () => { + + const test_user_with_unfinished_registration = { + email: "unfinished@email.com", + password: "password123", + }; + + let test_registration_unfinished_company; + + beforeAll(async () => { + test_registration_unfinished_company = await Company.create({ ...test_company_data, hasFinishedRegistration: false }); + + await Account.create({ + email: test_user_with_unfinished_registration.email, + password: await hash(test_user_with_unfinished_registration.password), + company: test_registration_unfinished_company._id, + }); + }); + + afterAll(async () => { + await Account.deleteOne({ email: test_user_with_unfinished_registration.email }); + await Company.deleteOne({ _id: test_registration_unfinished_company._id }); + }); + + test("should fail if company hasn't finished registration and user is owner", async () => { + await test_agent + .post("/auth/login") + .send(test_user_with_unfinished_registration) + .expect(StatusCodes.OK); + + const res = await test_agent + .get( + `/company/${test_registration_unfinished_company._id}` + ) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.FORBIDDEN + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.REGISTRATION_NOT_FINISHED, + }), + ]) + ); + }); + + test("should fail if company hasn't finished registration and user is admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .get( + `/company/${test_registration_unfinished_company._id}` + ) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.FORBIDDEN + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.REGISTRATION_NOT_FINISHED, + }), + ]) + ); + }); + + test("should fail if company hasn't finished registration and user is god", async () => { + const res = await test_agent + .get( + `/company/${test_registration_unfinished_company._id}` + ) + .send(withGodToken()) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty( + "error_code", + ErrorTypes.FORBIDDEN + ); + expect(res.body).toHaveProperty( + "errors", + expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.REGISTRATION_NOT_FINISHED, + }), + ]) + ); + }); + }); + }); +}); From 073dba577884b238367d2ca05203b0cb84d18069 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sun, 23 Apr 2023 17:10:53 +0100 Subject: [PATCH 11/30] Corrected company deletion tests --- test/end-to-end/company/:id/delete.js | 314 +++++++++++++++++++++++++- 1 file changed, 313 insertions(+), 1 deletion(-) diff --git a/test/end-to-end/company/:id/delete.js b/test/end-to-end/company/:id/delete.js index 3db9e2ea..f0ac6f36 100644 --- a/test/end-to-end/company/:id/delete.js +++ b/test/end-to-end/company/:id/delete.js @@ -1 +1,313 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import { COMPANY_DELETED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; +import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import withGodToken from "../../../utils/GodToken"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; +jest.mock("../../../../src/lib/emailService"); +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); + +describe("POST /company/:companyId/delete", () => { + + const test_agent = agent(); + + beforeAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + await Offer.deleteMany({}); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + await Offer.deleteMany({}); + }); + + describe("Id validation", () => { + test("Should fail if using invalid id", async () => { + + const res = await test_agent + .post("/company/123/delete") + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "param": "companyId", + "msg": ValidationReasons.OBJECT_ID, + }) + ])); + }); + + test("Should fail if company does not exist", async () => { + + const id = "111111111111111111111111"; + const res = await test_agent + .post(`/company/${id}/delete`) + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "param": "companyId", + "msg": ValidationReasons.COMPANY_NOT_FOUND(id), + }) + ])); + }); + }); + + describe("Without auth", () => { + + let test_company; + + const companyData = { + name: "Test Company", + hasFinishedRegistration: true + }; + + beforeAll(async () => { + test_company = await Company.create(companyData); + }); + + afterAll(async () => { + await Company.delete({ _id: test_company._id }); + }); + + test("should fail to delete company if not logged", async () => { + + const res = await test_agent + .post(`/company/${test_company._id}/delete`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + { // use an object literal since we are expecting an exact match + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS, + } + ])); + }); + }); + + describe("With auth", () => { + + const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date()).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: false, + requirements: ["The candidate must be tested", "Fluent in testJS"], + ...params, + }); + + let test_company_1, test_company_2, test_company_offers, test_company_mail; + + const test_user_company_1 = { + email: "company1@email.com", + password: "password123", + }; + const test_user_company_2 = { + email: "company2@email.com", + password: "password123", + }; + const test_user_company_offers = { + email: "offers@email.com", + password: "password123", + }; + const test_user_company_mail = { + email: "email@email.com", + password: "password123", + }; + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + beforeAll(async () => { + [test_company_1, test_company_2, test_company_offers, test_company_mail] = await Company.create([ + { + name: "test-company-1", + hasFinishedRegistration: true + }, + { + name: "test-company-2", + hasFinishedRegistration: true, + }, + { + name: "test-company-offers", + hasFinishedRegistration: true, + logo: "https://test.com/logo.png", + }, + { + name: "test-company-mail", + hasFinishedRegistration: true, + } + ]); + + await Account.create([ + { + email: test_user_company_1.email, + password: await hash(test_user_company_1.password), + company: test_company_1._id + }, + { + email: test_user_company_2.email, + password: await hash(test_user_company_2.password), + company: test_company_2._id + }, + { + email: test_user_company_offers.email, + password: await hash(test_user_company_offers.password), + company: test_company_offers._id + }, + { + email: test_user_company_mail.email, + password: await hash(test_user_company_mail.password), + company: test_company_mail._id + } + ]); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + const offer = generateTestOffer({ + owner: test_company_offers._id, + ownerName: test_company_offers.name, + ownerLogo: test_company_offers.logo, + }); + + await Offer.create([offer, offer]); + }); + + afterAll(async () => { + await Company.deleteMany({ _id: test_company_1._id }); + await Company.deleteMany({ _id: test_company_2._id }); + await Company.deleteMany({ _id: test_company_offers._id }); + await Company.deleteMany({ _id: test_company_mail._id }); + await Account.deleteMany({ email: test_user_company_1.email }); + await Account.deleteMany({ email: test_user_company_2.email }); + await Account.deleteMany({ email: test_user_company_offers.email }); + await Account.deleteMany({ email: test_user_company_mail.email }); + await Account.deleteMany({ email: test_user_admin.email }); + await Offer.deleteMany({}); + }); + + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + test("should fail to delete company if logged as different company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_2) + .expect(StatusCodes.OK); + + const res = await test_agent + .post(`/company/${test_company_1._id}/delete`) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + { + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS, + } + ])); + }); + + test("should fail to delete company if logged as admin", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .post(`/company/${test_company_1._id}/delete`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + { + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS, + } + ])); + }); + + test("Should delete company if god token is sent", async () => { + + await test_agent + .post(`/company/${test_company_1._id}/delete`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(await Company.exists({ _id: test_company_1._id })).toBeNull(); + expect(await Account.exists({ company: test_company_1._id })).toBeNull(); + }); + + test("Should delete company if logged as the same company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_2) + .expect(StatusCodes.OK); + + await test_agent + .post(`/company/${test_company_2._id}/delete`) + .expect(StatusCodes.OK); + + expect(await Company.exists({ _id: test_company_2._id })).toBeNull(); + expect(await Account.exists({ company: test_company_2._id })).toBeNull(); + }); + + test("Should delete company's offers when it is deleted", async () => { + expect(await Offer.exists({ owner: test_company_offers._id })).not.toBeNull(); + + await test_agent + .post(`/company/${test_company_offers._id}/delete`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(await Company.exists({ _id: test_company_offers._id })).toBeNull(); + expect(await Account.exists({ company: test_company_offers._id })).toBeNull(); + expect(await Offer.exists({ owner: test_company_offers._id })).toBeNull(); + }); + + test("should send an email to the company user when it is deleted", async () => { + await test_agent + .post(`/company/${test_company_mail._id}/delete`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + const emailOptions = COMPANY_DELETED_NOTIFICATION( + test_company_mail.name + ); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_user_company_mail.email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); + }); +}); From d604ea6ebe4986d7d39a0e408da586c80c525f10 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sun, 23 Apr 2023 22:38:12 +0100 Subject: [PATCH 12/30] Fixed minor bug --- test/end-to-end/company.js | 193 -------------------------- test/end-to-end/company/:id/delete.js | 2 +- 2 files changed, 1 insertion(+), 194 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index aab18248..15020c37 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -3,7 +3,6 @@ import { ErrorTypes } from "../../src/api/middleware/errorHandler"; import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; import { COMPANY_BLOCKED_NOTIFICATION, - COMPANY_DELETED_NOTIFICATION, COMPANY_DISABLED_NOTIFICATION, COMPANY_ENABLED_NOTIFICATION, COMPANY_UNBLOCKED_NOTIFICATION @@ -1083,198 +1082,6 @@ describe("Company endpoint", () => { }); }); - describe("POST /company/delete", () => { - let test_company_1, test_company_2; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [test_company_1, test_company_2] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true, - logo: "http://oniebuedafixe.com/wow.png" - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }); - - const offer = generateTestOffer({ - owner: test_company_2._id, - ownerName: test_company_2.name, - ownerLogo: test_company_2.logo, - }); - - await Offer.create([offer, offer]); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - - const res = await test_agent - .post("/company/123/delete") - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .post(`/company/${id}/delete`) - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - test("should fail to delete company if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - }); - - test("should fail to delete company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("should fail to delete company if not logged", async () => { - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("Should delete company if god token is sent", async () => { - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(await Company.exists({ _id: test_company_1._id })).toBeNull(); - expect(await Account.exists({ company: test_company_1._id })).toBeNull(); - }); - - test("Should delete company if logged as the same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(await Company.exists({ _id: test_company_1._id })).toBeNull(); - expect(await Account.exists({ company: test_company_1._id })).toBeNull(); - }); - - test("Should delete company's offers when it is deleted", async () => { - const res = await test_agent - .post(`/company/${test_company_2._id}/delete`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(await Company.exists({ _id: test_company_2._id })).toBeNull(); - expect(await Account.exists({ company: test_company_2._id })).toBeNull(); - expect(await Offer.exists({ owner: test_company_2._id })).toBeNull(); - }); - - test("should send an email to the company user when it is deleted", async () => { - await test_agent - .post(`/company/${test_company_1._id}/delete`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_DELETED_NOTIFICATION( - test_company_1.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_company_1.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); - describe("PUT /company/edit", () => { let test_companies; let test_company, test_company_blocked, test_company_disabled; diff --git a/test/end-to-end/company/:id/delete.js b/test/end-to-end/company/:id/delete.js index f0ac6f36..077af2a6 100644 --- a/test/end-to-end/company/:id/delete.js +++ b/test/end-to-end/company/:id/delete.js @@ -77,7 +77,7 @@ describe("POST /company/:companyId/delete", () => { }); afterAll(async () => { - await Company.delete({ _id: test_company._id }); + await Company.deleteMany({ _id: test_company._id }); }); test("should fail to delete company if not logged", async () => { From 3b6b558f933604229c766281f5bece24b679cb3f Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Mon, 24 Apr 2023 02:40:28 +0100 Subject: [PATCH 13/30] Remade company edit tests --- test/end-to-end/company/:id/edit.js | 490 +++++++++++++++++++++++++++- 1 file changed, 489 insertions(+), 1 deletion(-) diff --git a/test/end-to-end/company/:id/edit.js b/test/end-to-end/company/:id/edit.js index 3db9e2ea..142a2e20 100644 --- a/test/end-to-end/company/:id/edit.js +++ b/test/end-to-end/company/:id/edit.js @@ -1 +1,489 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import withGodToken from "../../../utils/GodToken"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; + +describe("PUT /company/edit", () => { + + const generateTestCompany = (params) => ({ + name: "Big Company", + bio: "Big Company Bio", + logo: "http://awebsite.com/alogo.jpg", + contacts: ["112", "122"], + hasFinishedRegistration: true, + ...params, + }); + + const test_agent = agent(); + + const edit_payload = { + name: "Changed name", + bio: "Changed bio", + logo: "http://awebsite.com/changedlogo.jpg", + contacts: ["123", "456"], + }; + + beforeAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + await Offer.deleteMany({}); + }); + + afterAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + await Offer.deleteMany({}); + }); + + describe("ID Validation", () => { + test("Should fail if id is not a valid ObjectID", async () => { + const id = "123"; + const res = await test_agent + .put(`/company/${id}/edit`) + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "location": "params", + "msg": ValidationReasons.OBJECT_ID, + "param": "companyId", + "value": id + }) + ])); + }); + + test("Should fail if id is not a valid company", async () => { + const id = "111111111111111111111111"; + + const res = await test_agent + .put(`/company/${id}/edit`) + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "location": "params", + "msg": ValidationReasons.COMPANY_NOT_FOUND(id), + "param": "companyId", + "value": id + }) + ])); + }); + }); + + describe("Without auth", () => { + + const company_data = generateTestCompany({ + name: "Test Company", + }); + let test_company; + + beforeAll(async () => { + test_company = await Company.create(company_data); + }); + + afterAll(async () => { + await Company.deleteMany({ name: test_company.name }); + }); + + test("Should fail if not logged in", async () => { + const res = await test_agent + .put(`/company/${test_company._id}/edit`) + .send({ + bio: edit_payload.bio, + contacts: edit_payload.contacts, + }) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); + }); + + describe("With auth", () => { + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const test_user_company_1 = { + email: "company1@email.com", + password: "password123", + }; + const test_user_company_2 = { + email: "company2@email.com", + password: "password123", + }; + + const test_company_1_data = generateTestCompany({ + name: "Test Company 1", + }); + const test_company_2_data = generateTestCompany({ + name: "Test Company 2", + }); + const test_company_god_data = generateTestCompany({ + name: "Test Company God", + }); + + let test_company_1, test_company_2, test_company_god; + + beforeAll(async () => { + + [ + test_company_1, + test_company_2, + test_company_god, + ] = await Company.create([ + test_company_1_data, + test_company_2_data, + test_company_god_data, + ]); + + await Account.create([ + { + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true, + }, + { + email: test_user_company_1.email, + password: await hash(test_user_company_1.password), + company: test_company_1._id, + }, + { + email: test_user_company_2.email, + password: await hash(test_user_company_2.password), + company: test_company_2._id, + }, + ]); + }); + + afterAll(async () => { + await Company.deleteMany({ + _id: { + $in: [ + test_company_god._id, + test_company_1._id, + test_company_2._id, + ] + } + }); + await Account.deleteMany({ + email: { + $in: [ + test_user_admin.email, + test_user_company_1.email, + test_user_company_2.email, + ] + } + }); + }); + + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + test("Should fail if logged in as different user", async () => { + await test_agent + .post("/auth/login") + .send(test_user_company_1) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_2._id}/edit`) + .send({ + name: edit_payload.name, + }) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS + }) + ])); + }); + + test("Should succeed if god", async () => { + const res = await test_agent + .put(`/company/${test_company_god._id}/edit`) + .send(withGodToken({ + name: edit_payload.name, + bio: edit_payload.bio, + })) + .expect(StatusCodes.OK); + + expect(res.body).toEqual(expect.objectContaining({ + _id: test_company_god._id.toString(), + name: edit_payload.name, + bio: edit_payload.bio, + })); + }); + + test("Should succeed if admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1._id}/edit`) + .send({ + name: edit_payload.name + }) + .expect(StatusCodes.OK); + + expect(res.body).toEqual(expect.objectContaining({ + name: edit_payload.name, + })); + }); + + test("Should succeed if same company", async () => { + await test_agent + .post("/auth/login") + .send(test_user_company_2) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_2._id}/edit`) + .send({ + name: edit_payload.name, + }) + .expect(StatusCodes.OK); + + expect(res.body).toEqual(expect.objectContaining({ + name: edit_payload.name, + })); + }); + + describe("Blocked company", () => { + + const test_user_company_blocked = { + email: "blocked@email.com", + password: "password123", + }; + + const test_company_blocked_data = generateTestCompany({ + name: "Test Company God", + isBlocked: true + }); + let test_company_blocked; + + beforeAll(async () => { + test_company_blocked = await Company.create(test_company_blocked_data); + + await Account.create({ + email: test_user_company_blocked.email, + password: await hash(test_user_company_blocked.password), + company: test_company_blocked._id, + }); + }); + + afterAll(async () => { + await Company.deleteMany({ + _id: test_company_blocked._id + }); + await Account.deleteMany({ email: test_user_company_blocked.email }); + }); + + test("Should fail if company is blocked (god)", async () => { + const res = await test_agent + .put(`/company/${test_company_blocked._id}/edit`) + .send(withGodToken({ + name: "Changing Blocked Company", + })) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.COMPANY_BLOCKED + }) + ])); + }); + + test("Should fail if company is blocked (user)", async () => { + await test_agent + .post("/auth/login") + .send(test_user_company_blocked) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_blocked._id}/edit`) + .send({ + name: "Changing Blocked Company", + }) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.COMPANY_BLOCKED + }) + ])); + }); + }); + + describe("Disabled company", () => { + + const test_user_company_disabled = { + email: "disabled@email.com", + password: "password123", + }; + + const test_company_disabled_data = generateTestCompany({ + name: "Test Company God", + isDisabled: true + }); + let test_company_disabled; + + beforeAll(async () => { + test_company_disabled = await Company.create(test_company_disabled_data); + + await Account.create({ + email: test_user_company_disabled.email, + password: await hash(test_user_company_disabled.password), + company: test_company_disabled._id, + }); + }); + + afterAll(async () => { + await Company.deleteMany({ + _id: test_company_disabled._id + }); + await Account.deleteMany({ email: test_user_company_disabled.email }); + }); + + test("Should fail if company is disabled (god)", async () => { + const res = await test_agent + .put(`/company/${test_company_disabled._id}/edit`) + .send(withGodToken({ + name: "Changing Disabled Company", + })) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.COMPANY_DISABLED + }) + ])); + }); + + test("Should fail if company is disabled (user)", async () => { + await test_agent + .post("/auth/login") + .send(test_user_company_disabled) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_disabled._id}/edit`) + .send({ + bio: "As user", + }) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.COMPANY_DISABLED + }) + ])); + }); + }); + + describe("With Offers", () => { + + const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date()).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: false, + requirements: ["The candidate must be tested", "Fluent in testJS"], + ...params, + }); + + const test_user_company_with_offers = { + email: "offers@email.com", + password: "password123", + }; + + const test_company_with_offers_data = generateTestCompany({ + name: "Test Company God", + logo: "https://test.com/logo.png", + }); + let test_company_with_offers; + let offer; + + beforeAll(async () => { + test_company_with_offers = await Company.create(test_company_with_offers_data); + + await Account.create({ + email: test_user_company_with_offers.email, + password: await hash(test_user_company_with_offers.password), + company: test_company_with_offers._id, + }); + + offer = await Offer.create( + generateTestOffer({ + owner: test_company_with_offers._id, + ownerName: test_company_with_offers.name, + ownerLogo: test_company_with_offers.logo, + }) + ); + }); + + afterAll(async () => { + await Company.deleteMany({ + _id: test_company_with_offers._id + }); + await Account.deleteMany({ email: test_user_company_with_offers.email }); + await Offer.deleteMany({ owner: test_company_with_offers._id }); + }); + + test("Offer should be updated", async () => { + + let test_offer = await Offer.findById(offer._id); + + expect(test_offer).not.toHaveProperty("ownerName", edit_payload.name); + expect(test_offer).not.toHaveProperty("contacts", edit_payload.contacts); + + await test_agent + .post("/auth/login") + .send(test_user_company_with_offers) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_with_offers._id}/edit`) + .send({ + name: edit_payload.name, + contacts: edit_payload.contacts, + }) + .expect(StatusCodes.OK); + + expect(res.body).toEqual(expect.objectContaining({ + name: edit_payload.name, + contacts: edit_payload.contacts, + })); + + test_offer = await Offer.findById(offer._id); + + expect(test_offer).toHaveProperty("ownerName", edit_payload.name); + expect(test_offer).toHaveProperty("contacts", edit_payload.contacts); + }); + }); + }); +}); From 5092b3509fb05f34bfcd984dad052a2b24965d17 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Mon, 24 Apr 2023 02:41:09 +0100 Subject: [PATCH 14/30] Cleaned existing company test file --- test/end-to-end/company.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index 15020c37..ea59159d 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -35,15 +35,6 @@ describe("Company endpoint", () => { ...params, }); - const generateTestCompany = (params) => ({ - name: "Big Company", - bio: "Big Company Bio", - logo: "http://awebsite.com/alogo.jpg", - contacts: ["112", "122"], - hasFinishedRegistration: true, - ...params, - }); - describe("PUT /company/:companyId/block", () => { const test_agent = agent(); From 87cd80a7d019afe1b910c32a3816b049d5702b96 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 25 Apr 2023 19:50:44 +0100 Subject: [PATCH 15/30] Finished input validation tests for /company/:id/edit --- codecov.yaml | 6 ++ src/api/middleware/validators/company.js | 5 +- test/end-to-end/company/:id/edit.js | 72 +++++++++++++++++++++--- 3 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 codecov.yaml diff --git a/codecov.yaml b/codecov.yaml new file mode 100644 index 00000000..c04ab9be --- /dev/null +++ b/codecov.yaml @@ -0,0 +1,6 @@ +coverage: + status: + project: + default: + target: 80% + threshold: 5% diff --git a/src/api/middleware/validators/company.js b/src/api/middleware/validators/company.js index 80bf09be..1ba36a9a 100644 --- a/src/api/middleware/validators/company.js +++ b/src/api/middleware/validators/company.js @@ -130,9 +130,10 @@ export const edit = useExpressValidators([ .withMessage(ValidationReasons.TOO_LONG(CompanyConstants.bio.max_length)), body("contacts", ValidationReasons.DEFAULT) .optional() - .customSanitizer(ensureArray) + .isArray().withMessage(ValidationReasons.ARRAY).bail() .isArray({ min: CompanyConstants.contacts.min_length, max: CompanyConstants.contacts.max_length }) - .withMessage(ValidationReasons.ARRAY_SIZE(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length)), + .withMessage(ValidationReasons.ARRAY_SIZE(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length)) + .customSanitizer(ensureArray), body("logo", ValidationReasons.DEFAULT) .optional() .isString().withMessage(ValidationReasons.STRING).bail() diff --git a/test/end-to-end/company/:id/edit.js b/test/end-to-end/company/:id/edit.js index 142a2e20..46138baf 100644 --- a/test/end-to-end/company/:id/edit.js +++ b/test/end-to-end/company/:id/edit.js @@ -3,8 +3,10 @@ import ValidationReasons from "../../../../src/api/middleware/validators/validat import hash from "../../../../src/lib/passwordHashing"; import Account from "../../../../src/models/Account"; import Company from "../../../../src/models/Company"; +import CompanyConstants from "../../../../src/models/constants/Company"; import Offer from "../../../../src/models/Offer"; import withGodToken from "../../../utils/GodToken"; +import ValidatorTester from "../../../utils/ValidatorTester"; import { DAY_TO_MS } from "../../../utils/TimeConstants"; describe("PUT /company/edit", () => { @@ -27,10 +29,21 @@ describe("PUT /company/edit", () => { contacts: ["123", "456"], }; + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + beforeAll(async () => { await Account.deleteMany({}); await Company.deleteMany({}); await Offer.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true, + }); }); afterAll(async () => { @@ -76,6 +89,56 @@ describe("PUT /company/edit", () => { }); }); + describe("Field Validation", () => { + + const company_data = { + name: "Test Company", + logo: "http://awebsite.com/alogo.jpg", + }; + + let company; + + beforeAll(async () => { + company = await Company.create(company_data); + }); + + afterAll(async () => { + await Company.deleteMany({ name: company.name }); + }); + + const EndpointValidatorTester = ValidatorTester( + (params) => request().put(`/company/${company._id}/edit`).send(withGodToken(params)) + ); + const BodyValidatorTester = EndpointValidatorTester("body"); + + describe("name", () => { + const FieldValidatorTester = BodyValidatorTester("name"); + + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMaxLength(CompanyConstants.companyName.max_length); + FieldValidatorTester.hasMinLength(CompanyConstants.companyName.min_length); + }); + + describe("bio", () => { + const FieldValidatorTester = BodyValidatorTester("bio"); + + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMaxLength(CompanyConstants.bio.max_length); + }); + + describe("contacts", () => { + const FieldValidatorTester = BodyValidatorTester("contacts"); + + FieldValidatorTester.mustBeArray(); + // FieldValidatorTester.mustHaveAtLeast(CompanyConstants.contacts.min_length); + FieldValidatorTester.mustBeArrayBetween(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length); + }); + + describe("logo", () => { + // TODO: Add tests for logo when the route has multer middleware to handle file uploads + }); + }); + describe("Without auth", () => { const company_data = generateTestCompany({ @@ -109,10 +172,6 @@ describe("PUT /company/edit", () => { }); describe("With auth", () => { - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; const test_user_company_1 = { email: "company1@email.com", @@ -148,11 +207,6 @@ describe("PUT /company/edit", () => { ]); await Account.create([ - { - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true, - }, { email: test_user_company_1.email, password: await hash(test_user_company_1.password), From 08cd06e5b7407706ddbd992a44740c2125af4b38 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sat, 29 Apr 2023 16:28:30 +0100 Subject: [PATCH 16/30] Restructured application finish tests --- test/end-to-end/company/application/finish.js | 399 +++++++++--------- 1 file changed, 206 insertions(+), 193 deletions(-) diff --git a/test/end-to-end/company/application/finish.js b/test/end-to-end/company/application/finish.js index 204983e0..428c6805 100644 --- a/test/end-to-end/company/application/finish.js +++ b/test/end-to-end/company/application/finish.js @@ -16,77 +16,9 @@ describe("POST /company/application/finish", () => { const test_agent = agent(); - const testUserAdmin = { - email: "admin@email.com", - password: "password123", - }; - - const testUser = { - email: "user@email.com", - password: "password123", - }; - const nonFinishedCompanyData = { - name: "Company Ltd", - }; - let testCompany; - - const testSingleContactUser = { - email: "userSingleContact@email.com", - password: "password123", - }; - const nonFinishedSingleContactCompanyData = { - name: "Company Ltd2", - }; - let testSingleContactCompany; - - const testFinishedUser = { - email: "finishedUsser@email.com", - password: "password123", - }; - const finishedCompanyData = { - name: "Company Ltd", - hasFinishedRegistration: true, - }; - beforeAll(async () => { await Company.deleteMany({}); await Account.deleteMany({}); - - await Account.create({ - email: testUserAdmin.email, - password: await hash(testUserAdmin.password), - isAdmin: true, - }); - - const [ - _testCompany, - testFinishedCompany, - _testSingleContactCompany, - ] = await Company.create([ - nonFinishedCompanyData, - finishedCompanyData, - nonFinishedSingleContactCompanyData, - ]); - testCompany = _testCompany; - testSingleContactCompany = _testSingleContactCompany; - - await Account.create({ - email: testUser.email, - password: await hash(testUser.password), - company: testCompany._id - }); - - await Account.create({ - email: testFinishedUser.email, - password: await hash(testFinishedUser.password), - company: testFinishedCompany._id - }); - - await Account.create({ - email: testSingleContactUser.email, - password: await hash(testSingleContactUser.password), - company: testSingleContactCompany._id - }); }); afterAll(async () => { @@ -94,21 +26,6 @@ describe("POST /company/application/finish", () => { await Account.deleteMany({}); }); - beforeEach(async () => { - // Login - await test_agent - .post("/auth/login") - .send(testUser) - .expect(StatusCodes.OK); - }); - - afterEach(async () => { - // Logout - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - describe("Input Validation", () => { const validationUser = { @@ -215,125 +132,221 @@ describe("POST /company/application/finish", () => { }); }); - test("should fail if making unauthenticated request", async () => { + describe("Without Auth", () => { + test("should fail if making unauthenticated request", async () => { + await test_agent + .post("/company/application/finish") + .expect(StatusCodes.UNAUTHORIZED); + }); + }); - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); + describe("With Auth", () => { - await request() - .post("/company/application/finish") - .expect(StatusCodes.UNAUTHORIZED); - }); + const testUserAdmin = { + email: "admin@email.com", + password: "password123", + }; - test("should fail if authenticated as admin", async () => { - // Login - await test_agent - .post("/auth/login") - .send(testUserAdmin) - .expect(StatusCodes.OK); + const testFinishedUser = { + email: "finishedUsser@email.com", + password: "password123", + }; + const finishedCompanyData = { + name: "Company Ltd", + hasFinishedRegistration: true, + }; - await request() - .post("/company/application/finish") - .expect(StatusCodes.UNAUTHORIZED); - }); + beforeAll(async () => { + await Account.create({ + email: testUserAdmin.email, + password: await hash(testUserAdmin.password), + isAdmin: true, + }); - test("should fail if sending god token", async () => { + const testFinishedCompany = await Company.create(finishedCompanyData); + await Account.create({ + email: testFinishedUser.email, + password: await hash(testFinishedUser.password), + company: testFinishedCompany._id + }); + }); - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); + afterAll(async () => { + await Company.deleteMany({ name: finishedCompanyData.name }); + await Account.deleteMany({ isAdmin: true }); + await Account.deleteMany({ email: testFinishedUser.email }); + }); - await test_agent - .post("/company/application/finish") - .send(withGodToken()) - .expect(StatusCodes.UNAUTHORIZED); - }); + afterEach(async () => { + // Logout + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); - test("should fail if company has already finished registration", async () => { - // Login - await test_agent - .post("/auth/login") - .send(testFinishedUser) - .expect(StatusCodes.OK); - - const res = await test_agent - .post("/company/application/finish") - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining( - [ - expect.objectContaining({ - "msg": ValidationReasons.REGISTRATION_FINISHED, - }), - ] - )); - }); + test("should fail if authenticated as admin", async () => { + // Login + await test_agent + .post("/auth/login") + .send(testUserAdmin) + .expect(StatusCodes.OK); - test("should finish the application with multiple contacts", async () => { - - const contacts = ["contact1", "contact2"]; - - await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", contacts) - .expect(StatusCodes.OK); - - const test_companies = await Company.find({ hasFinishedRegistration: true }); - expect(test_companies).toHaveLength(2); - expect(test_companies).toEqual(expect.arrayContaining( - [ - expect.objectContaining({ - name: testCompany.name, - hasFinishedRegistration: true, - bio: "A very interesting and compelling bio", - contacts, - }), - ] - )); - - const filename = path.join(`${config.upload_folder}/${testCompany.id}.png`); - expect(fs.existsSync(filename)).toBe(true); // TODO: change to async - - // clean up file created - await fs.promises.unlink(filename); - }); + await request() + .post("/company/application/finish") + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should fail if sending god token", async () => { + + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); - test("should finish the application with single contact", async () => { - - await test_agent - .post("/auth/login") - .send(testSingleContactUser) - .expect(StatusCodes.OK); - - const contacts = ["contact1"]; - - await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", contacts) - .expect(StatusCodes.OK); - - const test_companies = await Company.find({ hasFinishedRegistration: true }); - expect(test_companies).toHaveLength(3); - expect(test_companies).toEqual(expect.arrayContaining( - [ - expect.objectContaining({ - name: testSingleContactCompany.name, - hasFinishedRegistration: true, - bio: "A very interesting and compelling bio", - contacts, - }), - ] - )); - - const filename = path.join(`${config.upload_folder}/${testSingleContactCompany.id}.png`); - expect(fs.existsSync(filename)).toBe(true); // TODO: change to async - - // clean up file created - await fs.promises.unlink(filename); + await test_agent + .post("/company/application/finish") + .send(withGodToken()) + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should fail if company has already finished registration", async () => { + // Login + await test_agent + .post("/auth/login") + .send(testFinishedUser) + .expect(StatusCodes.OK); + + const res = await test_agent + .post("/company/application/finish") + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining( + [ + expect.objectContaining({ + "msg": ValidationReasons.REGISTRATION_FINISHED, + }), + ] + )); + }); + + describe("Contacts", () => { + + const testUser = { + email: "user@email.com", + password: "password123", + }; + const nonFinishedCompanyData = { + name: "Company Ltd", + }; + let testCompany; + + const testSingleContactUser = { + email: "userSingleContact@email.com", + password: "password123", + }; + const nonFinishedSingleContactCompanyData = { + name: "Company Ltd2", + }; + let testSingleContactCompany; + + beforeAll(async () => { + [ + testCompany, + testSingleContactCompany + ] = await Company.create([ + nonFinishedCompanyData, + nonFinishedSingleContactCompanyData + ]); + + await Account.create([ + { + email: testUser.email, + password: await hash(testUser.password), + company: testCompany._id + }, + { + email: testSingleContactUser.email, + password: await hash(testSingleContactUser.password), + company: testSingleContactCompany._id + }, + ]); + }); + + afterAll(async () => { + await Company.deleteMany({ name: nonFinishedCompanyData.name }); + await Company.deleteMany({ name: nonFinishedSingleContactCompanyData.name }); + await Account.deleteMany({ email: testUser.email }); + await Account.deleteMany({ email: testSingleContactUser.email }); + }); + + test("should finish the application with multiple contacts", async () => { + await test_agent + .post("/auth/login") + .send(testUser) + .expect(StatusCodes.OK); + + const contacts = ["contact1", "contact2"]; + + await test_agent + .post("/company/application/finish") + .attach("logo", "test/data/logo-niaefeup.png") + .field("bio", "A very interesting and compelling bio") + .field("contacts", contacts) + .expect(StatusCodes.OK); + + const test_companies = await Company.find({ hasFinishedRegistration: true }); + expect(test_companies).toHaveLength(2); + expect(test_companies).toEqual(expect.arrayContaining( + [ + expect.objectContaining({ + name: testCompany.name, + hasFinishedRegistration: true, + bio: "A very interesting and compelling bio", + contacts, + }), + ] + )); + + const filename = path.join(`${config.upload_folder}/${testCompany.id}.png`); + expect(fs.existsSync(filename)).toBe(true); // TODO: change to async + + // clean up file created + await fs.promises.unlink(filename); + }); + + test("should finish the application with single contact", async () => { + await test_agent + .post("/auth/login") + .send(testSingleContactUser) + .expect(StatusCodes.OK); + + const contacts = ["contact1"]; + + await test_agent + .post("/company/application/finish") + .attach("logo", "test/data/logo-niaefeup.png") + .field("bio", "A very interesting and compelling bio") + .field("contacts", contacts) + .expect(StatusCodes.OK); + + const test_companies = await Company.find({ hasFinishedRegistration: true }); + expect(test_companies).toHaveLength(3); + expect(test_companies).toEqual(expect.arrayContaining( + [ + expect.objectContaining({ + name: testSingleContactCompany.name, + hasFinishedRegistration: true, + bio: "A very interesting and compelling bio", + contacts, + }), + ] + )); + + const filename = path.join(`${config.upload_folder}/${testSingleContactCompany.id}.png`); + expect(fs.existsSync(filename)).toBe(true); // TODO: change to async + + // clean up file created + await fs.promises.unlink(filename); + }); + }); }); }); From 60cffeda50b21f0cd7a5580c67da74e7e2c99671 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Sat, 29 Apr 2023 22:38:40 +0100 Subject: [PATCH 17/30] Refactored company unblock tests --- test/end-to-end/company.js | 268 ----------- ...sReachedMaxConcurrentOffersBetweenDates.js | 1 + test/end-to-end/company/:id/unblock.js | 424 +++++++++++++++++- 3 files changed, 424 insertions(+), 269 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index ea59159d..7e4935a1 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -5,7 +5,6 @@ import { COMPANY_BLOCKED_NOTIFICATION, COMPANY_DISABLED_NOTIFICATION, COMPANY_ENABLED_NOTIFICATION, - COMPANY_UNBLOCKED_NOTIFICATION } from "../../src/email-templates/companyManagement"; import EmailService from "../../src/lib/emailService"; import hash from "../../src/lib/passwordHashing"; @@ -295,273 +294,6 @@ describe("Company endpoint", () => { }); }); - describe("PUT /company/:companyId/unblock", () => { - const test_agent = agent(); - - const company_data = { - name: "Company Ltd" - }; - const test_user_1 = { - email: "user1@email.com", - password: "password123", - }; - const test_user_2 = { - email: "user2@email.com", - password: "password123", - }; - const test_user_email = { - email: "test_email@email.com", - password: "password123", - }; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - let test_company_1, test_company_2, test_company_email; - - beforeAll(async () => { - await Company.deleteMany({}); - test_company_1 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - test_company_email = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - await Account.deleteMany({}); - await Account.create({ email: test_user_1.email, password: await hash(test_user_1.password), company: test_company_1._id }); - await Account.create({ email: test_user_2.email, password: await hash(test_user_2.password), company: test_company_2._id }); - await Account.create({ - email: test_user_email.email, - password: await hash(test_user_email.password), - company: test_company_email._id - }); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - - test("should fail if not logged in", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("should fail if logged in as company", async () => { - await test_agent - .post("/auth/login") - .send(test_user_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("should allow if logged in as admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - expect(res.body).not.toHaveProperty("adminReason"); - }); - - test("should fail if not a valid id", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put("/company/123/unblock") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("should fail if company does not exist", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/unblock`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - - test("should allow with god token", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_2.id}/unblock`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - }); - - test("should send an email to the company user when it is unblocked", async () => { - await test_agent - .del("/auth/login"); - await test_agent - .put(`/company/${test_company_email._id}/unblock`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_UNBLOCKED_NOTIFICATION( - test_company_email.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_email.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - - - describe("testing with offers", () => { - let test_company; - - beforeEach(async () => { - const company = { - email: "test_company_email_@email.com", - password: "password123", - }; - - await Company.deleteMany({}); - test_company = await Company.create({ - name: company_data.name, - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg" - }); - - - await Account.deleteMany({}); - await Account.create({ - email: company.email, - password: await hash(company.password), - company: test_company._id - }); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - }); - - test("should unblock offers blocked by company block", async () => { - - const offers = Array(3).fill(await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED - })); - - const res = await test_agent - .put(`/company/${test_company.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - - - for (const offer of offers) { - expect(await Offer.findById(offer._id)).toHaveProperty("isHidden", false); - } - }); - - test("should not unblock offers hidden by company request", async () => { - - const offer = await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_REQUEST - }); - - const res = await test_agent - .put(`/company/${test_company.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - - - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.COMPANY_REQUEST); - expect(updated_offer).toHaveProperty("isHidden", true); - - }); - - test("should not unblock offers hidden by admin request", async () => { - - const offer = await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK - }); - - const res = await test_agent - .put(`/company/${test_company.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - - - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.ADMIN_BLOCK); - expect(updated_offer).toHaveProperty("isHidden", true); - - }); - - }); - - - }); - describe("PUT /company/enable", () => { let disabled_test_company_1, disabled_test_company_2, disabled_test_company_3, disabled_test_company_4, disabled_test_company_mail; diff --git a/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js b/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js index 277c3a0b..4977d437 100644 --- a/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js +++ b/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js @@ -203,6 +203,7 @@ describe("GET /company/:companyId/hasReachedMaxConcurrentOffersBetweenDates", () expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); expect(res.body).toHaveProperty("errors"); + // TODO: change to use expect's helpers expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); }); diff --git a/test/end-to-end/company/:id/unblock.js b/test/end-to-end/company/:id/unblock.js index 3db9e2ea..6c78c58a 100644 --- a/test/end-to-end/company/:id/unblock.js +++ b/test/end-to-end/company/:id/unblock.js @@ -1 +1,423 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import { COMPANY_UNBLOCKED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; +import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import OfferConstants from "../../../../src/models/constants/Offer"; +import withGodToken from "../../../utils/GodToken"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; + +jest.mock("../../../../src/lib/emailService"); +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); + +describe("PUT /company/:companyId/unblock", () => { + const test_agent = agent(); + + beforeAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + await Offer.deleteMany({}); + }); + + afterAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + await Offer.deleteMany({}); + }); + + describe("ID Validation", () => { + test("should fail if not a valid id", async () => { + const res = await test_agent + .put("/company/123/unblock") + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.OBJECT_ID + }) + ])); + }); + + test("should fail if company does not exist", async () => { + const id = "111111111111111111111111"; + + const res = await test_agent + .put(`/company/${id}/unblock`) + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND(id) + }) + ])); + }); + }); + + describe("Without auth", () => { + + const test_user = { + email: "email@email.com", + password: "password123", + }; + const test_company_data = { + name: "Company Ltd", + hasFinishedRegistration: true, + isBlocked: true + }; + + let test_company; + + beforeAll(async () => { + test_company = await Company.create(test_company_data); + + await Account.create({ + email: test_user.email, + password: await hash(test_user.password), + company: test_company._id + }); + }); + + afterAll(async () => { + await Account.deleteMany({ email: test_user.email }); + await Company.deleteMany({ _id: test_company._id }); + }); + + test("should fail if not logged in", async () => { + await test_agent + .del("/auth/login"); + + const res = await test_agent + .put(`/company/${test_company.id}/unblock`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); + }); + + describe("With auth", () => { + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const test_user_1 = { + email: "company1@email.com", + password: "password123", + }; + const test_company_1_data = { + name: "Company Ltd", + hasFinishedRegistration: true, + isBlocked: true + }; + + const test_user_2 = { + email: "company2@email.com", + password: "password123", + }; + const test_company_2_data = { + name: "Company Ltd", + hasFinishedRegistration: true, + isBlocked: true + }; + + const test_user_email = { + email: "companyemail@email.com", + password: "password123", + }; + const test_company_email_data = { + name: "Company Ltd", + hasFinishedRegistration: true, + isBlocked: true + }; + + let test_company_1, test_company_2, test_company_email; + + beforeAll(async () => { + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + [ + test_company_1, + test_company_2, + test_company_email, + ] = await Company.create([ + test_company_1_data, + test_company_2_data, + test_company_email_data, + ]); + + await Account.create([ + { + email: test_user_1.email, + password: await hash(test_user_1.password), + company: test_company_1._id + }, + { + email: test_user_2.email, + password: await hash(test_user_2.password), + company: test_company_2._id + }, + { + email: test_user_email.email, + password: await hash(test_user_email.password), + company: test_company_email._id + } + ]); + }); + + afterAll(async () => { + await Account.deleteMany({ email: test_user_admin.email }); + await Account.deleteMany({ email: test_user_1.email }); + await Account.deleteMany({ email: test_user_2.email }); + + await Company.deleteMany({ name: test_company_1_data.name }); + await Company.deleteMany({ name: test_company_2_data.name }); + }); + + afterEach(async () => { + await test_agent + .del("/auth/login") + .expect(StatusCodes.OK); + }); + + test("should fail if logged in as company", async () => { + await test_agent + .post("/auth/login") + .send(test_user_1) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1.id}/unblock`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); + + test("should allow if logged in as admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1.id}/unblock`) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("isBlocked", false); + expect(res.body).not.toHaveProperty("adminReason"); + }); + + test("should allow with god token", async () => { + const res = await test_agent + .put(`/company/${test_company_2.id}/unblock`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("isBlocked", false); + }); + + test("should send an email to the company user when it is unblocked", async () => { + + await test_agent + .put(`/company/${test_company_email._id}/unblock`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + const emailOptions = COMPANY_UNBLOCKED_NOTIFICATION( + test_company_email.name + ); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_user_email.email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); + + describe("With offers", () => { + + const companyData = { + name: "Company Ltd", + hasFinishedRegistration: true, + isBlocked: true, + logo: "http://logo.com/alogo.png" + }; + + const test_user_with_company_hidden_offer = { + email: "with_company_hidden_offer@email.com", + password: "password123", + }; + + const test_user_with_admin_hidden_offer = { + email: "with_admin_hidden_offer@email.com", + password: "password123", + }; + + const test_user_with_blocked_company_hidden_offer = { + email: "with_blocked_company_hidden_offer@email.com", + password: "password123", + }; + + let company_with_company_hidden_offer, company_with_admin_hidden_offer, company_with_blocked_company_hidden_offer; + + const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date()).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: true, + requirements: ["The candidate must be tested", "Fluent in testJS"], + ...params, + }); + + beforeAll(async () => { + [ + company_with_company_hidden_offer, + company_with_admin_hidden_offer, + company_with_blocked_company_hidden_offer + ] = await Company.create([ + companyData, + companyData, + companyData, + ]); + + await Account.create([ + { + email: test_user_with_company_hidden_offer.email, + password: await hash(test_user_with_company_hidden_offer.password), + company: company_with_company_hidden_offer._id + }, + { + email: test_user_with_admin_hidden_offer.email, + password: await hash(test_user_with_admin_hidden_offer.password), + company: company_with_admin_hidden_offer._id + }, + { + email: test_user_with_blocked_company_hidden_offer.email, + password: await hash(test_user_with_blocked_company_hidden_offer.password), + company: company_with_blocked_company_hidden_offer._id + } + ]); + + await Offer.create( + generateTestOffer({ + owner: company_with_company_hidden_offer._id, + ownerName: company_with_company_hidden_offer.name, + ownerLogo: company_with_company_hidden_offer.logo, + hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_REQUEST + }) + ); + + await Offer.create( + generateTestOffer({ + owner: company_with_admin_hidden_offer._id, + ownerName: company_with_admin_hidden_offer.name, + ownerLogo: company_with_admin_hidden_offer.logo, + hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK + }) + ); + + await Offer.create( + generateTestOffer({ + owner: company_with_blocked_company_hidden_offer._id, + ownerName: company_with_blocked_company_hidden_offer.name, + ownerLogo: company_with_blocked_company_hidden_offer.logo, + hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED + }) + ); + }); + + afterAll(async () => { + await Company.deleteMany({ name: companyData.name }); + + await Account.deleteMany({ email: test_user_with_company_hidden_offer.email }); + await Account.deleteMany({ email: test_user_with_admin_hidden_offer.email }); + await Account.deleteMany({ email: test_user_with_blocked_company_hidden_offer.email }); + + await Offer.deleteMany({ owner: company_with_company_hidden_offer._id }); + await Offer.deleteMany({ owner: company_with_admin_hidden_offer._id }); + await Offer.deleteMany({ owner: company_with_blocked_company_hidden_offer._id }); + }); + + test("should unblock offers blocked by company block", async () => { + const res = await test_agent + .put(`/company/${company_with_blocked_company_hidden_offer.id}/unblock`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("isBlocked", false); + + const offers = await Offer.find({ owner: company_with_blocked_company_hidden_offer._id }); + expect(offers).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ + isHidden: true, + }) + ])); + }); + + test("should not unblock offers hidden by admin request", async () => { + const res = await test_agent + .put(`/company/${company_with_admin_hidden_offer.id}/unblock`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("isBlocked", false); + + const offers = await Offer.find({ owner: company_with_admin_hidden_offer._id }); + expect(offers).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ + isHidden: false, + }) + ])); + }); + + test("should not unblock offers blocked by company request", async () => { + const res = await test_agent + .put(`/company/${company_with_company_hidden_offer.id}/unblock`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("isBlocked", false); + + const offers = await Offer.find({ owner: company_with_company_hidden_offer._id }); + expect(offers).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ + isHidden: false, + }) + ])); + }); + }); + }); +}); From a37984a0e3d1437f5ad92397923f4bcac13a49be Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 20 Jun 2023 00:57:14 +0100 Subject: [PATCH 18/30] Refactored company disable tests --- test/end-to-end/company.js | 1 - test/end-to-end/company/:id/disable.js | 309 ++++++++++++++++++++++++- 2 files changed, 308 insertions(+), 2 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index 7e4935a1..e8c7972b 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -3,7 +3,6 @@ import { ErrorTypes } from "../../src/api/middleware/errorHandler"; import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; import { COMPANY_BLOCKED_NOTIFICATION, - COMPANY_DISABLED_NOTIFICATION, COMPANY_ENABLED_NOTIFICATION, } from "../../src/email-templates/companyManagement"; import EmailService from "../../src/lib/emailService"; diff --git a/test/end-to-end/company/:id/disable.js b/test/end-to-end/company/:id/disable.js index 3db9e2ea..7784d490 100644 --- a/test/end-to-end/company/:id/disable.js +++ b/test/end-to-end/company/:id/disable.js @@ -1 +1,308 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import { COMPANY_DISABLED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import OfferConstants from "../../../../src/models/constants/Offer"; +import withGodToken from "../../../utils/GodToken"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; +import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; + +jest.mock("../../../../src/lib/emailService"); +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); + +describe("PUT /company/disable", () => { + + const test_agent = agent(); + + describe("ID Validation", () => { + test("Should fail if id is not a valid ObjectID", async () => { + const id = "123"; + const res = await test_agent + .put(`/company/${id}/disable`) + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "location": "params", + "msg": ValidationReasons.OBJECT_ID, + "param": "companyId", + "value": id + }) + ])); + }); + + test("Should fail if id is not a valid company", async () => { + const id = "111111111111111111111111"; + + const res = await test_agent + .put(`/company/${id}/disable`) + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "location": "params", + "msg": ValidationReasons.COMPANY_NOT_FOUND(id), + "param": "companyId", + "value": id + }) + ])); + }); + }); + + describe("Without auth", () => { + + let company; + const company_data = { + name: "test-company-no-auth", + hasFinishedRegistration: true + }; + + beforeAll(async () => { + company = await Company.create(company_data); + }); + + afterAll(async () => { + await Company.deleteMany({ name: company_data }); + }); + + test("Should not disable company if not authenticated", async () => { + const res = await test_agent + .put(`/company/${company._id}/disable`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); + }); + + describe("With auth", () => { + + let test_company_1, test_company_2, test_company_mail; + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + const test_user_company_1 = { + email: "company1@email.com", + password: "password123", + }; + const test_user_company_2 = { + email: "company2@email.com", + password: "password123", + }; + + const test_user_company_mail = { + email: "company_mail@email.com", + password: "password123", + }; + + beforeAll(async () => { + [test_company_1, test_company_2, test_company_mail] = await Company.create([ + { + name: "test-company-1", + hasFinishedRegistration: true + }, { + name: "test-company-2", + hasFinishedRegistration: true + }, { + name: "test-company-main", + hasFinishedRegistration: true + } + ]); + + await Account.create([ + { + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }, { + email: test_user_company_1.email, + password: await hash(test_user_company_1.password), + company: test_company_1._id + }, { + email: test_user_company_2.email, + password: await hash(test_user_company_2.password), + company: test_company_2._id + }, { + email: test_user_company_mail.email, + password: await hash(test_user_company_mail.password), + company: test_company_mail._id + } + ]); + }); + + afterAll(async () => { + await Company.deleteMany({ name: { $in: [test_company_1._id, test_company_2._id] } }); + await Account.deleteMany({ + email: { + $in: [ + test_user_admin.email, + test_user_company_1.email, + test_user_company_2.email, + test_user_company_mail.email + ] + } + }); + }); + + test("should fail to disable company if logged as different company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_2) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1._id}/disable`) + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS + }) + ])); + }); + + test("should fail to disable company if logged as admin", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1._id}/disable`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); + + test("Should disable company if god token is sent", async () => { + + const res = await test_agent + .put(`/company/${test_company_2._id}/disable`) + .send(withGodToken()); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.isDisabled).toBe(true); + }); + + test("Should disable company if logged as same company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_1) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1._id}/disable`); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.isDisabled).toBe(true); + }); + + describe("With offers", () => { + const assertOfferList = (offers, expectedIsHidden, expectedHiddenReason) => { + expect(offers.every(({ isHidden }) => isHidden === expectedIsHidden)).toBe(true); + expect(offers.every(({ hiddenReason }) => hiddenReason === expectedHiddenReason)).toBe(true); + }; + + let company_with_offers; + const company_with_offers_data = { + name: "test-company-with-offers", + logo: "http://awebsite.com/alogo.jpg", + hasFinishedRegistration: true + }; + const account_with_offers_data = { + email: "withOffers@mail.com", + password: "password123", + }; + + beforeAll(async () => { + company_with_offers = await Company.create(company_with_offers_data); + await Account.create({ + email: account_with_offers_data.email, + password: await hash(account_with_offers_data.password), + company: company_with_offers._id + }); + + const offer = { + title: "Test Offer", + publishDate: new Date(Date.now()), + publishEndDate: new Date(Date.now() + (DAY_TO_MS)), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 2, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + requirements: ["The candidate must be tested", "Fluent in testJS"], + owner: company_with_offers._id, + ownerName: company_with_offers.name, + ownerLogo: company_with_offers.logo, + }; + + await Offer.create([offer, offer]); + }); + + afterAll(async () => { + await Account.deleteMany({ email: account_with_offers_data.email }); + await Company.deleteMany({ name: company_with_offers_data.name }); + await Offer.deleteMany({ owner: company_with_offers._id }); + }); + + test("should change offers' 'isHidden' on company disable", async () => { + + const offersBefore = await Offer.find({ owner: company_with_offers._id }); + + assertOfferList(offersBefore, false, undefined); + + const res = await test_agent + .put(`/company/${company_with_offers._id}/disable`) + .send(withGodToken()); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.isDisabled).toBe(true); + + const offersAfter = await Offer.find({ owner: company_with_offers._id }); + + assertOfferList(offersAfter, true, OfferConstants.HiddenOfferReasons.COMPANY_DISABLED); + }); + }); + + test("should send an email to the company user when it is disabled", async () => { + await test_agent + .put(`/company/${test_company_mail._id}/disable`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + const emailOptions = COMPANY_DISABLED_NOTIFICATION( + test_company_mail.name + ); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_user_company_mail.email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); + }); +}); From 17cbe24ac73094b7028ad1ee55623c40307ab790 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 20 Jun 2023 01:09:56 +0100 Subject: [PATCH 19/30] Started reworking company enablement tests --- test/end-to-end/company.js | 1 - test/end-to-end/company/:id/enable.js | 314 +++++++++++++++++++++++++- 2 files changed, 313 insertions(+), 2 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index e8c7972b..329d3d64 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -3,7 +3,6 @@ import { ErrorTypes } from "../../src/api/middleware/errorHandler"; import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; import { COMPANY_BLOCKED_NOTIFICATION, - COMPANY_ENABLED_NOTIFICATION, } from "../../src/email-templates/companyManagement"; import EmailService from "../../src/lib/emailService"; import hash from "../../src/lib/passwordHashing"; diff --git a/test/end-to-end/company/:id/enable.js b/test/end-to-end/company/:id/enable.js index 3db9e2ea..0cfa5f22 100644 --- a/test/end-to-end/company/:id/enable.js +++ b/test/end-to-end/company/:id/enable.js @@ -1 +1,313 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import OfferConstants from "../../../../src/models/constants/Offer"; +import Account from "../../../../src/models/Account"; +import hash from "../../../../src/lib/passwordHashing"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; +import withGodToken from "../../../utils/GodToken"; +import { COMPANY_ENABLED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; +import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; + +jest.mock("../../../../src/lib/emailService"); +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); + +describe("PUT /company/enable", () => { + + let disabled_test_company_1, disabled_test_company_2, disabled_test_company_3, disabled_test_company_4, disabled_test_company_mail; + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + const test_user_company_1 = { + email: "company1@email.com", + password: "password123", + }; + const test_user_company_2 = { + email: "company2@email.com", + password: "password123", + }; + const test_user_company_3 = { + email: "company3@email.com", + password: "password123", + }; + const test_user_company_4 = { + email: "company4@email.com", + password: "password123", + }; + const test_user_mail = { + email: "company_mail@email.com", + password: "password123", + }; + + const test_agent = agent(); + + beforeAll(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + + await Company.deleteMany({}); + + [ + disabled_test_company_1, + disabled_test_company_2, + disabled_test_company_3, + disabled_test_company_4, + disabled_test_company_mail + ] = await Company.create([ + { + name: "disabled-test-company-1", + isDisabled: true, + hasFinishedRegistration: true + }, { + name: "disabled-test-company-2", + isDisabled: true, + hasFinishedRegistration: true + }, { + name: "disabled-test-company-3", + isDisabled: true, + hasFinishedRegistration: true + }, { + name: "disabled-test-company-4", + logo: "http://awebsite.com/alogo.jpg", + isDisabled: true, + hasFinishedRegistration: true + }, { + name: "disabled-test-company-mail", + isDisabled: true, + hasFinishedRegistration: true + } + ]); + + await Account.deleteMany({}); + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + await Account.create({ + email: test_user_company_1.email, + password: await hash(test_user_company_1.password), + company: disabled_test_company_1._id + }); + await Account.create({ + email: test_user_company_2.email, + password: await hash(test_user_company_2.password), + company: disabled_test_company_2._id + }); + await Account.create({ + email: test_user_company_3.email, + password: await hash(test_user_company_3.password), + company: disabled_test_company_3._id + }); + await Account.create({ + email: test_user_company_4.email, + password: await hash(test_user_company_4.password), + company: disabled_test_company_4._id + }); + await Account.create({ + email: test_user_mail.email, + password: await hash(test_user_mail.password), + company: disabled_test_company_mail._id + }); + + const offer = { + title: "Test Offer", + publishDate: new Date(Date.now()), + publishEndDate: new Date(Date.now() + (DAY_TO_MS)), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + requirements: ["The candidate must be tested", "Fluent in testJS"], + owner: disabled_test_company_4._id, + ownerName: disabled_test_company_4.name, + ownerLogo: disabled_test_company_4.logo, + isHidden: true, + hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_DISABLED, + }; + + await Offer.create([offer, offer]); + + }); + + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + describe("Id validation", () => { + test("Should fail if using invalid id", async () => { + const res = await test_agent + .put("/company/123/enable") + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.OBJECT_ID + }) + ])); + }); + + test("Should fail if company does not exist", async () => { + const id = "111111111111111111111111"; + const res = await test_agent + .put(`/company/${id}/enable`) + .send(withGodToken()) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND(id) + }) + ])); + }); + }); + + describe("Without auth", () => { + + let disabled_company; + const disabled_company_data = { + name: "disabled-company", + isDisabled: true, + hasFinishedRegistration: true + }; + + beforeAll(async () => { + disabled_company = await Company.create(disabled_company_data); + }); + + afterAll(async () => { + await Company.deleteMany({ name: disabled_company_data.name }); + }); + + test("should fail to enable if not logged", async () => { + + const res = await test_agent + .put(`/company/${disabled_company._id}/enable`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); + + }); + + describe("With auth", () => { }); + + test("should fail to enable if logged as different company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_2) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${disabled_test_company_1._id}/enable`); + + expect(res.status).toBe(StatusCodes.FORBIDDEN); + expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); + expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); + + }); + + test("Should enable company if god token is sent", async () => { + + const res = await test_agent + .put(`/company/${disabled_test_company_3._id}/enable`) + .send(withGodToken()); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.isDisabled).toBe(false); + }); + + test("Should enable company if logged as admin", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${disabled_test_company_2._id}/enable`); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.isDisabled).toBe(false); + }); + + test("Should enable company if logged as same company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company_1) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${disabled_test_company_1._id}/enable`); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.isDisabled).toBe(false); + }); + + test("should change offers' 'isHidden' on company enable", async () => { + + const offersBefore = await Offer.find({ owner: disabled_test_company_4._id }); + + expect(offersBefore.every(({ isHidden }) => isHidden === true)).toBe(true); + expect(offersBefore.every( + ({ hiddenReason }) => hiddenReason === OfferConstants.HiddenOfferReasons.COMPANY_DISABLED) + ).toBe(true); + + const res = await test_agent + .put(`/company/${disabled_test_company_4._id}/enable`) + .send(withGodToken()); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.isDisabled).toBe(false); + + const offersAfter = await Offer.find({ owner: disabled_test_company_4._id }); + + expect(offersAfter.every(({ isHidden }) => isHidden === false)).toBe(true); + expect(offersAfter.every(({ hiddenReason }) => hiddenReason === undefined)).toBe(true); + }); + + test("should send an email to the company user when it is enabled", async () => { + await test_agent + .put(`/company/${disabled_test_company_mail._id}/enable`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + const emailOptions = COMPANY_ENABLED_NOTIFICATION( + disabled_test_company_mail.name + ); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_user_mail.email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); +}); From 01fe2b39f829e083f50383d990b49ad610553f09 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 20 Jun 2023 01:40:22 +0100 Subject: [PATCH 20/30] Finished refactoring company enablement tests --- test/end-to-end/company/:id/disable.js | 18 + test/end-to-end/company/:id/enable.js | 438 ++++++++++++++----------- 2 files changed, 263 insertions(+), 193 deletions(-) diff --git a/test/end-to-end/company/:id/disable.js b/test/end-to-end/company/:id/disable.js index 7784d490..7f85cab7 100644 --- a/test/end-to-end/company/:id/disable.js +++ b/test/end-to-end/company/:id/disable.js @@ -18,6 +18,18 @@ describe("PUT /company/disable", () => { const test_agent = agent(); + beforeAll(async () => { + await Company.deleteMany({}); + await Offer.deleteMany({}); + await Account.deleteMany({}); + }); + + afterAll(async () => { + await Company.deleteMany({}); + await Offer.deleteMany({}); + await Account.deleteMany({}); + }); + describe("ID Validation", () => { test("Should fail if id is not a valid ObjectID", async () => { const id = "123"; @@ -154,6 +166,12 @@ describe("PUT /company/disable", () => { }); }); + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + test("should fail to disable company if logged as different company", async () => { await test_agent diff --git a/test/end-to-end/company/:id/enable.js b/test/end-to-end/company/:id/enable.js index 0cfa5f22..04728d0b 100644 --- a/test/end-to-end/company/:id/enable.js +++ b/test/end-to-end/company/:id/enable.js @@ -16,137 +16,18 @@ jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(( describe("PUT /company/enable", () => { - let disabled_test_company_1, disabled_test_company_2, disabled_test_company_3, disabled_test_company_4, disabled_test_company_mail; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - const test_user_company_3 = { - email: "company3@email.com", - password: "password123", - }; - const test_user_company_4 = { - email: "company4@email.com", - password: "password123", - }; - const test_user_mail = { - email: "company_mail@email.com", - password: "password123", - }; - const test_agent = agent(); beforeAll(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - await Company.deleteMany({}); - - [ - disabled_test_company_1, - disabled_test_company_2, - disabled_test_company_3, - disabled_test_company_4, - disabled_test_company_mail - ] = await Company.create([ - { - name: "disabled-test-company-1", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-2", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-3", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-4", - logo: "http://awebsite.com/alogo.jpg", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-mail", - isDisabled: true, - hasFinishedRegistration: true - } - ]); - + await Offer.deleteMany({}); await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: disabled_test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: disabled_test_company_2._id - }); - await Account.create({ - email: test_user_company_3.email, - password: await hash(test_user_company_3.password), - company: disabled_test_company_3._id - }); - await Account.create({ - email: test_user_company_4.email, - password: await hash(test_user_company_4.password), - company: disabled_test_company_4._id - }); - await Account.create({ - email: test_user_mail.email, - password: await hash(test_user_mail.password), - company: disabled_test_company_mail._id - }); - - const offer = { - title: "Test Offer", - publishDate: new Date(Date.now()), - publishEndDate: new Date(Date.now() + (DAY_TO_MS)), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - requirements: ["The candidate must be tested", "Fluent in testJS"], - owner: disabled_test_company_4._id, - ownerName: disabled_test_company_4.name, - ownerLogo: disabled_test_company_4.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_DISABLED, - }; - - await Offer.create([offer, offer]); - - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); }); afterAll(async () => { - await Account.deleteMany({}); await Company.deleteMany({}); + await Offer.deleteMany({}); + await Account.deleteMany({}); }); describe("Id validation", () => { @@ -212,102 +93,273 @@ describe("PUT /company/enable", () => { }) ])); }); - }); - describe("With auth", () => { }); + describe("With auth", () => { - test("should fail to enable if logged as different company", async () => { + let disabled_test_company_1, disabled_test_company_2, disabled_test_company_3, disabled_test_company_mail; - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(StatusCodes.OK); + const disabled_test_company_1_data = { + name: "disabled-test-company-1", + isDisabled: true, + hasFinishedRegistration: true + }; + const disabled_test_company_2_data = { + name: "disabled-test-company-2", + isDisabled: true, + hasFinishedRegistration: true + }; + const disabled_test_company_3_data = { + name: "disabled-test-company-3", + isDisabled: true, + hasFinishedRegistration: true + }; + const disabled_test_company_mail_data = { + name: "disabled-test-company-mail", + isDisabled: true, + hasFinishedRegistration: true + }; - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); + const disabled_account_1 = { + email: "disabled1@email.com", + password: "password123" + }; + const disabled_account_2 = { + email: "disabled2@email.com", + password: "password123" + }; + const disabled_account_3 = { + email: "disabled3@email.com", + password: "password123" + }; + const disabled_account_email = { + email: "disabled.mail@email.com", + password: "password123" + }; + const test_user_admin = { + email: "admin@email.com", + password: "password123" + }; - expect(res.status).toBe(StatusCodes.FORBIDDEN); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); + beforeAll(async () => { + [ + disabled_test_company_1, + disabled_test_company_2, + disabled_test_company_3, + disabled_test_company_mail + ] = await Company.create([ + disabled_test_company_1_data, + disabled_test_company_2_data, + disabled_test_company_3_data, + disabled_test_company_mail_data + ]); + + await Account.create([ + { + email: disabled_account_1.email, + password: await hash(disabled_account_1.password), + company: disabled_test_company_1._id + }, + { + email: disabled_account_2.email, + password: await hash(disabled_account_2.password), + company: disabled_test_company_2._id + }, + { + email: disabled_account_3.email, + password: await hash(disabled_account_3.password), + company: disabled_test_company_3._id + }, + { + email: disabled_account_email.email, + password: await hash(disabled_account_email.password), + company: disabled_test_company_mail._id + }, + { + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true, + } + ]); + }); - }); + afterAll(async () => { + await Company.deleteMany({ + _id: { + $in: [ + disabled_account_1._id, + disabled_account_2._id, + disabled_account_3._id, + disabled_account_email._id + ] + } + }); + await Account.deleteMany({ + email: { + $in: [ + disabled_account_1.email, + disabled_account_2.email, + disabled_account_3.email, + disabled_account_email.email, + test_user_admin.email + ] + } + }); + }); - test("Should enable company if god token is sent", async () => { + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); - const res = await test_agent - .put(`/company/${disabled_test_company_3._id}/enable`) - .send(withGodToken()); + test("should fail to enable if logged as different company", async () => { - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.isDisabled).toBe(false); - }); + await test_agent + .post("/auth/login") + .send(disabled_account_2) + .expect(StatusCodes.OK); - test("Should enable company if logged as admin", async () => { + const res = await test_agent + .put(`/company/${disabled_test_company_1._id}/enable`) + .expect(StatusCodes.FORBIDDEN); - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS + }) + ])); + }); - const res = await test_agent - .put(`/company/${disabled_test_company_2._id}/enable`); + test("Should enable company if god token is sent", async () => { - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.isDisabled).toBe(false); - }); + const res = await test_agent + .put(`/company/${disabled_test_company_3._id}/enable`) + .send(withGodToken()) + .expect(StatusCodes.OK); - test("Should enable company if logged as same company", async () => { + expect(res.body.isDisabled).toBe(false); + }); - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(StatusCodes.OK); + test("Should enable company if logged as admin", async () => { - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.isDisabled).toBe(false); - }); + const res = await test_agent + .put(`/company/${disabled_test_company_2._id}/enable`) + .expect(StatusCodes.OK); - test("should change offers' 'isHidden' on company enable", async () => { + expect(res.body.isDisabled).toBe(false); + }); - const offersBefore = await Offer.find({ owner: disabled_test_company_4._id }); + test("Should enable company if logged as same company", async () => { - expect(offersBefore.every(({ isHidden }) => isHidden === true)).toBe(true); - expect(offersBefore.every( - ({ hiddenReason }) => hiddenReason === OfferConstants.HiddenOfferReasons.COMPANY_DISABLED) - ).toBe(true); + await test_agent + .post("/auth/login") + .send(disabled_account_1) + .expect(StatusCodes.OK); - const res = await test_agent - .put(`/company/${disabled_test_company_4._id}/enable`) - .send(withGodToken()); + const res = await test_agent + .put(`/company/${disabled_test_company_1._id}/enable`) + .expect(StatusCodes.OK); - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.isDisabled).toBe(false); + expect(res.body.isDisabled).toBe(false); + }); - const offersAfter = await Offer.find({ owner: disabled_test_company_4._id }); + describe("With offers", () => { + const assertOfferList = (offers, expectedIsHidden, expectedHiddenReason) => { + expect(offers.every(({ isHidden }) => isHidden === expectedIsHidden)).toBe(true); + expect(offers.every(({ hiddenReason }) => hiddenReason === expectedHiddenReason)).toBe(true); + }; - expect(offersAfter.every(({ isHidden }) => isHidden === false)).toBe(true); - expect(offersAfter.every(({ hiddenReason }) => hiddenReason === undefined)).toBe(true); - }); + let disabled_company_with_offers; + const disabled_company_with_offers_data = { + name: "company-with-offers", + isDisabled: true, + hasFinishedRegistration: true, + logo: "http://awebsite.com/alogo.jpg", + }; + const account_with_offers = { + email: "offers@email.com", + password: "password123", + }; + + beforeAll(async () => { + disabled_company_with_offers = await Company.create(disabled_company_with_offers_data); + await Account.create({ + email: account_with_offers.email, + password: await hash(account_with_offers.password), + company: disabled_company_with_offers._id + }); + + const offer = { + title: "Test Offer", + publishDate: new Date(Date.now()), + publishEndDate: new Date(Date.now() + (DAY_TO_MS)), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 2, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + requirements: ["The candidate must be tested", "Fluent in testJS"], + isHidden: true, + hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_DISABLED, + owner: disabled_company_with_offers._id, + ownerName: disabled_company_with_offers.name, + ownerLogo: disabled_company_with_offers.logo, + }; + + await Offer.create([offer, offer]); + }); + + afterAll(async () => { + await Account.delete({ email: account_with_offers.email }); + await Company.deleteMany({ name: disabled_company_with_offers_data.name }); + await Offer.deleteMany({ owner: disabled_company_with_offers._id }); + }); + + test("should change offers' 'isHidden' on company enable", async () => { + + const offersBefore = await Offer.find({ owner: disabled_company_with_offers._id }); + + assertOfferList(offersBefore, true, OfferConstants.HiddenOfferReasons.COMPANY_DISABLED); + + const res = await test_agent + .put(`/company/${disabled_company_with_offers._id}/enable`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + expect(res.body.isDisabled).toBe(false); + + const offersAfter = await Offer.find({ owner: disabled_company_with_offers._id }); + + assertOfferList(offersAfter, false, undefined); + }); + }); - test("should send an email to the company user when it is enabled", async () => { - await test_agent - .put(`/company/${disabled_test_company_mail._id}/enable`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - const emailOptions = COMPANY_ENABLED_NOTIFICATION( - disabled_test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); + test("should send an email to the company user when it is enabled", async () => { + await test_agent + .put(`/company/${disabled_test_company_mail._id}/enable`) + .send(withGodToken()) + .expect(StatusCodes.OK); + + const emailOptions = COMPANY_ENABLED_NOTIFICATION( + disabled_test_company_mail.name + ); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: disabled_account_email.email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); }); }); From e754c8897f20e6176bbdc03c0d371a8543e8026f Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 20 Jun 2023 01:59:14 +0100 Subject: [PATCH 21/30] Started reworking company block tests --- test/end-to-end/company.js | 518 +------------------------- test/end-to-end/company/:id/block.js | 294 ++++++++++++++- test/end-to-end/company/:id/delete.js | 2 +- 3 files changed, 303 insertions(+), 511 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index 329d3d64..07e4d9aa 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -12,6 +12,7 @@ import Offer from "../../src/models/Offer"; import OfferConstants from "../../src/models/constants/Offer"; import withGodToken from "../utils/GodToken"; import { DAY_TO_MS } from "../utils/TimeConstants"; +import { MAX_FILE_SIZE_MB } from "../../src/api/middleware/utils"; describe("Company endpoint", () => { @@ -292,518 +293,17 @@ describe("Company endpoint", () => { }); }); - describe("PUT /company/enable", () => { - - let disabled_test_company_1, disabled_test_company_2, disabled_test_company_3, disabled_test_company_4, disabled_test_company_mail; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - const test_user_company_3 = { - email: "company3@email.com", - password: "password123", - }; - const test_user_company_4 = { - email: "company4@email.com", - password: "password123", - }; - const test_user_mail = { - email: "company_mail@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeAll(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [ - disabled_test_company_1, - disabled_test_company_2, - disabled_test_company_3, - disabled_test_company_4, - disabled_test_company_mail - ] = await Company.create([ - { - name: "disabled-test-company-1", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-2", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-3", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-4", - logo: "http://awebsite.com/alogo.jpg", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-mail", - isDisabled: true, - hasFinishedRegistration: true - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: disabled_test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: disabled_test_company_2._id - }); - await Account.create({ - email: test_user_company_3.email, - password: await hash(test_user_company_3.password), - company: disabled_test_company_3._id - }); - await Account.create({ - email: test_user_company_4.email, - password: await hash(test_user_company_4.password), - company: disabled_test_company_4._id - }); - await Account.create({ - email: test_user_mail.email, - password: await hash(test_user_mail.password), - company: disabled_test_company_mail._id - }); - - const offer = { - title: "Test Offer", - publishDate: new Date(Date.now()), - publishEndDate: new Date(Date.now() + (DAY_TO_MS)), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - requirements: ["The candidate must be tested", "Fluent in testJS"], - owner: disabled_test_company_4._id, - ownerName: disabled_test_company_4.name, - ownerLogo: disabled_test_company_4.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_DISABLED, - }; - - await Offer.create([offer, offer]); - - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - - const res = await test_agent - .put("/company/123/enable") - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/enable`) - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - test("should fail to enable if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - - }); - - test("should fail to enable if not logged", async () => { - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - - }); - - test("Should enable company if god token is sent", async () => { - - const res = await test_agent - .put(`/company/${disabled_test_company_3._id}/enable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - }); - - test("Should enable company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_2._id}/enable`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - }); - - test("Should enable company if logged as same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - }); - - test("should change offers' 'isHidden' on company enable", async () => { - - const offersBefore = await Offer.find({ owner: disabled_test_company_4._id }); - - expect(offersBefore.every(({ isHidden }) => isHidden === true)).toBe(true); - expect(offersBefore.every( - ({ hiddenReason }) => hiddenReason === OfferConstants.HiddenOfferReasons.COMPANY_DISABLED) - ).toBe(true); - - const res = await test_agent - .put(`/company/${disabled_test_company_4._id}/enable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - - const offersAfter = await Offer.find({ owner: disabled_test_company_4._id }); - - expect(offersAfter.every(({ isHidden }) => isHidden === false)).toBe(true); - expect(offersAfter.every(({ hiddenReason }) => hiddenReason === undefined)).toBe(true); - }); - - test("should send an email to the company user when it is enabled", async () => { - await test_agent - .put(`/company/${disabled_test_company_mail._id}/enable`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_ENABLED_NOTIFICATION( - disabled_test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); - - describe("PUT /company/disable", () => { - - let test_company_1, test_company_2, test_company_3, test_company_mail; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - const test_user_company_3 = { - email: "company3@email.com", - password: "password123", - }; - const test_user_company_mail = { - email: "company_mail@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeAll(async () => { - - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [test_company_1, test_company_2, test_company_3, test_company_mail] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true - }, { - name: "test-company-3", - logo: "http://awebsite.com/alogo.jpg", - hasFinishedRegistration: true - }, { - name: "test-company-mail", - hasFinishedRegistration: true - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }); - await Account.create({ - email: test_user_company_3.email, - password: await hash(test_user_company_3.password), - company: test_company_3._id - }); - await Account.create({ - email: test_user_company_mail.email, - password: await hash(test_user_company_mail.password), - company: test_company_mail._id - }); - - const offer = { - title: "Test Offer", - publishDate: new Date(Date.now()), - publishEndDate: new Date(Date.now() + (DAY_TO_MS)), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 2, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - requirements: ["The candidate must be tested", "Fluent in testJS"], - owner: test_company_3._id, - ownerName: test_company_3.name, - ownerLogo: test_company_3.logo, - }; - - await Offer.create([offer, offer]); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - - const res = await test_agent - .put("/company/123/disable") - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/disable`) - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - test("should fail to disable company if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - }); - - test("should fail to disable company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("should fail to disable company if not logged", async () => { - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("Should disable company if god token is sent", async () => { - - const res = await test_agent - .put(`/company/${test_company_2._id}/disable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(true); - }); - - test("Should disable company if logged as same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(true); - }); - - test("should change offers' 'isHidden' on company disable", async () => { - - const offersBefore = await Offer.find({ owner: test_company_3._id }); - - expect(offersBefore.every(({ isHidden }) => isHidden === false)).toBe(true); - expect(offersBefore.every(({ hiddenReason }) => hiddenReason === undefined)).toBe(true); - - const res = await test_agent - .put(`/company/${test_company_3._id}/disable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(true); - - const offersAfter = await Offer.find({ owner: test_company_3._id }); - - expect(offersAfter.every(({ isHidden }) => isHidden === true)).toBe(true); - expect(offersAfter.every(({ hiddenReason }) => hiddenReason === OfferConstants.HiddenOfferReasons.COMPANY_DISABLED)).toBe(true); - }); - - test("should send an email to the company user when it is disabled", async () => { - await test_agent - .put(`/company/${test_company_mail._id}/disable`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_DISABLED_NOTIFICATION( - test_company_mail.name - ); + describe("PUT /company/edit", () => { - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_company_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); + const generateTestCompany = (params) => ({ + name: "Big Company", + bio: "Big Company Bio", + logo: "http://awebsite.com/alogo.jpg", + contacts: ["112", "122"], + hasFinishedRegistration: true, + ...params, }); - }); - describe("PUT /company/edit", () => { let test_companies; let test_company, test_company_blocked, test_company_disabled; let test_offer; diff --git a/test/end-to-end/company/:id/block.js b/test/end-to-end/company/:id/block.js index 3db9e2ea..7c3394af 100644 --- a/test/end-to-end/company/:id/block.js +++ b/test/end-to-end/company/:id/block.js @@ -1 +1,293 @@ -test("should be true", () => expect(true).toBe(true)); +import { StatusCodes } from "http-status-codes"; +import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import Offer from "../../../../src/models/Offer"; +import OfferConstants from "../../../../src/models/constants/Offer"; +import { COMPANY_BLOCKED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; +import { DAY_TO_MS } from "../../../utils/TimeConstants"; +import withGodToken from "../../../utils/GodToken"; +import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; + +jest.mock("../../../../src/lib/emailService"); +jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); + +describe("PUT /company/block", () => { + + const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date()).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: false, + requirements: ["The candidate must be tested", "Fluent in testJS"], + ...params, + }); + + const test_agent = agent(); + + const company_data = { + name: "Company Ltd" + }; + + const test_users = Array(4).fill({}).map((_c, idx) => ({ + email: `test_email_${idx}@email.com`, + password: "password123", + })); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const adminReason = "An admin reason!"; + + let test_company_1, test_company_2, blocked_test_company_2, test_email_company; + + beforeAll(async () => { + await Company.deleteMany({}); + test_company_1 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); + test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); + test_email_company = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); + blocked_test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); + await Account.deleteMany({}); + [test_email_company, test_company_1, test_company_2, blocked_test_company_2] + .forEach(async (company, idx) => { + await Account.create({ + email: test_users[idx].email, + password: await hash(test_users[idx].password), + company: company._id + }); + }); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + }); + + + test("should fail if not logged in", async () => { + await test_agent + .del("/auth/login"); + + const res = await test_agent + .put(`/company/${test_company_1.id}/block`) + .expect(StatusCodes.UNAUTHORIZED); + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); + }); + + test("should fail if logged in as company", async () => { + await test_agent + .post("/auth/login") + .send(test_users[1]) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1.id}/block`) + .expect(StatusCodes.UNAUTHORIZED); + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); + }); + + + test("should fail if admin reason not provided", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1.id}/block`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("param", "adminReason"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.REQUIRED); + }); + + test("should allow if logged in as admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1.id}/block`) + .send({ adminReason }) + .expect(StatusCodes.OK); + expect(res.body).toHaveProperty("isBlocked", true); + expect(res.body).toHaveProperty("adminReason", adminReason); + }); + + test("should fail if not a valid id", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put("/company/123/block") + .send({ adminReason }) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("param", "companyId"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); + }); + + test("should fail if company does not exist", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const id = "111111111111111111111111"; + const res = await test_agent + .put(`/company/${id}/block`) + .send({ adminReason }) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors[0]).toHaveProperty("param", "companyId"); + expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); + }); + + test("should allow with god token", async () => { + await test_agent + .del("/auth/login"); + + const res = await test_agent + .put(`/company/${test_company_2.id}/block`) + .send(withGodToken({ adminReason })) + .expect(StatusCodes.OK); + expect(res.body).toHaveProperty("isBlocked", true); + expect(res.body).toHaveProperty("adminReason", adminReason); + }); + + test("should send an email to the company user when it is blocked", async () => { + await test_agent + .del("/auth/login"); + await test_agent + .put(`/company/${test_email_company._id}/block`) + .send(withGodToken({ adminReason })) + .expect(StatusCodes.OK); + + const emailOptions = COMPANY_BLOCKED_NOTIFICATION( + test_email_company.name + ); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_users[0].email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); + + describe("testing with offers", () => { + let test_company; + + beforeEach(async () => { + const company = { + email: "test_company_email_@email.com", + password: "password123", + }; + + await Company.deleteMany({}); + test_company = await Company.create({ + name: company_data.name, + hasFinishedRegistration: true, + logo: "http://awebsite.com/alogo.jpg" + }); + + + await Account.deleteMany({}); + await Account.create({ + email: company.email, + password: await hash(company.password), + company: test_company._id + }); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); + + test("should block active offers", async () => { + + const offers = Array(3).fill(await Offer.create({ + ...generateTestOffer({ + "publishDate": (new Date(Date.now())).toISOString(), + "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() + }), + owner: test_company._id, + ownerName: test_company.name, + ownerLogo: test_company.logo + })); + + const res = await test_agent + .put(`/company/${test_company.id}/block`) + .send({ adminReason }) + .expect(StatusCodes.OK); + expect(res.body).toHaveProperty("isBlocked", true); + expect(res.body).toHaveProperty("adminReason", adminReason); + + + for (const offer of offers) { + const updated_offer = await Offer.findById(offer._id); + + expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED); + expect(updated_offer).toHaveProperty("isHidden", true); + } + }); + + test("should not override offers already hidden", async () => { + + const offer = await Offer.create({ + ...generateTestOffer({ + "publishDate": (new Date(Date.now())).toISOString(), + "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() + }), + owner: test_company._id, + ownerName: test_company.name, + ownerLogo: test_company.logo, + isHidden: true, + hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK + }); + + const res = await test_agent + .put(`/company/${test_company.id}/block`) + .send({ adminReason }) + .expect(StatusCodes.OK); + expect(res.body).toHaveProperty("isBlocked", true); + expect(res.body).toHaveProperty("adminReason", adminReason); + + + const updated_offer = await Offer.findById(offer._id); + + expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.ADMIN_BLOCK); + expect(updated_offer).toHaveProperty("isHidden", true); + + }); + }); +}); diff --git a/test/end-to-end/company/:id/delete.js b/test/end-to-end/company/:id/delete.js index 077af2a6..b45a4975 100644 --- a/test/end-to-end/company/:id/delete.js +++ b/test/end-to-end/company/:id/delete.js @@ -12,7 +12,7 @@ import { DAY_TO_MS } from "../../../utils/TimeConstants"; jest.mock("../../../../src/lib/emailService"); jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); -describe("POST /company/:companyId/delete", () => { +describe("POST /company/delete", () => { const test_agent = agent(); From cbd8bf85141ded2f92c52abb37a0b3fdfda51f65 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 20 Jun 2023 23:17:12 +0100 Subject: [PATCH 22/30] Fixed errors in company enablement tests --- test/end-to-end/company/:id/block.js | 2 +- test/end-to-end/company/:id/enable.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/end-to-end/company/:id/block.js b/test/end-to-end/company/:id/block.js index 7c3394af..3a5d44a3 100644 --- a/test/end-to-end/company/:id/block.js +++ b/test/end-to-end/company/:id/block.js @@ -76,7 +76,6 @@ describe("PUT /company/block", () => { }); }); - test("should fail if not logged in", async () => { await test_agent .del("/auth/login"); @@ -160,6 +159,7 @@ describe("PUT /company/block", () => { .put(`/company/${id}/block`) .send({ adminReason }) .expect(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); expect(res.body).toHaveProperty("errors"); expect(res.body.errors[0]).toHaveProperty("param", "companyId"); diff --git a/test/end-to-end/company/:id/enable.js b/test/end-to-end/company/:id/enable.js index 04728d0b..fc324517 100644 --- a/test/end-to-end/company/:id/enable.js +++ b/test/end-to-end/company/:id/enable.js @@ -320,7 +320,7 @@ describe("PUT /company/enable", () => { }); afterAll(async () => { - await Account.delete({ email: account_with_offers.email }); + await Account.deleteMany({ email: account_with_offers.email }); await Company.deleteMany({ name: disabled_company_with_offers_data.name }); await Offer.deleteMany({ owner: disabled_company_with_offers._id }); }); From fd7d13e56366fc073d2da4da58a0ae7782e473b7 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 21 Jun 2023 00:57:52 +0100 Subject: [PATCH 23/30] Finished company blocking tests --- test/end-to-end/company.js | 266 ------------- test/end-to-end/company/:id/block.js | 561 ++++++++++++++++----------- 2 files changed, 337 insertions(+), 490 deletions(-) diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js index 07e4d9aa..213832a1 100644 --- a/test/end-to-end/company.js +++ b/test/end-to-end/company.js @@ -1,15 +1,9 @@ import { StatusCodes as HTTPStatus } from "http-status-codes"; -import { ErrorTypes } from "../../src/api/middleware/errorHandler"; import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; -import { - COMPANY_BLOCKED_NOTIFICATION, -} from "../../src/email-templates/companyManagement"; -import EmailService from "../../src/lib/emailService"; import hash from "../../src/lib/passwordHashing"; import Account from "../../src/models/Account"; import Company from "../../src/models/Company"; import Offer from "../../src/models/Offer"; -import OfferConstants from "../../src/models/constants/Offer"; import withGodToken from "../utils/GodToken"; import { DAY_TO_MS } from "../utils/TimeConstants"; import { MAX_FILE_SIZE_MB } from "../../src/api/middleware/utils"; @@ -33,266 +27,6 @@ describe("Company endpoint", () => { ...params, }); - describe("PUT /company/:companyId/block", () => { - const test_agent = agent(); - - const company_data = { - name: "Company Ltd" - }; - - const test_users = Array(4).fill({}).map((_c, idx) => ({ - email: `test_email_${idx}@email.com`, - password: "password123", - })); - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - const adminReason = "An admin reason!"; - - let test_company_1, test_company_2, blocked_test_company_2, test_email_company; - - beforeAll(async () => { - await Company.deleteMany({}); - test_company_1 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - test_email_company = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - blocked_test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - await Account.deleteMany({}); - [test_email_company, test_company_1, test_company_2, blocked_test_company_2] - .forEach(async (company, idx) => { - await Account.create({ - email: test_users[idx].email, - password: await hash(test_users[idx].password), - company: company._id - }); - }); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - - test("should fail if not logged in", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("should fail if logged in as company", async () => { - await test_agent - .post("/auth/login") - .send(test_users[1]) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - - test("should fail if admin reason not provided", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "adminReason"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.REQUIRED); - }); - - test("should allow if logged in as admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - }); - - test("should fail if not a valid id", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put("/company/123/block") - .send({ adminReason }) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("should fail if company does not exist", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - - test("should allow with god token", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_2.id}/block`) - .send(withGodToken({ adminReason })) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - }); - - test("should send an email to the company user when it is blocked", async () => { - await test_agent - .del("/auth/login"); - await test_agent - .put(`/company/${test_email_company._id}/block`) - .send(withGodToken({ adminReason })) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_BLOCKED_NOTIFICATION( - test_email_company.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_users[0].email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - - describe("testing with offers", () => { - let test_company; - - beforeEach(async () => { - const company = { - email: "test_company_email_@email.com", - password: "password123", - }; - - await Company.deleteMany({}); - test_company = await Company.create({ - name: company_data.name, - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg" - }); - - - await Account.deleteMany({}); - await Account.create({ - email: company.email, - password: await hash(company.password), - company: test_company._id - }); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - }); - - test("should block active offers", async () => { - - const offers = Array(3).fill(await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo - })); - - const res = await test_agent - .put(`/company/${test_company.id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - - - for (const offer of offers) { - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED); - expect(updated_offer).toHaveProperty("isHidden", true); - } - }); - - test("should not override offers already hidden", async () => { - - const offer = await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK - }); - - const res = await test_agent - .put(`/company/${test_company.id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - - - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.ADMIN_BLOCK); - expect(updated_offer).toHaveProperty("isHidden", true); - - }); - }); - }); - describe("PUT /company/edit", () => { const generateTestCompany = (params) => ({ diff --git a/test/end-to-end/company/:id/block.js b/test/end-to-end/company/:id/block.js index 3a5d44a3..99023a34 100644 --- a/test/end-to-end/company/:id/block.js +++ b/test/end-to-end/company/:id/block.js @@ -16,278 +16,391 @@ jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(( describe("PUT /company/block", () => { - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - const test_agent = agent(); - const company_data = { - name: "Company Ltd" - }; - - const test_users = Array(4).fill({}).map((_c, idx) => ({ - email: `test_email_${idx}@email.com`, - password: "password123", - })); - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - const adminReason = "An admin reason!"; - - let test_company_1, test_company_2, blocked_test_company_2, test_email_company; - beforeAll(async () => { await Company.deleteMany({}); - test_company_1 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - test_email_company = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - blocked_test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); await Account.deleteMany({}); - [test_email_company, test_company_1, test_company_2, blocked_test_company_2] - .forEach(async (company, idx) => { - await Account.create({ - email: test_users[idx].email, - password: await hash(test_users[idx].password), - company: company._id - }); - }); + }); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); + afterAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); }); - test("should fail if not logged in", async () => { - await test_agent - .del("/auth/login"); + describe("ID Validation", () => { + const adminReason = "An admin reason!"; - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(StatusCodes.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); + test("should fail if not a valid id", async () => { + const res = await test_agent + .put("/company/123/block") + .send(withGodToken({ adminReason })) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.OBJECT_ID + }) + ])); + }); - test("should fail if logged in as company", async () => { - await test_agent - .post("/auth/login") - .send(test_users[1]) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(StatusCodes.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); + test("should fail if company does not exist", async () => { + const id = "111111111111111111111111"; + const res = await test_agent + .put(`/company/${id}/block`) + .send(withGodToken({ adminReason })) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "companyId", + msg: ValidationReasons.COMPANY_NOT_FOUND(id) + }) + ])); + }); }); + describe("Without auth", () => { - test("should fail if admin reason not provided", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "adminReason"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.REQUIRED); - }); + let test_company; + const test_company_data = { + name: "Company Ltd", + hasFinishedRegistration: true, + }; - test("should allow if logged in as admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .send({ adminReason }) - .expect(StatusCodes.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - }); + const test_user = { + email: "no-auth@email.com", + password: "password123" + }; - test("should fail if not a valid id", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put("/company/123/block") - .send({ adminReason }) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); + beforeAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); - test("should fail if company does not exist", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/block`) - .send({ adminReason }) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); + test_company = await Company.create(test_company_data); - test("should allow with god token", async () => { - await test_agent - .del("/auth/login"); + // Need to create the account because of the mail notification + await Account.create({ + email: test_user.email, + password: await hash(test_user.password), + company: test_company._id + }); + }); - const res = await test_agent - .put(`/company/${test_company_2.id}/block`) - .send(withGodToken({ adminReason })) - .expect(StatusCodes.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - }); + afterAll(async () => { + await Company.deleteMany({ _id: test_company._id }); + await Account.deleteMany({ email: test_user.email }); + }); - test("should send an email to the company user when it is blocked", async () => { - await test_agent - .del("/auth/login"); - await test_agent - .put(`/company/${test_email_company._id}/block`) - .send(withGodToken({ adminReason })) - .expect(StatusCodes.OK); - - const emailOptions = COMPANY_BLOCKED_NOTIFICATION( - test_email_company.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_users[0].email, - template: emailOptions.template, - context: emailOptions.context, - })); + test("should fail if not logged in", async () => { + const res = await test_agent + .put(`/company/${test_company.id}/block`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); }); - describe("testing with offers", () => { - let test_company; + describe("With auth", () => { + + let test_company_1, test_company_2, test_company_mail; + const company_data = { + name: "Company Ltd", + hasFinishedRegistration: true, + }; + + const test_user_1 = { + email: "company1@email.com", + password: "password123" + }; + + const test_user_2 = { + email: "company2@email.com", + password: "password123" + }; + + const test_user_mail = { + email: "company-mail@email.com", + password: "password123" + }; + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const adminReason = "An admin reason!"; + + beforeAll(async () => { + [test_company_1, test_company_2, test_company_mail] = await Company.create([ + company_data, + company_data, + company_data + ]); + + await Account.create([ + { + email: test_user_1.email, + password: await hash(test_user_1.password), + company: test_company_1._id + }, { + email: test_user_2.email, + password: await hash(test_user_2.password), + company: test_company_2._id + }, { + email: test_user_mail.email, + password: await hash(test_user_mail.password), + company: test_company_mail._id + }, { + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + } + ]); + }); - beforeEach(async () => { - const company = { - email: "test_company_email_@email.com", - password: "password123", - }; + afterAll(async () => { + await Company.deleteMany({ _id: { $in: [test_company_1._id, test_company_2._id, test_company_mail._id] } }); + await Account.deleteMany({ email: { $in: [test_user_1.email, test_user_2.email, test_user_mail.email] } }); + }); - await Company.deleteMany({}); - test_company = await Company.create({ - name: company_data.name, - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg" - }); + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + test("should fail if logged in as company", async () => { + await test_agent + .post("/auth/login") + .send(test_user_1) + .expect(StatusCodes.OK); - await Account.deleteMany({}); - await Account.create({ - email: company.email, - password: await hash(company.password), - company: test_company._id - }); + const res = await test_agent + .put(`/company/${test_company_1.id}/block`) + .expect(StatusCodes.UNAUTHORIZED); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + msg: ValidationReasons.INSUFFICIENT_PERMISSIONS + }) + ])); + }); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); + test("should fail if admin reason not provided", async () => { await test_agent .post("/auth/login") .send(test_user_admin) .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1.id}/block`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + param: "adminReason", + msg: ValidationReasons.REQUIRED + }) + ])); }); - test("should block active offers", async () => { + test("should allow if logged in as admin", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .put(`/company/${test_company_1.id}/block`) + .send({ adminReason }) + .expect(StatusCodes.OK); - const offers = Array(3).fill(await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo + expect(res.body).toEqual(expect.objectContaining({ + isBlocked: true, + adminReason })); + }); + test("should allow with god token", async () => { const res = await test_agent - .put(`/company/${test_company.id}/block`) - .send({ adminReason }) + .put(`/company/${test_company_2.id}/block`) + .send(withGodToken({ adminReason })) .expect(StatusCodes.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); + expect(res.body).toEqual(expect.objectContaining({ + isBlocked: true, + adminReason + })); + }); - for (const offer of offers) { - const updated_offer = await Offer.findById(offer._id); + test("should send an email to the company user when it is blocked", async () => { + await test_agent + .put(`/company/${test_company_mail._id}/block`) + .send(withGodToken({ adminReason })) + .expect(StatusCodes.OK); - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED); - expect(updated_offer).toHaveProperty("isHidden", true); - } + const emailOptions = COMPANY_BLOCKED_NOTIFICATION( + test_company_mail.name + ); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_user_mail.email, + template: emailOptions.template, + context: emailOptions.context, + })); }); - test("should not override offers already hidden", async () => { - - const offer = await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK + describe("With offers", () => { + + const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date()).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobType: "SUMMER INTERNSHIP", + jobMinDuration: 1, + jobMaxDuration: 6, + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: false, + requirements: ["The candidate must be tested", "Fluent in testJS"], + ...params, }); - const res = await test_agent - .put(`/company/${test_company.id}/block`) - .send({ adminReason }) - .expect(StatusCodes.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); + let test_company_with_offers; + const test_company_with_offers_data = { + name: "Company With Offers", + logo: "https://www.google.com/image.jpg", + hasFinishedRegistration: true, + }; + + const test_user_with_offers = { + email: "offers@email.com", + password: "password123" + }; + + beforeAll(async () => { + await Offer.deleteMany({}); + + test_company_with_offers = await Company.create(test_company_with_offers_data); + + await Account.create({ + email: test_user_with_offers.email, + password: await hash(test_user_with_offers.password), + company: test_company_with_offers._id + }); + }); + + afterAll(async () => { + await Offer.deleteMany({}); + }); + + afterEach(async () => { + await test_agent + .put(`/company/${test_company_with_offers.id}/unblock`) + .send(withGodToken({})) + .expect(StatusCodes.OK); + }); + + describe("With active offers", () => { - const updated_offer = await Offer.findById(offer._id); + let test_active_offers; - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.ADMIN_BLOCK); - expect(updated_offer).toHaveProperty("isHidden", true); + beforeAll(async () => { + test_active_offers = await Offer.create(Array(3).fill(generateTestOffer({ + "publishDate": (new Date(Date.now())).toISOString(), + "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + owner: test_company_with_offers._id, + ownerName: test_company_with_offers.name, + ownerLogo: test_company_with_offers.logo + }))); + }); + afterAll(async () => { + await Offer.deleteMany({ _id: { $in: test_active_offers.map((offer) => offer._id) } }); + }); + + test("should block active offers", async () => { + + const res = await test_agent + .put(`/company/${test_company_with_offers.id}/block`) + .send(withGodToken({ adminReason })) + .expect(StatusCodes.OK); + + expect(res.body).toEqual(expect.objectContaining({ + isBlocked: true, + adminReason + })); + + const offers = await Offer.find({ _id: { $in: test_active_offers.map((offer) => offer._id) } }); + + expect(offers).toHaveLength(test_active_offers.length); + expect(offers).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ + isHidden: false, // we can check on just this attribute since both are set at the same time + }) + ])); + }); + }); + + describe("With hidden offers", () => { + + let test_hidden_offers; + + beforeAll(async () => { + test_hidden_offers = await Offer.create(Array(3).fill(generateTestOffer({ + "publishDate": (new Date(Date.now())).toISOString(), + "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + owner: test_company_with_offers._id, + ownerName: test_company_with_offers.name, + ownerLogo: test_company_with_offers.logo, + isHidden: true, + hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK + }))); + }); + + afterAll(async () => { + await Offer.deleteMany({ _id: { $in: test_hidden_offers.map((offer) => offer._id) } }); + }); + + test("should not override offers already hidden", async () => { + + const res = await test_agent + .put(`/company/${test_company_with_offers.id}/block`) + .send(withGodToken({ adminReason })) + .expect(StatusCodes.OK); + + expect(res.body).toEqual(expect.objectContaining({ + isBlocked: true, + adminReason + })); + + const offers = await Offer.find({ _id: { $in: test_hidden_offers.map((offer) => offer._id) } }); + + expect(offers).toHaveLength(test_hidden_offers.length); + expect(offers).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ + isHidden: false, + hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED + }) + ])); + }); + }); }); }); }); From cb16da1e4ceb2ff7f16cc00bc6cf9564d981d6d1 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 21 Jun 2023 01:09:05 +0100 Subject: [PATCH 24/30] Fixed audit issues --- package-lock.json | 2165 ++++++++++++++++++++++++++------------------- 1 file changed, 1248 insertions(+), 917 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72629667..3d1b0735 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,9 +57,27 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, "node_modules/@aws-crypto/ie11-detection": { - "version": "2.0.2", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "optional": true, "dependencies": { "tslib": "^1.11.1" @@ -67,19 +85,21 @@ }, "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/sha256-browser": { - "version": "2.0.0", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", "optional": true, "dependencies": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" @@ -87,27 +107,31 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/sha256-js": { - "version": "2.0.0", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", "optional": true, "dependencies": { - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" } }, "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/supports-web-crypto": { - "version": "2.0.2", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", "optional": true, "dependencies": { "tslib": "^1.11.1" @@ -115,502 +139,540 @@ }, "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/util": { - "version": "2.0.2", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", "optional": true, "dependencies": { - "@aws-sdk/types": "^3.110.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" } }, "node_modules/@aws-crypto/util/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-sdk/abort-controller": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.347.0.tgz", + "integrity": "sha512-P/2qE6ntYEmYG4Ez535nJWZbXqgbkJx8CMz7ChEuEg3Gp3dvVYEKg+iEUEvlqQ2U5dWP5J3ehw5po9t86IsVPQ==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.354.0.tgz", + "integrity": "sha512-VYoPiup85Zn1uiqn6X7Kl1/5AsihyW0jOPpO5Xv39shRKFTLYWIgPxjg7k+dNPVAX62XrWoWNkGR6sB/JN9Qdg==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.354.0.tgz", + "integrity": "sha512-4jmvjJYDaaPmm1n2TG4LYfTEnHLKcJmImgBqhgzhMgaypb4u/k1iw0INV2r/afYPL/FsrLFwc46RM3HYx3nc4A==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.354.0.tgz", + "integrity": "sha512-XZcg4s2zKb4S8ltluiw5yxpm974uZqzo2HTECt1lbzUJgVgLsMAh/nPJ1fLqg4jadT+rf8Lq2FEFqOM/vxWT8A==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.354.0.tgz", + "integrity": "sha512-l9Ar/C/3PNlToM1ukHVfBtp4plbRUxLMYY2DOTMI0nb3jzfcvETBcdEGCP51fX4uAfJ2vc4g5qBF/qXKX0LMWA==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-sdk-sts": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "fast-xml-parser": "4.2.4", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/config-resolver": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.354.0.tgz", + "integrity": "sha512-K4XWie8yJPT8bpYVX54VJMQhiJRTw8PrjEs9QrKqvwoCcZ3G4qEt40tIu33XksuokXxk8rrVH5d7odOPBsAtdg==", "optional": true, "dependencies": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.354.0.tgz", + "integrity": "sha512-Q5UcqASJWqwD4AXpfv4Zpw5tUV/fzbhnEC9TzyB39zXcu4Qd0cQgVQOOq9FX1GbtLNOzkPnbvHsbv2PdEaNM4A==", "optional": true, "dependencies": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.353.0.tgz", + "integrity": "sha512-Y4VsNS8O1FAD5J7S5itOhnOghQ5LIXlZ44t35nF8cbcF+JPvY3ToKzYpjYN1jM7DXKqU4shtqgYpzSqxlvEgKQ==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-imds": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.354.0.tgz", + "integrity": "sha512-AB+PuDd1jX6qgz+JYvIyOn8Kz9/lQ60KuY1TFb7g3S8zURw+DSeMJNR1jzEsorWICTzhxXmyasHVMa4Eo4Uq+Q==", "optional": true, "dependencies": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.354.0.tgz", + "integrity": "sha512-bn2ifrRsxWpxzwXa25jRdUECQ1dC+NB3YlRYnGdIaIQLF559N2jnfCabYzqyfKI++WU7aQeMofPe2PxVGlbv9Q==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.354.0.tgz", + "integrity": "sha512-ltKiRtHfqDaCcrb44DIoSHQ9MposFl/aDtNdu5OdQv/2Q1r7M/r2fQdq9DHOrxeQQjaUH4C6k6fGTsxALTHyNA==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.354.0.tgz", + "integrity": "sha512-AxpASm+tS8V1PY4PLfG9dtqa96lzBJ3niTQb+RAm4uYCddW7gxNDkGB+jSCzVdUPVa3xA2ITBS/ka3C5yM8YWg==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.354.0.tgz", + "integrity": "sha512-ihiaUxh8V/nQgTOgQZxWQcbckXhM+J6Wdc4F0z9soi48iSOqzRpzPw5E14wSZScEZjNY/gKEDz8gCt8WkT/G0w==", "optional": true, "dependencies": { - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.229.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/token-providers": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.354.0.tgz", + "integrity": "sha512-scx9mAf4m3Hc3uMX2Vh8GciEcC/5GqeDI8qc0zBj+UF/5c/GtihZA4WoCV3Sg3jMPDUKY81DiFCtcKHhtUqKfg==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/credential-provider-cognito-identity": "3.229.0", - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.354.0.tgz", + "integrity": "sha512-GjkSKGWL+lbEVAYGRvE2kdKn8lnhLEBB98yKMz6k9VhqVBrMPZVGTFTlNNtPRZ7IfnnmgLnk6IHtue9xgaycfg==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/credential-provider-cognito-identity": "3.354.0", + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/eventstream-codec": { + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.347.0.tgz", + "integrity": "sha512-61q+SyspjsaQ4sdgjizMyRgVph2CiW4aAtfpoH69EJFJfTxTR/OqnZ9Jx/3YiYi0ksrvDenJddYodfWWJqD8/w==", + "optional": true, + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, "node_modules/@aws-sdk/fetch-http-handler": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.353.0.tgz", + "integrity": "sha512-8ic2+4E6jzfDevd++QS1rOR05QFkAhEFbi5Ja3/Zzp7TkWIS8wv5wwMATjNkbbdsXYuB5Lhl/OsjfZmIv5aqRw==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/hash-node": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.347.0.tgz", + "integrity": "sha512-96+ml/4EaUaVpzBdOLGOxdoXOjkPgkoJp/0i1fxOJEvl8wdAQSwc3IugVK9wZkCxy2DlENtgOe6DfIOhfffm/g==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/invalid-dependency": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.347.0.tgz", + "integrity": "sha512-8imQcwLwqZ/wTJXZqzXT9pGLIksTRckhGLZaXT60tiBOPKuerTsus2L59UstLs5LP8TKaVZKFFSsjRIn9dQdmQ==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/is-array-buffer": { - "version": "3.201.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-content-length": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.347.0.tgz", + "integrity": "sha512-i4qtWTDImMaDUtwKQPbaZpXsReiwiBomM1cWymCU4bhz81HL01oIxOxOBuiM+3NlDoCSPr3KI6txZSz/8cqXCQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-endpoint": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.347.0.tgz", + "integrity": "sha512-unF0c6dMaUL1ffU+37Ugty43DgMnzPWXr/Jup/8GbK5fzzWT5NQq6dj9KHPubMbWeEjQbmczvhv25JuJdK8gNQ==", "optional": true, "dependencies": { - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.347.0.tgz", + "integrity": "sha512-kpKmR9OvMlnReqp5sKcJkozbj1wmlblbVSbnQAIkzeQj2xD5dnVR3Nn2ogQKxSmU1Fv7dEroBtrruJ1o3fY38A==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.347.0.tgz", + "integrity": "sha512-NYC+Id5UCkVn+3P1t/YtmHt75uED06vwaKyxDy0UmB2K66PZLVtwWbLpVWrhbroaw1bvUHYcRyQ9NIfnVcXQjA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.347.0.tgz", + "integrity": "sha512-qfnSvkFKCAMjMHR31NdsT0gv5Sq/ZHTUD4yQsSLpbVQ6iYAS834lrzXt41iyEHt57Y514uG7F/Xfvude3u4icQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-retry": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.354.0.tgz", + "integrity": "sha512-dnG5Nd/mobbhcWCM71DQWI9+f6b6fDSzALXftFIP/8lsXKRcWDSQuYjrnVST2wZzk/QmdF8TnVD0C1xL14K6CQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/service-error-classification": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-retry": "3.347.0", + "tslib": "^2.5.0", "uuid": "^8.3.2" }, "engines": { @@ -618,406 +680,439 @@ } }, "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.354.0.tgz", + "integrity": "sha512-L6vyAwYrdcOoB4YgCqNJNr+ZZtLHEF2Ym3CTfmFm2srXHqHuRB+mBu0NLV/grz77znIArK1H1ZL/ZaH2I5hclA==", "optional": true, "dependencies": { - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-serde": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.347.0.tgz", + "integrity": "sha512-x5Foi7jRbVJXDu9bHfyCbhYDH5pKK+31MmsSJ3k8rY8keXLBxm2XEEg/AIoV9/TUF9EeVvZ7F1/RmMpJnWQsEg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-signing": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.354.0.tgz", + "integrity": "sha512-Dd+vIhJL0VqqKWqlTKlKC5jkCaEIk73ZEXNfv44XbsI25a0vXbatHp1M8jB/cgkJC/Mri1TX9dmckP/C0FDEwA==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/signature-v4": "3.354.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-stack": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.347.0.tgz", + "integrity": "sha512-Izidg4rqtYMcKuvn2UzgEpPLSmyd8ub9+LQ2oIzG3mpIzCBITq7wp40jN1iNkMg+X6KEnX9vdMJIYZsPYMCYuQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.352.0.tgz", + "integrity": "sha512-QGqblMTsVDqeomy22KPm9LUW8PHZXBA2Hjk9Hcw8U1uFS8IKYJrewInG3ae2+9FAcTyug4LFWDf8CRr9YH2B3Q==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-endpoints": "3.352.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/node-config-provider": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.354.0.tgz", + "integrity": "sha512-pF1ZGWWvmwbrloNHYF3EDqCb9hq5wfZwDqAwAPhWkYnUYKkR7E7MZVuTwUDU48io8k6Z5pM52l/54w8e8aedTw==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/node-http-handler": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.350.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.350.0.tgz", + "integrity": "sha512-oD96GAlmpzYilCdC8wwyURM5lNfNHZCjm/kxBkQulHKa2kRbIrnD9GfDqdCkWA5cTpjh1NzGLT4D6e6UFDjt9w==", "optional": true, "dependencies": { - "@aws-sdk/abort-controller": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/abort-controller": "3.347.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/property-provider": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.353.0.tgz", + "integrity": "sha512-Iu6J59hncaew7eBKroTcLjZ8cgrom0IWyZZ09rsow3rZDHVtw7LQSrUyuqsSbKGY9eRtL7Wa6ZtYHnXFiAE2kg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/protocol-http": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.347.0.tgz", + "integrity": "sha512-2YdBhc02Wvy03YjhGwUxF0UQgrPWEy8Iq75pfS42N+/0B/+eWX1aQgfjFxIpLg7YSjT5eKtYOQGlYd4MFTgj9g==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/querystring-builder": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.347.0.tgz", + "integrity": "sha512-phtKTe6FXoV02MoPkIVV6owXI8Mwr5IBN3bPoxhcPvJG2AjEmnetSIrhb8kwc4oNhlwfZwH6Jo5ARW/VEWbZtg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/querystring-parser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.347.0.tgz", + "integrity": "sha512-5VXOhfZz78T2W7SuXf2avfjKglx1VZgZgp9Zfhrt/Rq+MTu2D+PZc5zmJHhYigD7x83jLSLogpuInQpFMA9LgA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/service-error-classification": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.347.0.tgz", + "integrity": "sha512-xZ3MqSY81Oy2gh5g0fCtooAbahqh9VhsF8vcKjVX8+XPbGC8y+kej82+MsMg4gYL8gRFB9u4hgYbNgIS6JTAvg==", "optional": true, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.354.0.tgz", + "integrity": "sha512-UL9loGEsdzpHBu/PtlwUvkl/yRdmWXkySp22jUaeeRtBhiGAnyeYhxJLIt+u+UkX7Mwz+810SaZJqA9ptOXNAg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/signature-v4": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.354.0.tgz", + "integrity": "sha512-bDp43P5NkwwznpZqmsr78DuyqNcjtS4mriuajb8XPhFNo8DrMXUrdrKJ+5aNABW7YG8uK8PSKBpq88ado692/w==", "optional": true, "dependencies": { - "@aws-sdk/is-array-buffer": "3.201.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-hex-encoding": "3.201.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/eventstream-codec": "3.347.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/smithy-client": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.347.0.tgz", + "integrity": "sha512-PaGTDsJLGK0sTjA6YdYQzILRlPRN3uVFyqeBUkfltXssvUzkm8z2t1lz2H4VyJLAhwnG5ZuZTNEV/2mcWrU7JQ==", "optional": true, "dependencies": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.354.0.tgz", + "integrity": "sha512-KcijiySy0oIyafKQagcwgu0fo35mK+2K8pwxRU1WfXqe80Gn1qGceeWcG4iW+t/rUaxa/LVo857N0LcagxCrZA==", "optional": true, "dependencies": { - "@aws-sdk/client-sso-oidc": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso-oidc": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.347.0.tgz", + "integrity": "sha512-GkCMy79mdjU9OTIe5KT58fI/6uqdf8UmMdWqVHmFJ+UpEzOci7L/uw4sOXWo7xpPzLs6cJ7s5ouGZW4GRPmHFA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/url-parser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.347.0.tgz", + "integrity": "sha512-lhrnVjxdV7hl+yCnJfDZOaVLSqKjxN20MIOiijRiqaWGLGEAiSqBreMhL89X1WKCifxAs4zZf9YB9SbdziRpAA==", "optional": true, "dependencies": { - "@aws-sdk/querystring-parser": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/querystring-parser": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-base64": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", "optional": true, "dependencies": { - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-body-length-browser": { - "version": "3.188.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-body-length-node": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-buffer-from": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", "optional": true, "dependencies": { - "@aws-sdk/is-array-buffer": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-config-provider": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-defaults-mode-browser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.353.0.tgz", + "integrity": "sha512-ushvOQKJIH7S6E//xMDPyf2/Bbu0K2A0GJRB88qQV6VKRBo4PEbeHTb6BbzPhYVX0IbY3uR/X7+Xwk4FeEkMWg==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, "node_modules/@aws-sdk/util-defaults-mode-node": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.354.0.tgz", + "integrity": "sha512-CaaRVBdOYX4wZadj+CDUxpO+4RjyYJcSv71A60jV6CZ/ya1+oYfmPbG5QZ4AlV6crdev2B+aUoR2LPIYqn/GnQ==", "optional": true, "dependencies": { - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.352.0.tgz", + "integrity": "sha512-PjWMPdoIUWfBPgAWLyOrWFbdSS/3DJtc0OmFb/JrE8C8rKFYl+VGW5f1p0cVdRWiDR0xCGr0s67p8itAakVqjw==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-hex-encoding": { - "version": "3.201.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-middleware": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.347.0.tgz", + "integrity": "sha512-8owqUA3ePufeYTUvlzdJ7Z0miLorTwx+rNol5lourGQZ9JXsVMo23+yGA7nOlFuXSGkoKpMOtn6S0BT2bcfeiw==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-retry": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.347.0.tgz", + "integrity": "sha512-NxnQA0/FHFxriQAeEgBonA43Q9/VPFQa8cfJDuT2A1YZruMasgjcltoZszi1dvoIRWSZsFTW42eY2gdOd0nffQ==", "optional": true, "dependencies": { - "@aws-sdk/service-error-classification": "3.229.0", - "tslib": "^2.3.1" + "@aws-sdk/service-error-classification": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@aws-sdk/util-uri-escape": { - "version": "3.201.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.347.0.tgz", + "integrity": "sha512-ydxtsKVtQefgbk1Dku1q7pMkjDYThauG9/8mQkZUAVik55OUZw71Zzr3XO8J8RKvQG8lmhPXuAQ0FKAyycc0RA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.354.0.tgz", + "integrity": "sha512-2xkblZS3PGxxh//0lgCwJw2gvh9ZBcI9H9xv05YP7hcwlz9BmkAlbei2i6Uew6agJMLO4unfgWoBTpzp3WLaKg==", "optional": true, "dependencies": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" @@ -1031,24 +1126,26 @@ } } }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.188.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/util-utf8-node": { - "version": "3.208.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", "optional": true, "dependencies": { - "@aws-sdk/util-buffer-from": "3.208.0", "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" } }, "node_modules/@babel/code-frame": { @@ -3176,6 +3273,31 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@smithy/protocol-http": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.0.tgz", + "integrity": "sha512-H5y/kZOqfJSqRkwtcAoVbqONmhdXwSgYNJ1Glk5Ry8qlhVVy5qUzD9EklaCH8/XLnoCsLO/F/Giee8MIvaBRkg==", + "optional": true, + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.0.tgz", + "integrity": "sha512-KzmvisMmuwD2jZXuC9e65JrgsZM97y5NpDU7g347oB+Q+xQLU6hQZ5zFNNbEfwwOJHoOvEVTna+dk1h/lW7alw==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "license": "MIT", @@ -3766,7 +3888,8 @@ }, "node_modules/bowser": { "version": "2.11.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", "optional": true }, "node_modules/brace-expansion": { @@ -5173,18 +5296,25 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "4.0.11", - "license": "MIT", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz", + "integrity": "sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "optional": true, "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" - }, - "funding": { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } }, "node_modules/fastq": { @@ -8452,7 +8582,8 @@ }, "node_modules/strnum": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "optional": true }, "node_modules/superagent": { @@ -8649,9 +8780,10 @@ "license": "MIT" }, "node_modules/tslib": { - "version": "2.4.1", - "devOptional": true, - "license": "0BSD" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "devOptional": true }, "node_modules/twostep": { "version": "0.4.2", @@ -8832,7 +8964,8 @@ }, "node_modules/uuid": { "version": "8.3.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "optional": true, "bin": { "uuid": "dist/bin/uuid" @@ -8866,9 +8999,9 @@ } }, "node_modules/vm2": { - "version": "3.9.17", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz", - "integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==", + "version": "3.9.19", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", + "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", "optional": true, "dependencies": { "acorn": "^8.7.0", @@ -9065,8 +9198,29 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "optional": true, + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + } + } + }, "@aws-crypto/ie11-detection": { - "version": "2.0.2", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "optional": true, "requires": { "tslib": "^1.11.1" @@ -9074,19 +9228,23 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/sha256-browser": { - "version": "2.0.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", "optional": true, "requires": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" @@ -9094,27 +9252,35 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/sha256-js": { - "version": "2.0.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", "optional": true, "requires": { - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" }, "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/supports-web-crypto": { - "version": "2.0.2", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", "optional": true, "requires": { "tslib": "^1.11.1" @@ -9122,710 +9288,846 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/util": { - "version": "2.0.2", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", "optional": true, "requires": { - "@aws-sdk/types": "^3.110.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" }, "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-sdk/abort-controller": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.347.0.tgz", + "integrity": "sha512-P/2qE6ntYEmYG4Ez535nJWZbXqgbkJx8CMz7ChEuEg3Gp3dvVYEKg+iEUEvlqQ2U5dWP5J3ehw5po9t86IsVPQ==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-cognito-identity": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.354.0.tgz", + "integrity": "sha512-VYoPiup85Zn1uiqn6X7Kl1/5AsihyW0jOPpO5Xv39shRKFTLYWIgPxjg7k+dNPVAX62XrWoWNkGR6sB/JN9Qdg==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sso": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.354.0.tgz", + "integrity": "sha512-4jmvjJYDaaPmm1n2TG4LYfTEnHLKcJmImgBqhgzhMgaypb4u/k1iw0INV2r/afYPL/FsrLFwc46RM3HYx3nc4A==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sso-oidc": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.354.0.tgz", + "integrity": "sha512-XZcg4s2zKb4S8ltluiw5yxpm974uZqzo2HTECt1lbzUJgVgLsMAh/nPJ1fLqg4jadT+rf8Lq2FEFqOM/vxWT8A==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sts": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.354.0.tgz", + "integrity": "sha512-l9Ar/C/3PNlToM1ukHVfBtp4plbRUxLMYY2DOTMI0nb3jzfcvETBcdEGCP51fX4uAfJ2vc4g5qBF/qXKX0LMWA==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-sdk-sts": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "fast-xml-parser": "4.2.4", + "tslib": "^2.5.0" } }, "@aws-sdk/config-resolver": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.354.0.tgz", + "integrity": "sha512-K4XWie8yJPT8bpYVX54VJMQhiJRTw8PrjEs9QrKqvwoCcZ3G4qEt40tIu33XksuokXxk8rrVH5d7odOPBsAtdg==", "optional": true, "requires": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.354.0.tgz", + "integrity": "sha512-Q5UcqASJWqwD4AXpfv4Zpw5tUV/fzbhnEC9TzyB39zXcu4Qd0cQgVQOOq9FX1GbtLNOzkPnbvHsbv2PdEaNM4A==", "optional": true, "requires": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-env": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.353.0.tgz", + "integrity": "sha512-Y4VsNS8O1FAD5J7S5itOhnOghQ5LIXlZ44t35nF8cbcF+JPvY3ToKzYpjYN1jM7DXKqU4shtqgYpzSqxlvEgKQ==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-imds": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.354.0.tgz", + "integrity": "sha512-AB+PuDd1jX6qgz+JYvIyOn8Kz9/lQ60KuY1TFb7g3S8zURw+DSeMJNR1jzEsorWICTzhxXmyasHVMa4Eo4Uq+Q==", "optional": true, "requires": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-ini": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.354.0.tgz", + "integrity": "sha512-bn2ifrRsxWpxzwXa25jRdUECQ1dC+NB3YlRYnGdIaIQLF559N2jnfCabYzqyfKI++WU7aQeMofPe2PxVGlbv9Q==", "optional": true, "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-node": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.354.0.tgz", + "integrity": "sha512-ltKiRtHfqDaCcrb44DIoSHQ9MposFl/aDtNdu5OdQv/2Q1r7M/r2fQdq9DHOrxeQQjaUH4C6k6fGTsxALTHyNA==", + "optional": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-process": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.354.0.tgz", + "integrity": "sha512-AxpASm+tS8V1PY4PLfG9dtqa96lzBJ3niTQb+RAm4uYCddW7gxNDkGB+jSCzVdUPVa3xA2ITBS/ka3C5yM8YWg==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-sso": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.354.0.tgz", + "integrity": "sha512-ihiaUxh8V/nQgTOgQZxWQcbckXhM+J6Wdc4F0z9soi48iSOqzRpzPw5E14wSZScEZjNY/gKEDz8gCt8WkT/G0w==", "optional": true, "requires": { - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.229.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/token-providers": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-web-identity": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.354.0.tgz", + "integrity": "sha512-scx9mAf4m3Hc3uMX2Vh8GciEcC/5GqeDI8qc0zBj+UF/5c/GtihZA4WoCV3Sg3jMPDUKY81DiFCtcKHhtUqKfg==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-providers": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/credential-provider-cognito-identity": "3.229.0", - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.354.0.tgz", + "integrity": "sha512-GjkSKGWL+lbEVAYGRvE2kdKn8lnhLEBB98yKMz6k9VhqVBrMPZVGTFTlNNtPRZ7IfnnmgLnk6IHtue9xgaycfg==", + "optional": true, + "requires": { + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/credential-provider-cognito-identity": "3.354.0", + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-codec": { + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.347.0.tgz", + "integrity": "sha512-61q+SyspjsaQ4sdgjizMyRgVph2CiW4aAtfpoH69EJFJfTxTR/OqnZ9Jx/3YiYi0ksrvDenJddYodfWWJqD8/w==", + "optional": true, + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/fetch-http-handler": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.353.0.tgz", + "integrity": "sha512-8ic2+4E6jzfDevd++QS1rOR05QFkAhEFbi5Ja3/Zzp7TkWIS8wv5wwMATjNkbbdsXYuB5Lhl/OsjfZmIv5aqRw==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/hash-node": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.347.0.tgz", + "integrity": "sha512-96+ml/4EaUaVpzBdOLGOxdoXOjkPgkoJp/0i1fxOJEvl8wdAQSwc3IugVK9wZkCxy2DlENtgOe6DfIOhfffm/g==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/invalid-dependency": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.347.0.tgz", + "integrity": "sha512-8imQcwLwqZ/wTJXZqzXT9pGLIksTRckhGLZaXT60tiBOPKuerTsus2L59UstLs5LP8TKaVZKFFSsjRIn9dQdmQ==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/is-array-buffer": { - "version": "3.201.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-content-length": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.347.0.tgz", + "integrity": "sha512-i4qtWTDImMaDUtwKQPbaZpXsReiwiBomM1cWymCU4bhz81HL01oIxOxOBuiM+3NlDoCSPr3KI6txZSz/8cqXCQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-endpoint": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.347.0.tgz", + "integrity": "sha512-unF0c6dMaUL1ffU+37Ugty43DgMnzPWXr/Jup/8GbK5fzzWT5NQq6dj9KHPubMbWeEjQbmczvhv25JuJdK8gNQ==", "optional": true, "requires": { - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-host-header": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.347.0.tgz", + "integrity": "sha512-kpKmR9OvMlnReqp5sKcJkozbj1wmlblbVSbnQAIkzeQj2xD5dnVR3Nn2ogQKxSmU1Fv7dEroBtrruJ1o3fY38A==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-logger": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.347.0.tgz", + "integrity": "sha512-NYC+Id5UCkVn+3P1t/YtmHt75uED06vwaKyxDy0UmB2K66PZLVtwWbLpVWrhbroaw1bvUHYcRyQ9NIfnVcXQjA==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-recursion-detection": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.347.0.tgz", + "integrity": "sha512-qfnSvkFKCAMjMHR31NdsT0gv5Sq/ZHTUD4yQsSLpbVQ6iYAS834lrzXt41iyEHt57Y514uG7F/Xfvude3u4icQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-retry": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.354.0.tgz", + "integrity": "sha512-dnG5Nd/mobbhcWCM71DQWI9+f6b6fDSzALXftFIP/8lsXKRcWDSQuYjrnVST2wZzk/QmdF8TnVD0C1xL14K6CQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/service-error-classification": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-retry": "3.347.0", + "tslib": "^2.5.0", "uuid": "^8.3.2" } }, "@aws-sdk/middleware-sdk-sts": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.354.0.tgz", + "integrity": "sha512-L6vyAwYrdcOoB4YgCqNJNr+ZZtLHEF2Ym3CTfmFm2srXHqHuRB+mBu0NLV/grz77znIArK1H1ZL/ZaH2I5hclA==", "optional": true, "requires": { - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-serde": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.347.0.tgz", + "integrity": "sha512-x5Foi7jRbVJXDu9bHfyCbhYDH5pKK+31MmsSJ3k8rY8keXLBxm2XEEg/AIoV9/TUF9EeVvZ7F1/RmMpJnWQsEg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-signing": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.354.0.tgz", + "integrity": "sha512-Dd+vIhJL0VqqKWqlTKlKC5jkCaEIk73ZEXNfv44XbsI25a0vXbatHp1M8jB/cgkJC/Mri1TX9dmckP/C0FDEwA==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/signature-v4": "3.354.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-stack": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.347.0.tgz", + "integrity": "sha512-Izidg4rqtYMcKuvn2UzgEpPLSmyd8ub9+LQ2oIzG3mpIzCBITq7wp40jN1iNkMg+X6KEnX9vdMJIYZsPYMCYuQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-user-agent": { - "version": "3.226.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.352.0.tgz", + "integrity": "sha512-QGqblMTsVDqeomy22KPm9LUW8PHZXBA2Hjk9Hcw8U1uFS8IKYJrewInG3ae2+9FAcTyug4LFWDf8CRr9YH2B3Q==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-endpoints": "3.352.0", + "tslib": "^2.5.0" } }, "@aws-sdk/node-config-provider": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.354.0.tgz", + "integrity": "sha512-pF1ZGWWvmwbrloNHYF3EDqCb9hq5wfZwDqAwAPhWkYnUYKkR7E7MZVuTwUDU48io8k6Z5pM52l/54w8e8aedTw==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/node-http-handler": { - "version": "3.226.0", + "version": "3.350.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.350.0.tgz", + "integrity": "sha512-oD96GAlmpzYilCdC8wwyURM5lNfNHZCjm/kxBkQulHKa2kRbIrnD9GfDqdCkWA5cTpjh1NzGLT4D6e6UFDjt9w==", "optional": true, "requires": { - "@aws-sdk/abort-controller": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/abort-controller": "3.347.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/property-provider": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.353.0.tgz", + "integrity": "sha512-Iu6J59hncaew7eBKroTcLjZ8cgrom0IWyZZ09rsow3rZDHVtw7LQSrUyuqsSbKGY9eRtL7Wa6ZtYHnXFiAE2kg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/protocol-http": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.347.0.tgz", + "integrity": "sha512-2YdBhc02Wvy03YjhGwUxF0UQgrPWEy8Iq75pfS42N+/0B/+eWX1aQgfjFxIpLg7YSjT5eKtYOQGlYd4MFTgj9g==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/querystring-builder": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.347.0.tgz", + "integrity": "sha512-phtKTe6FXoV02MoPkIVV6owXI8Mwr5IBN3bPoxhcPvJG2AjEmnetSIrhb8kwc4oNhlwfZwH6Jo5ARW/VEWbZtg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/querystring-parser": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.347.0.tgz", + "integrity": "sha512-5VXOhfZz78T2W7SuXf2avfjKglx1VZgZgp9Zfhrt/Rq+MTu2D+PZc5zmJHhYigD7x83jLSLogpuInQpFMA9LgA==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/service-error-classification": { - "version": "3.229.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.347.0.tgz", + "integrity": "sha512-xZ3MqSY81Oy2gh5g0fCtooAbahqh9VhsF8vcKjVX8+XPbGC8y+kej82+MsMg4gYL8gRFB9u4hgYbNgIS6JTAvg==", "optional": true }, "@aws-sdk/shared-ini-file-loader": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.354.0.tgz", + "integrity": "sha512-UL9loGEsdzpHBu/PtlwUvkl/yRdmWXkySp22jUaeeRtBhiGAnyeYhxJLIt+u+UkX7Mwz+810SaZJqA9ptOXNAg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/signature-v4": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.354.0.tgz", + "integrity": "sha512-bDp43P5NkwwznpZqmsr78DuyqNcjtS4mriuajb8XPhFNo8DrMXUrdrKJ+5aNABW7YG8uK8PSKBpq88ado692/w==", "optional": true, "requires": { - "@aws-sdk/is-array-buffer": "3.201.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-hex-encoding": "3.201.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/eventstream-codec": "3.347.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/smithy-client": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.347.0.tgz", + "integrity": "sha512-PaGTDsJLGK0sTjA6YdYQzILRlPRN3uVFyqeBUkfltXssvUzkm8z2t1lz2H4VyJLAhwnG5ZuZTNEV/2mcWrU7JQ==", "optional": true, "requires": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/token-providers": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.354.0.tgz", + "integrity": "sha512-KcijiySy0oIyafKQagcwgu0fo35mK+2K8pwxRU1WfXqe80Gn1qGceeWcG4iW+t/rUaxa/LVo857N0LcagxCrZA==", "optional": true, "requires": { - "@aws-sdk/client-sso-oidc": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso-oidc": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/types": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.347.0.tgz", + "integrity": "sha512-GkCMy79mdjU9OTIe5KT58fI/6uqdf8UmMdWqVHmFJ+UpEzOci7L/uw4sOXWo7xpPzLs6cJ7s5ouGZW4GRPmHFA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/url-parser": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.347.0.tgz", + "integrity": "sha512-lhrnVjxdV7hl+yCnJfDZOaVLSqKjxN20MIOiijRiqaWGLGEAiSqBreMhL89X1WKCifxAs4zZf9YB9SbdziRpAA==", "optional": true, "requires": { - "@aws-sdk/querystring-parser": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/querystring-parser": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-base64": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", "optional": true, "requires": { - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-body-length-browser": { - "version": "3.188.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-body-length-node": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-buffer-from": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", "optional": true, "requires": { - "@aws-sdk/is-array-buffer": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-config-provider": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-defaults-mode-browser": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.353.0.tgz", + "integrity": "sha512-ushvOQKJIH7S6E//xMDPyf2/Bbu0K2A0GJRB88qQV6VKRBo4PEbeHTb6BbzPhYVX0IbY3uR/X7+Xwk4FeEkMWg==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-defaults-mode-node": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.354.0.tgz", + "integrity": "sha512-CaaRVBdOYX4wZadj+CDUxpO+4RjyYJcSv71A60jV6CZ/ya1+oYfmPbG5QZ4AlV6crdev2B+aUoR2LPIYqn/GnQ==", "optional": true, "requires": { - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-endpoints": { - "version": "3.226.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.352.0.tgz", + "integrity": "sha512-PjWMPdoIUWfBPgAWLyOrWFbdSS/3DJtc0OmFb/JrE8C8rKFYl+VGW5f1p0cVdRWiDR0xCGr0s67p8itAakVqjw==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-hex-encoding": { - "version": "3.201.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-locate-window": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-middleware": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.347.0.tgz", + "integrity": "sha512-8owqUA3ePufeYTUvlzdJ7Z0miLorTwx+rNol5lourGQZ9JXsVMo23+yGA7nOlFuXSGkoKpMOtn6S0BT2bcfeiw==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-retry": { - "version": "3.229.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.347.0.tgz", + "integrity": "sha512-NxnQA0/FHFxriQAeEgBonA43Q9/VPFQa8cfJDuT2A1YZruMasgjcltoZszi1dvoIRWSZsFTW42eY2gdOd0nffQ==", "optional": true, "requires": { - "@aws-sdk/service-error-classification": "3.229.0", - "tslib": "^2.3.1" + "@aws-sdk/service-error-classification": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-uri-escape": { - "version": "3.201.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-user-agent-browser": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.347.0.tgz", + "integrity": "sha512-ydxtsKVtQefgbk1Dku1q7pMkjDYThauG9/8mQkZUAVik55OUZw71Zzr3XO8J8RKvQG8lmhPXuAQ0FKAyycc0RA==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-user-agent-node": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.354.0.tgz", + "integrity": "sha512-2xkblZS3PGxxh//0lgCwJw2gvh9ZBcI9H9xv05YP7hcwlz9BmkAlbei2i6Uew6agJMLO4unfgWoBTpzp3WLaKg==", "optional": true, "requires": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, - "@aws-sdk/util-utf8-browser": { - "version": "3.188.0", + "@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" } }, - "@aws-sdk/util-utf8-node": { - "version": "3.208.0", + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", "optional": true, "requires": { - "@aws-sdk/util-buffer-from": "3.208.0", "tslib": "^2.3.1" } }, @@ -11146,6 +11448,25 @@ "@sinonjs/commons": "^1.7.0" } }, + "@smithy/protocol-http": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.0.tgz", + "integrity": "sha512-H5y/kZOqfJSqRkwtcAoVbqONmhdXwSgYNJ1Glk5Ry8qlhVVy5qUzD9EklaCH8/XLnoCsLO/F/Giee8MIvaBRkg==", + "optional": true, + "requires": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@smithy/types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.0.tgz", + "integrity": "sha512-KzmvisMmuwD2jZXuC9e65JrgsZM97y5NpDU7g347oB+Q+xQLU6hQZ5zFNNbEfwwOJHoOvEVTna+dk1h/lW7alw==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + } + }, "@tootallnate/once": { "version": "1.1.2", "optional": true @@ -11548,6 +11869,8 @@ }, "bowser": { "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", "optional": true }, "brace-expansion": { @@ -12387,7 +12710,9 @@ "dev": true }, "fast-xml-parser": { - "version": "4.0.11", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz", + "integrity": "sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ==", "optional": true, "requires": { "strnum": "^1.0.5" @@ -14466,6 +14791,8 @@ }, "strnum": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "optional": true }, "superagent": { @@ -14588,7 +14915,9 @@ "version": "0.0.3" }, "tslib": { - "version": "2.4.1", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", "devOptional": true }, "twostep": { @@ -14688,6 +15017,8 @@ }, "uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "optional": true }, "v8-to-istanbul": { @@ -14706,9 +15037,9 @@ "version": "1.1.2" }, "vm2": { - "version": "3.9.17", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz", - "integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==", + "version": "3.9.19", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", + "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", "optional": true, "requires": { "acorn": "^8.7.0", From 5c443616683cabd02a346333c39519b1a022ec78 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 21 Jun 2023 02:31:46 +0100 Subject: [PATCH 25/30] Made some assertions be more descriptive --- test/end-to-end/company/:id/disable.js | 8 ++++++-- test/end-to-end/company/:id/enable.js | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/test/end-to-end/company/:id/disable.js b/test/end-to-end/company/:id/disable.js index 7f85cab7..406a496b 100644 --- a/test/end-to-end/company/:id/disable.js +++ b/test/end-to-end/company/:id/disable.js @@ -236,8 +236,12 @@ describe("PUT /company/disable", () => { describe("With offers", () => { const assertOfferList = (offers, expectedIsHidden, expectedHiddenReason) => { - expect(offers.every(({ isHidden }) => isHidden === expectedIsHidden)).toBe(true); - expect(offers.every(({ hiddenReason }) => hiddenReason === expectedHiddenReason)).toBe(true); + expect(offers).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ + isHidden: !expectedIsHidden, + hiddenReason: !expectedHiddenReason + }) + ])); }; let company_with_offers; diff --git a/test/end-to-end/company/:id/enable.js b/test/end-to-end/company/:id/enable.js index fc324517..1c61b2b8 100644 --- a/test/end-to-end/company/:id/enable.js +++ b/test/end-to-end/company/:id/enable.js @@ -272,8 +272,12 @@ describe("PUT /company/enable", () => { describe("With offers", () => { const assertOfferList = (offers, expectedIsHidden, expectedHiddenReason) => { - expect(offers.every(({ isHidden }) => isHidden === expectedIsHidden)).toBe(true); - expect(offers.every(({ hiddenReason }) => hiddenReason === expectedHiddenReason)).toBe(true); + expect(offers).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ + isHidden: !expectedIsHidden, + hiddenReason: !expectedHiddenReason + }) + ])); }; let disabled_company_with_offers; From 0bcd3259d6874e86dacd866fa6c325648cfade2d Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 21 Jun 2023 02:33:46 +0100 Subject: [PATCH 26/30] Fixed bad cleanup --- test/end-to-end/company/:id/disable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/end-to-end/company/:id/disable.js b/test/end-to-end/company/:id/disable.js index 406a496b..0ce61154 100644 --- a/test/end-to-end/company/:id/disable.js +++ b/test/end-to-end/company/:id/disable.js @@ -80,7 +80,7 @@ describe("PUT /company/disable", () => { }); afterAll(async () => { - await Company.deleteMany({ name: company_data }); + await Company.deleteMany({ name: company_data.name }); }); test("Should not disable company if not authenticated", async () => { From f23a0e105aa2a9d66d479498e7abd347c5665444 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 21 Jun 2023 19:05:50 +0100 Subject: [PATCH 27/30] Added updates image upload tests for company editing --- test/end-to-end/company/:id/edit.js | 72 +++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/test/end-to-end/company/:id/edit.js b/test/end-to-end/company/:id/edit.js index 46138baf..52bc5177 100644 --- a/test/end-to-end/company/:id/edit.js +++ b/test/end-to-end/company/:id/edit.js @@ -1,13 +1,14 @@ import { StatusCodes } from "http-status-codes"; +import { MAX_FILE_SIZE_MB } from "../../../../src/api/middleware/utils"; import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; import hash from "../../../../src/lib/passwordHashing"; import Account from "../../../../src/models/Account"; import Company from "../../../../src/models/Company"; -import CompanyConstants from "../../../../src/models/constants/Company"; import Offer from "../../../../src/models/Offer"; +import CompanyConstants from "../../../../src/models/constants/Company"; import withGodToken from "../../../utils/GodToken"; -import ValidatorTester from "../../../utils/ValidatorTester"; import { DAY_TO_MS } from "../../../utils/TimeConstants"; +import ValidatorTester from "../../../utils/ValidatorTester"; describe("PUT /company/edit", () => { @@ -25,7 +26,7 @@ describe("PUT /company/edit", () => { const edit_payload = { name: "Changed name", bio: "Changed bio", - logo: "http://awebsite.com/changedlogo.jpg", + logo: "test/data/logo-niaefeup.png", contacts: ["123", "456"], }; @@ -539,5 +540,70 @@ describe("PUT /company/edit", () => { expect(test_offer).toHaveProperty("contacts", edit_payload.contacts); }); }); + + describe("Updating company logo", () => { + + let company_with_logo; + const company_with_logo_data = generateTestCompany({ + name: "Test Company With Logo", + logo: "https://test.com/logo.png", + }); + + beforeAll(async () => { + company_with_logo = await Company.create(company_with_logo_data); + }); + + afterAll(async () => { + await Company.deleteMany({ _id: company_with_logo._id }); + }); + + beforeEach(async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); + + afterEach(async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + test("Should fail if not an image", async () => { + const res = await test_agent + .put(`/company/${company_with_logo._id}/edit`) + .attach("logo", "test/data/not-a-logo.txt") + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.IMAGE_FORMAT, + "param": "logo" + }); + }); + + test("Should fail if image is too big", async () => { + const res = await test_agent + .put(`/company/${company_with_logo._id}/edit`) + .attach("logo", "test/data/logo-niaefeup-10mb.png") + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), + "param": "logo" + }); + }); + + test("Should succeed if image is valid", async () => { + const res = await test_agent + .put(`/company/${company_with_logo._id}/edit`) + .attach("logo", edit_payload.logo) + .expect(StatusCodes.OK); + + expect(res.body).toHaveProperty("logo"); + }); + }); }); }); From f01014805167b5a9b8ec9ee2f6b4fed118b65e2d Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 21 Jun 2023 20:37:56 +0100 Subject: [PATCH 28/30] Started work on refactoring offer tests --- test/end-to-end/company.js | 349 ------------------ test/end-to-end/offer.js | 33 +- test/end-to-end/offer/:id/archive.js | 0 test/end-to-end/offer/:id/disable.js | 0 test/end-to-end/offer/:id/enable.js | 0 test/end-to-end/offer/:id/hide.js | 0 test/end-to-end/offer/:id/index.js | 0 .../offer/company/:companyId/index.js | 0 test/end-to-end/offer/edit/:offerId/index.js | 0 test/end-to-end/offer/index.js | 0 10 files changed, 19 insertions(+), 363 deletions(-) delete mode 100644 test/end-to-end/company.js create mode 100644 test/end-to-end/offer/:id/archive.js create mode 100644 test/end-to-end/offer/:id/disable.js create mode 100644 test/end-to-end/offer/:id/enable.js create mode 100644 test/end-to-end/offer/:id/hide.js create mode 100644 test/end-to-end/offer/:id/index.js create mode 100644 test/end-to-end/offer/company/:companyId/index.js create mode 100644 test/end-to-end/offer/edit/:offerId/index.js create mode 100644 test/end-to-end/offer/index.js diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js deleted file mode 100644 index 213832a1..00000000 --- a/test/end-to-end/company.js +++ /dev/null @@ -1,349 +0,0 @@ -import { StatusCodes as HTTPStatus } from "http-status-codes"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; -import hash from "../../src/lib/passwordHashing"; -import Account from "../../src/models/Account"; -import Company from "../../src/models/Company"; -import Offer from "../../src/models/Offer"; -import withGodToken from "../utils/GodToken"; -import { DAY_TO_MS } from "../utils/TimeConstants"; -import { MAX_FILE_SIZE_MB } from "../../src/api/middleware/utils"; - -describe("Company endpoint", () => { - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - describe("PUT /company/edit", () => { - - const generateTestCompany = (params) => ({ - name: "Big Company", - bio: "Big Company Bio", - logo: "http://awebsite.com/alogo.jpg", - contacts: ["112", "122"], - hasFinishedRegistration: true, - ...params, - }); - - let test_companies; - let test_company, test_company_blocked, test_company_disabled; - let test_offer; - - const changing_values = { - name: "Changed name", - bio: "Changed bio", - logo: "test/data/logo-niaefeup.png", - contacts: ["123", "456"], - }; - - /* Admin, Company, Blocked, Disabled*/ - const test_users = Array(4).fill({}).map((_c, idx) => ({ - email: `test_email_${idx}@email.com`, - password: "password123", - })); - - const [test_user_admin, test_user_company, test_user_company_blocked, test_user_company_disabled] = test_users; - - const test_agent = agent(); - - beforeAll(async () => { - await Account.deleteMany({}); - - const test_company_data = await generateTestCompany(); - const test_company_blocked_data = await generateTestCompany({ isBlocked: true }); - const test_company_disabled_data = await generateTestCompany({ isDisabled: true }); - - test_companies = await Company.create( - [test_company_data, test_company_blocked_data, test_company_disabled_data], - { session: null } - ); - - [test_company, test_company_blocked, test_company_disabled] = test_companies; - - test_offer = await Offer.create( - generateTestOffer({ - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - }) - ); - - for (let i = 0; i < test_users.length; i++) { - if (i === 0) { // Admin - await Account.create({ - email: test_users[i].email, - password: await hash(test_users[i].password), - isAdmin: true, - }); - } else { // Company - await Account.create({ - email: test_users[i].email, - password: await hash(test_users[i].password), - company: test_companies[i - 1]._id, - }); - } - } - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - afterAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - }); - - describe("ID Validation", () => { - beforeEach(async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - }); - - test("Should fail if id is not a valid ObjectID", async () => { - const id = "123"; - const res = await test_agent - .put(`/company/${id}/edit`) - .send() - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual( - { "location": "params", "msg": ValidationReasons.OBJECT_ID, "param": "companyId", "value": id } - ); - }); - - test("Should fail if id is not a valid company", async () => { - const id = "111111111111111111111111"; - - const res = await test_agent - .put(`/company/${id}/edit`) - .send() - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual( - { "location": "params", "msg": ValidationReasons.COMPANY_NOT_FOUND(id), "param": "companyId", "value": id } - ); - }); - }); - - describe("Using a bad user", () => { - test("Should fail if different user", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_blocked) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send({ - name: changing_values.name, - }) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - }); - - test("Should fail if not logged in", async () => { - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send({ - bio: changing_values.bio, - contacts: changing_values.contacts, - }) - .expect(HTTPStatus.UNAUTHORIZED); - - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - }); - - describe("Using a good user", () => { - test("Should pass if god", async () => { - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send(withGodToken({ - name: changing_values.name, - bio: changing_values.bio, - })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("name", changing_values.name); - expect(res.body).toHaveProperty("bio", changing_values.bio); - }); - - test("Should pass if admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .field("name", changing_values.name) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("name", changing_values.name); - }); - - test("Should pass if same company", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .field("name", changing_values.name) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("name", changing_values.name); - }); - }); - - test("Offer should be updated", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send({ - name: changing_values.name, - contacts: changing_values.contacts, - }) - .expect(HTTPStatus.OK); - - test_offer = await Offer.findById(test_offer._id); - - expect(res.body).toHaveProperty("name", changing_values.name); - expect(res.body).toHaveProperty("contacts", changing_values.contacts); - - expect(test_offer.ownerName).toEqual(changing_values.name); - expect(test_offer.contacts).toEqual(changing_values.contacts); - }); - - describe("Updating company logo", () => { - test("Should fail if not an image", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .attach("logo", "test/data/not-a-logo.txt") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.IMAGE_FORMAT, - "param": "logo" - }); - }); - - test("Should fail if image is too big", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .attach("logo", "test/data/logo-niaefeup-10mb.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), - "param": "logo" - }); - }); - - test("Should succeed if image is valid", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .attach("logo", changing_values.logo) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("logo"); - }); - }); - - describe("Using disabled/blocked company (god)", () => { - test("Should fail if company is blocked (god)", async () => { - const res = await test_agent - .put(`/company/${test_company_blocked._id}/edit`) - .send(withGodToken({ - name: "Changing Blocked Company", - })) - .expect(HTTPStatus.FORBIDDEN); - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_BLOCKED }); - }); - - test("Should fail if company is disabled (god)", async () => { - const res = await test_agent - .put(`/company/${test_company_disabled._id}/edit`) - .send(withGodToken({ - name: "Changing Disabled Company", - })) - .expect(HTTPStatus.FORBIDDEN); - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_DISABLED }); - }); - }); - - describe("Using disabled/blocked company (user)", () => { - test("Should fail if company is blocked (user)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_blocked) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_blocked._id}/edit`) - .send({ - name: "Changing Blocked Company", - }) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_BLOCKED }); - }); - - test("Should fail if company is disabled (user)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_disabled) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_disabled._id}/edit`) - .send({ - bio: "As user", - }) - .expect(HTTPStatus.FORBIDDEN); - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_DISABLED }); - }); - }); - }); -}); diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js index d79cfe6b..fe1d9b9e 100644 --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -1,27 +1,27 @@ +import base64url from "base64url"; import { StatusCodes as HTTPStatus } from "http-status-codes"; -import Offer from "../../src/models/Offer"; -import JobTypes from "../../src/models/constants/JobTypes"; -import * as FieldConstants from "../../src/models/constants/FieldTypes"; -import * as TechnologyConstants from "../../src/models/constants/TechnologyTypes"; import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import ValidatorTester from "../utils/ValidatorTester"; -import withGodToken from "../utils/GodToken"; -import { DAY_TO_MS } from "../utils/TimeConstants"; -import OfferConstants from "../../src/models/constants/Offer"; +import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; +import { concurrentOffersNotExceeded } from "../../src/api/middleware/validators/validatorUtils"; +import { OFFER_DISABLED_NOTIFICATION } from "../../src/email-templates/companyOfferDisabled"; +import EmailService from "../../src/lib/emailService"; +import hash from "../../src/lib/passwordHashing"; import Account from "../../src/models/Account"; import Company from "../../src/models/Company"; -import hash from "../../src/lib/passwordHashing"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; +import Offer from "../../src/models/Offer"; import CompanyConstants from "../../src/models/constants/Company"; +import * as FieldConstants from "../../src/models/constants/FieldTypes"; +import JobTypes from "../../src/models/constants/JobTypes"; +import OfferConstants from "../../src/models/constants/Offer"; +import * as TechnologyConstants from "../../src/models/constants/TechnologyTypes"; import { MONTH_IN_MS, OFFER_MAX_LIFETIME_MONTHS } from "../../src/models/constants/TimeConstants"; import OfferService from "../../src/services/offer"; -import EmailService from "../../src/lib/emailService"; -import { concurrentOffersNotExceeded } from "../../src/api/middleware/validators/validatorUtils"; -import { OFFER_DISABLED_NOTIFICATION } from "../../src/email-templates/companyOfferDisabled"; -import base64url from "base64url"; +import withGodToken from "../utils/GodToken"; +import { DAY_TO_MS } from "../utils/TimeConstants"; +import ValidatorTester from "../utils/ValidatorTester"; //---------------------------------------------------------------- describe("Offer endpoint tests", () => { @@ -2704,6 +2704,7 @@ describe("Offer endpoint tests", () => { await Offer.deleteMany({}); }); + // TODO: This is perfect for the "test.each" Jest construct test("should sort by publishDate by default", async () => { const res = await request() .get("/offers"); @@ -3195,6 +3196,7 @@ describe("Offer endpoint tests", () => { expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); }); + test("should fail if an offer does not exist", async () => { const id = "5facf0cdb8bc30016ee58952"; const res = await request() @@ -3642,6 +3644,7 @@ describe("Offer endpoint tests", () => { expect(res.body.errors[0]).toHaveProperty("param", "jobMinDuration"); expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_BEFORE("jobMaxDuration")); }); + test("should fail if maxDuration smaller than offer's minDuration", async () => { const res = await test_agent .post(`/offers/edit/${future_test_offer._id.toString()}`) @@ -3650,6 +3653,7 @@ describe("Offer endpoint tests", () => { expect(res.body.errors[0]).toHaveProperty("param", "jobMaxDuration"); expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_AFTER("jobMinDuration")); }); + test("should fail if invalid combination of jobDuration in request", async () => { const res = await test_agent .post(`/offers/edit/${future_test_offer._id.toString()}`) @@ -4136,6 +4140,7 @@ describe("Offer endpoint tests", () => { describe("POST /offers/:offerId/disable", () => { let test_offer, test_offer_2, hidden_default_test_offer, hidden_user_test_offer, email_test_offer; + beforeAll(async () => { test_offer = await Offer.create({ ...generateTestOffer({ diff --git a/test/end-to-end/offer/:id/archive.js b/test/end-to-end/offer/:id/archive.js new file mode 100644 index 00000000..e69de29b diff --git a/test/end-to-end/offer/:id/disable.js b/test/end-to-end/offer/:id/disable.js new file mode 100644 index 00000000..e69de29b diff --git a/test/end-to-end/offer/:id/enable.js b/test/end-to-end/offer/:id/enable.js new file mode 100644 index 00000000..e69de29b diff --git a/test/end-to-end/offer/:id/hide.js b/test/end-to-end/offer/:id/hide.js new file mode 100644 index 00000000..e69de29b diff --git a/test/end-to-end/offer/:id/index.js b/test/end-to-end/offer/:id/index.js new file mode 100644 index 00000000..e69de29b diff --git a/test/end-to-end/offer/company/:companyId/index.js b/test/end-to-end/offer/company/:companyId/index.js new file mode 100644 index 00000000..e69de29b diff --git a/test/end-to-end/offer/edit/:offerId/index.js b/test/end-to-end/offer/edit/:offerId/index.js new file mode 100644 index 00000000..e69de29b diff --git a/test/end-to-end/offer/index.js b/test/end-to-end/offer/index.js new file mode 100644 index 00000000..e69de29b From 2183a949b055b05e0bad9177c8fcfb26435d11c5 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Mon, 26 Jun 2023 13:05:23 +0100 Subject: [PATCH 29/30] Prepared test files --- test/end-to-end/offer.js | 141 +------------ test/end-to-end/offer/:id/archive.js | 3 + test/end-to-end/offer/:id/disable.js | 3 + test/end-to-end/offer/:id/enable.js | 3 + test/end-to-end/offer/:id/hide.js | 3 + test/end-to-end/offer/:id/index.js | 3 + .../offer/company/:companyId/index.js | 196 ++++++++++++++++++ test/end-to-end/offer/edit/:offerId/index.js | 3 + test/end-to-end/offer/index.js | 3 + 9 files changed, 218 insertions(+), 140 deletions(-) diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js index fe1d9b9e..b88d2d2a 100644 --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -25,6 +25,7 @@ import ValidatorTester from "../utils/ValidatorTester"; //---------------------------------------------------------------- describe("Offer endpoint tests", () => { + const generateTestOffer = (params) => ({ title: "Test Offer", publishDate: (new Date(Date.now())).toISOString(), @@ -3043,146 +3044,6 @@ describe("Offer endpoint tests", () => { }); }); - describe("GET /offers/company/:companyId", () => { - beforeAll(async () => { - await Offer.deleteMany({}); - }); - - describe("Id Validation", () => { - test("should fail if requested an invalid companyId", async () => { - const companyId = "123"; - const res = await request() - .get(`/offers/company/${companyId}`); - - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("should fail if there isn't a company with that id", async () => { - const missingCompanyId = "60ddb0bb2849830020883f91"; - const res = await request().get(`/offers/company/${missingCompanyId}`); - - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(missingCompanyId)); - }); - }); - - describe("Get offer by companyId", () => { - const test_offers = [{}, {}, {}, {}]; - const test_agent = agent(); - - beforeAll(async () => { - await Offer.deleteMany({}); - - const createOffer = async (offer) => { - const { _id, owner, ownerName, ownerLogo } = await Offer.create({ - ...offer, - owner: test_company._id.toString(), - ownerName: test_company.name, - ownerLogo: test_company.logo, - }); - return { - ...offer, - owner: owner.toString(), - ownerName, - ownerLogo, - _id: _id.toString() - }; - }; - - (await Promise.all(test_offers - .map((_, i) => createOffer({ ...generateTestOffer(), isHidden: i === 2 })))) - .forEach((elem, i) => { - test_offers[i] = elem; - }); - }); - - test("should return hidden company offers as company", async () => { - // Login wiht test_user_company - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - - // Logout - await test_agent - .del("/auth/login") - .expect(HTTPStatus.OK); - }); - - test("should return non-hidden offers", async () => { - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() - ); - }); - - test("should return non-hidden offers, even if target owner is set", async () => { - const res = await test_agent - .get(`/offers/company/${test_company._id}`) - .send({ - owner: test_company._id - }); - - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() - ); - }); - - test("should return hidden company offers as admin", async () => { - // Login with test_user_company - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - - // Logout - await test_agent - .del("/auth/login") - .expect(HTTPStatus.OK); - }); - - test("should return hidden company offers with god token", async () => { - // Send request with god token - const res = await test_agent - .get(`/offers/company/${test_company._id}`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - }); - }); - }); - describe("GET /offers/:offerId", () => { beforeAll(async () => { diff --git a/test/end-to-end/offer/:id/archive.js b/test/end-to-end/offer/:id/archive.js index e69de29b..491247ec 100644 --- a/test/end-to-end/offer/:id/archive.js +++ b/test/end-to-end/offer/:id/archive.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/offer/:id/disable.js b/test/end-to-end/offer/:id/disable.js index e69de29b..491247ec 100644 --- a/test/end-to-end/offer/:id/disable.js +++ b/test/end-to-end/offer/:id/disable.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/offer/:id/enable.js b/test/end-to-end/offer/:id/enable.js index e69de29b..491247ec 100644 --- a/test/end-to-end/offer/:id/enable.js +++ b/test/end-to-end/offer/:id/enable.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/offer/:id/hide.js b/test/end-to-end/offer/:id/hide.js index e69de29b..491247ec 100644 --- a/test/end-to-end/offer/:id/hide.js +++ b/test/end-to-end/offer/:id/hide.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/offer/:id/index.js b/test/end-to-end/offer/:id/index.js index e69de29b..491247ec 100644 --- a/test/end-to-end/offer/:id/index.js +++ b/test/end-to-end/offer/:id/index.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/offer/company/:companyId/index.js b/test/end-to-end/offer/company/:companyId/index.js index e69de29b..a44c8aa5 100644 --- a/test/end-to-end/offer/company/:companyId/index.js +++ b/test/end-to-end/offer/company/:companyId/index.js @@ -0,0 +1,196 @@ +import { StatusCodes } from "http-status-codes"; +import ValidationReasons from "../../../../../src/api/middleware/validators/validationReasons"; +import Offer from "../../../../../src/models/Offer"; +// import { DAY_TO_MS } from "../../../../utils/TimeConstants"; +import Company from "../../../../../src/models/Company"; +import Account from "../../../../../src/models/Account"; +import { ErrorTypes } from "../../../../../src/api/middleware/errorHandler"; + +describe("GET /offers/company/:companyId", () => { + + /* const generateTestOffer = (params) => ({ + title: "Test Offer", + publishDate: (new Date(Date.now())).toISOString(), + publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), + description: "For Testing Purposes", + contacts: ["geral@niaefeup.pt", "229417766"], + jobMinDuration: 1, + jobMaxDuration: 6, + jobType: "SUMMER INTERNSHIP", + fields: ["DEVOPS", "BACKEND", "OTHER"], + technologies: ["React", "CSS"], + location: "Testing Street, Test City, 123", + isHidden: false, + isArchived: false, + requirements: ["The candidate must be tested", "Fluent in testJS"], + vacancies: 2, + ...params, + }); */ + + beforeAll(async () => { + await Offer.deleteMany({}); + await Company.deleteMany({}); + await Account.deleteMany({}); + }); + + afterAll(async () => { + await Offer.deleteMany({}); + await Company.deleteMany({}); + await Account.deleteMany({}); + }); + + describe("Id Validation", () => { + test("should fail if requested an invalid companyId", async () => { + const res = await request() + .get("/offers/company/123") + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "param": "companyId", + "msg": ValidationReasons.OBJECT_ID, + }) + ])); + }); + + test("should fail if there isn't a company with that id", async () => { + const missingCompanyId = "60ddb0bb2849830020883f91"; + + const res = await request() + .get(`/offers/company/${missingCompanyId}`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors", expect.arrayContaining([ + expect.objectContaining({ + "param": "companyId", + "msg": ValidationReasons.COMPANY_NOT_FOUND(missingCompanyId), + }) + ])); + }); + }); + + describe("Without auth", () => { }); + + describe("With auth", () => { }); + + /* + describe("Get offer by companyId", () => { + const test_offers = [{}, {}, {}, {}]; + const test_agent = agent(); + + beforeAll(async () => { + await Offer.deleteMany({}); + + const createOffer = async (offer) => { + const { _id, owner, ownerName, ownerLogo } = await Offer.create({ + ...offer, + owner: test_company._id.toString(), + ownerName: test_company.name, + ownerLogo: test_company.logo, + }); + return { + ...offer, + owner: owner.toString(), + ownerName, + ownerLogo, + _id: _id.toString() + }; + }; + + (await Promise.all(test_offers + .map((_, i) => createOffer({ ...generateTestOffer(), isHidden: i === 2 })))) + .forEach((elem, i) => { + test_offers[i] = elem; + }); + }); + + test("should return hidden company offers as company", async () => { + // Login wiht test_user_company + await test_agent + .post("/auth/login") + .send(test_user_company) + .expect(StatusCodes.OK); + + const res = await test_agent.get(`/offers/company/${test_company._id}`); + expect(res.status).toBe(StatusCodes.OK); + + const extractedData = res.body; + expect(extractedData.map((offer) => offer._id).sort()) + .toMatchObject( + test_offers.map((offer) => offer._id).sort() + ); + + // Logout + await test_agent + .del("/auth/login") + .expect(StatusCodes.OK); + }); + + test("should return non-hidden offers", async () => { + const res = await test_agent.get(`/offers/company/${test_company._id}`); + expect(res.status).toBe(StatusCodes.OK); + + const extractedData = res.body; + expect(extractedData.map((offer) => offer._id).sort()) + .toMatchObject( + test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() + ); + }); + + test("should return non-hidden offers, even if target owner is set", async () => { + const res = await test_agent + .get(`/offers/company/${test_company._id}`) + .send({ + owner: test_company._id + }); + + expect(res.status).toBe(StatusCodes.OK); + + const extractedData = res.body; + expect(extractedData.map((offer) => offer._id).sort()) + .toMatchObject( + test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() + ); + }); + + test("should return hidden company offers as admin", async () => { + // Login with test_user_company + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent.get(`/offers/company/${test_company._id}`); + expect(res.status).toBe(StatusCodes.OK); + + const extractedData = res.body; + expect(extractedData.map((offer) => offer._id).sort()) + .toMatchObject( + test_offers.map((offer) => offer._id).sort() + ); + + // Logout + await test_agent + .del("/auth/login") + .expect(StatusCodes.OK); + }); + + test("should return hidden company offers with god token", async () => { + // Send request with god token + const res = await test_agent + .get(`/offers/company/${test_company._id}`) + .send(withGodToken()); + + expect(res.status).toBe(StatusCodes.OK); + + const extractedData = res.body; + expect(extractedData.map((offer) => offer._id).sort()) + .toMatchObject( + test_offers.map((offer) => offer._id).sort() + ); + }); + }); + */ +}); diff --git a/test/end-to-end/offer/edit/:offerId/index.js b/test/end-to-end/offer/edit/:offerId/index.js index e69de29b..491247ec 100644 --- a/test/end-to-end/offer/edit/:offerId/index.js +++ b/test/end-to-end/offer/edit/:offerId/index.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/offer/index.js b/test/end-to-end/offer/index.js index e69de29b..491247ec 100644 --- a/test/end-to-end/offer/index.js +++ b/test/end-to-end/offer/index.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); From 5bd79a6430b51ec0e449fcd17c31c93e0fad11e2 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 29 Nov 2023 15:59:19 +0000 Subject: [PATCH 30/30] Updated more tests --- .dockerignore | 0 .env | 0 .env.test | 0 .eslintrc | 0 .github/workflows/ci.yml | 0 .gitignore | 0 Dockerfile | 0 Dockerfile-prod | 0 Dockerfile-test | 0 LICENSE | 0 README.md | 0 __mocks__/nodemailer.js | 0 babel.config.json | 0 certs/.gitignore | 0 codecov.yaml | 0 docker-compose.yml | 0 documentation/.gitignore | 0 documentation/README.md | 0 documentation/babel.config.js | 0 documentation/docs-index.js | 0 documentation/docs/applications/approve.md | 0 documentation/docs/applications/create.md | 0 documentation/docs/applications/reject.md | 0 documentation/docs/applications/search.md | 0 documentation/docs/auth/confirm.md | 0 documentation/docs/auth/finish-recovery.md | 0 documentation/docs/auth/login.md | 0 documentation/docs/auth/logout.md | 0 documentation/docs/auth/me.md | 0 documentation/docs/auth/recover.md | 0 documentation/docs/auth/register.md | 0 documentation/docs/companies/block.md | 0 .../docs/companies/concurrent-offers.md | 0 documentation/docs/companies/delete.md | 0 documentation/docs/companies/disable.md | 0 documentation/docs/companies/enable.md | 0 .../docs/companies/finish-registration.md | 0 documentation/docs/companies/list.md | 0 documentation/docs/companies/unblock.md | 0 documentation/docs/intro.md | 0 documentation/docs/intro/getting-started.md | 0 documentation/docs/intro/how-to-docs.md | 0 documentation/docs/offers/archive.md | 0 documentation/docs/offers/create.md | 0 documentation/docs/offers/disable.md | 0 documentation/docs/offers/edit.md | 0 documentation/docs/offers/enable.md | 0 documentation/docs/offers/get-company.md | 0 documentation/docs/offers/get.md | 0 documentation/docs/offers/hide.md | 0 documentation/docs/offers/search.md | 0 documentation/docusaurus.config.js | 0 documentation/package-lock.json | 0 documentation/package.json | 0 documentation/src/css/custom.css | 0 documentation/src/highlight.js | 0 documentation/static/.nojekyll | 0 documentation/static/img/favicon.ico | Bin documentation/static/img/logo_2018.svg | 0 jest.config.js | 0 netlify.toml | 0 package-lock.json | 0 package.json | 0 src/api/APIErrorTypes.js | 0 src/api/index.js | 0 src/api/middleware/auth.js | 0 src/api/middleware/company.js | 0 src/api/middleware/errorHandler.js | 0 src/api/middleware/files.js | 0 src/api/middleware/offer.js | 0 src/api/middleware/utils.js | 0 src/api/middleware/validators/application.js | 0 src/api/middleware/validators/auth.js | 0 src/api/middleware/validators/company.js | 0 src/api/middleware/validators/offer.js | 0 .../validators/validationReasons.js | 0 .../middleware/validators/validatorUtils.js | 0 src/api/routes/application.js | 0 src/api/routes/auth.js | 0 src/api/routes/company.js | 0 src/api/routes/offer.js | 0 src/api/routes/review.js | 0 src/config/env.js | 0 src/config/multer.js | 0 src/config/passport.js | 0 src/email-templates/accountManagement.js | 0 .../approval_notification.handlebars | 0 .../companyApplicationApproval.js | 0 src/email-templates/companyManagement.js | 0 src/email-templates/companyOfferDisabled.js | 0 .../company_blocked_notification.handlebars | 0 .../company_deleted_notification.handlebars | 0 .../company_disabled_notification.handlebars | 0 .../company_enabled_notification.handlebars | 0 .../company_unblocked_notification.handlebars | 0 src/email-templates/layouts/main.handlebars | 0 .../new_company_application_admins.handlebars | 0 ...new_company_application_company.handlebars | 0 .../offer_disabled_notification.handlebars | 0 .../rejection_notification.handlebars | 0 .../request_password_recovery.handlebars | 0 src/index.js | 0 src/lib/emailService.js | 0 src/lib/passwordHashing.js | 0 src/lib/token.js | 0 src/loaders/emailService.js | 0 src/loaders/express.js | 0 src/loaders/index.js | 0 src/loaders/mongoose.js | 0 src/loaders/requestEnhancers.js | 0 src/loaders/static.js | 0 src/models/Account.js | 0 src/models/Company.js | 0 src/models/CompanyApplication.js | 0 src/models/Offer.js | 0 src/models/Point.js | 0 src/models/constants/Account.js | 0 src/models/constants/ApplicationStatus.js | 0 src/models/constants/Company.js | 0 src/models/constants/CompanyApplication.js | 0 src/models/constants/FieldTypes.js | 0 src/models/constants/JobTypes.js | 0 src/models/constants/Offer.js | 0 src/models/constants/TechnologyTypes.js | 0 src/models/constants/TimeConstants.js | 0 src/models/modelUtils.js | 0 src/services/account.js | 0 src/services/application.js | 0 src/services/company.js | 0 src/services/offer.js | 0 src/setupTests.js | 0 test/data/logo-niaefeup-10mb.png | Bin test/data/logo-niaefeup.png | Bin test/data/not-a-logo.txt | 0 .../applications/company/:id/approve.js | 205 ----- .../applications/company/:id/reject.js | 184 ----- .../end-to-end/applications/company/search.js | 0 test/end-to-end/apply/company.js | 0 test/end-to-end/auth/login.js | 0 test/end-to-end/auth/me.js | 0 .../end-to-end/auth/recover/:token/confirm.js | 188 ----- test/end-to-end/auth/recover/request.js | 0 test/end-to-end/auth/register.js | 0 test/end-to-end/company/:id/block.js | 406 --------- test/end-to-end/company/:id/delete.js | 313 ------- test/end-to-end/company/:id/disable.js | 330 -------- test/end-to-end/company/:id/edit.js | 609 -------------- test/end-to-end/company/:id/enable.js | 369 --------- ...sReachedMaxConcurrentOffersBetweenDates.js | 354 -------- test/end-to-end/company/:id/index.js | 776 ------------------ test/end-to-end/company/:id/unblock.js | 423 ---------- test/end-to-end/company/application/finish.js | 0 test/end-to-end/company/index.js | 0 test/end-to-end/offer.js | 0 test/end-to-end/offer/:id/archive.js | 3 - test/end-to-end/offer/:id/disable.js | 3 - test/end-to-end/offer/:id/enable.js | 3 - test/end-to-end/offer/:id/hide.js | 3 - test/end-to-end/offer/:id/index.js | 3 - .../offer/company/:companyId/index.js | 196 ----- test/end-to-end/offer/edit/:offerId/index.js | 3 - test/end-to-end/offer/index.js | 0 test/schema/AccountSchema.js | 0 test/schema/CompanyApplicationSchema.js | 0 test/schema/CompanySchema.js | 0 test/schema/OfferSchema.js | 0 test/unit/EmailService.js | 0 test/unit/auth.js | 0 test/unit/token.js | 0 test/unit/utils.js | 0 test/utils/GodToken.js | 0 test/utils/SchemaTester.js | 0 test/utils/TimeConstants.js | 0 test/utils/ValidatorTester.js | 0 174 files changed, 4371 deletions(-) mode change 100644 => 100755 .dockerignore mode change 100644 => 100755 .env mode change 100644 => 100755 .env.test mode change 100644 => 100755 .eslintrc mode change 100644 => 100755 .github/workflows/ci.yml mode change 100644 => 100755 .gitignore mode change 100644 => 100755 Dockerfile mode change 100644 => 100755 Dockerfile-prod mode change 100644 => 100755 Dockerfile-test mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 __mocks__/nodemailer.js mode change 100644 => 100755 babel.config.json mode change 100644 => 100755 certs/.gitignore mode change 100644 => 100755 codecov.yaml mode change 100644 => 100755 docker-compose.yml mode change 100644 => 100755 documentation/.gitignore mode change 100644 => 100755 documentation/README.md mode change 100644 => 100755 documentation/babel.config.js mode change 100644 => 100755 documentation/docs-index.js mode change 100644 => 100755 documentation/docs/applications/approve.md mode change 100644 => 100755 documentation/docs/applications/create.md mode change 100644 => 100755 documentation/docs/applications/reject.md mode change 100644 => 100755 documentation/docs/applications/search.md mode change 100644 => 100755 documentation/docs/auth/confirm.md mode change 100644 => 100755 documentation/docs/auth/finish-recovery.md mode change 100644 => 100755 documentation/docs/auth/login.md mode change 100644 => 100755 documentation/docs/auth/logout.md mode change 100644 => 100755 documentation/docs/auth/me.md mode change 100644 => 100755 documentation/docs/auth/recover.md mode change 100644 => 100755 documentation/docs/auth/register.md mode change 100644 => 100755 documentation/docs/companies/block.md mode change 100644 => 100755 documentation/docs/companies/concurrent-offers.md mode change 100644 => 100755 documentation/docs/companies/delete.md mode change 100644 => 100755 documentation/docs/companies/disable.md mode change 100644 => 100755 documentation/docs/companies/enable.md mode change 100644 => 100755 documentation/docs/companies/finish-registration.md mode change 100644 => 100755 documentation/docs/companies/list.md mode change 100644 => 100755 documentation/docs/companies/unblock.md mode change 100644 => 100755 documentation/docs/intro.md mode change 100644 => 100755 documentation/docs/intro/getting-started.md mode change 100644 => 100755 documentation/docs/intro/how-to-docs.md mode change 100644 => 100755 documentation/docs/offers/archive.md mode change 100644 => 100755 documentation/docs/offers/create.md mode change 100644 => 100755 documentation/docs/offers/disable.md mode change 100644 => 100755 documentation/docs/offers/edit.md mode change 100644 => 100755 documentation/docs/offers/enable.md mode change 100644 => 100755 documentation/docs/offers/get-company.md mode change 100644 => 100755 documentation/docs/offers/get.md mode change 100644 => 100755 documentation/docs/offers/hide.md mode change 100644 => 100755 documentation/docs/offers/search.md mode change 100644 => 100755 documentation/docusaurus.config.js mode change 100644 => 100755 documentation/package-lock.json mode change 100644 => 100755 documentation/package.json mode change 100644 => 100755 documentation/src/css/custom.css mode change 100644 => 100755 documentation/src/highlight.js mode change 100644 => 100755 documentation/static/.nojekyll mode change 100644 => 100755 documentation/static/img/favicon.ico mode change 100644 => 100755 documentation/static/img/logo_2018.svg mode change 100644 => 100755 jest.config.js mode change 100644 => 100755 netlify.toml mode change 100644 => 100755 package-lock.json mode change 100644 => 100755 package.json mode change 100644 => 100755 src/api/APIErrorTypes.js mode change 100644 => 100755 src/api/index.js mode change 100644 => 100755 src/api/middleware/auth.js mode change 100644 => 100755 src/api/middleware/company.js mode change 100644 => 100755 src/api/middleware/errorHandler.js mode change 100644 => 100755 src/api/middleware/files.js mode change 100644 => 100755 src/api/middleware/offer.js mode change 100644 => 100755 src/api/middleware/utils.js mode change 100644 => 100755 src/api/middleware/validators/application.js mode change 100644 => 100755 src/api/middleware/validators/auth.js mode change 100644 => 100755 src/api/middleware/validators/company.js mode change 100644 => 100755 src/api/middleware/validators/offer.js mode change 100644 => 100755 src/api/middleware/validators/validationReasons.js mode change 100644 => 100755 src/api/middleware/validators/validatorUtils.js mode change 100644 => 100755 src/api/routes/application.js mode change 100644 => 100755 src/api/routes/auth.js mode change 100644 => 100755 src/api/routes/company.js mode change 100644 => 100755 src/api/routes/offer.js mode change 100644 => 100755 src/api/routes/review.js mode change 100644 => 100755 src/config/env.js mode change 100644 => 100755 src/config/multer.js mode change 100644 => 100755 src/config/passport.js mode change 100644 => 100755 src/email-templates/accountManagement.js mode change 100644 => 100755 src/email-templates/approval_notification.handlebars mode change 100644 => 100755 src/email-templates/companyApplicationApproval.js mode change 100644 => 100755 src/email-templates/companyManagement.js mode change 100644 => 100755 src/email-templates/companyOfferDisabled.js mode change 100644 => 100755 src/email-templates/company_blocked_notification.handlebars mode change 100644 => 100755 src/email-templates/company_deleted_notification.handlebars mode change 100644 => 100755 src/email-templates/company_disabled_notification.handlebars mode change 100644 => 100755 src/email-templates/company_enabled_notification.handlebars mode change 100644 => 100755 src/email-templates/company_unblocked_notification.handlebars mode change 100644 => 100755 src/email-templates/layouts/main.handlebars mode change 100644 => 100755 src/email-templates/new_company_application_admins.handlebars mode change 100644 => 100755 src/email-templates/new_company_application_company.handlebars mode change 100644 => 100755 src/email-templates/offer_disabled_notification.handlebars mode change 100644 => 100755 src/email-templates/rejection_notification.handlebars mode change 100644 => 100755 src/email-templates/request_password_recovery.handlebars mode change 100644 => 100755 src/index.js mode change 100644 => 100755 src/lib/emailService.js mode change 100644 => 100755 src/lib/passwordHashing.js mode change 100644 => 100755 src/lib/token.js mode change 100644 => 100755 src/loaders/emailService.js mode change 100644 => 100755 src/loaders/express.js mode change 100644 => 100755 src/loaders/index.js mode change 100644 => 100755 src/loaders/mongoose.js mode change 100644 => 100755 src/loaders/requestEnhancers.js mode change 100644 => 100755 src/loaders/static.js mode change 100644 => 100755 src/models/Account.js mode change 100644 => 100755 src/models/Company.js mode change 100644 => 100755 src/models/CompanyApplication.js mode change 100644 => 100755 src/models/Offer.js mode change 100644 => 100755 src/models/Point.js mode change 100644 => 100755 src/models/constants/Account.js mode change 100644 => 100755 src/models/constants/ApplicationStatus.js mode change 100644 => 100755 src/models/constants/Company.js mode change 100644 => 100755 src/models/constants/CompanyApplication.js mode change 100644 => 100755 src/models/constants/FieldTypes.js mode change 100644 => 100755 src/models/constants/JobTypes.js mode change 100644 => 100755 src/models/constants/Offer.js mode change 100644 => 100755 src/models/constants/TechnologyTypes.js mode change 100644 => 100755 src/models/constants/TimeConstants.js mode change 100644 => 100755 src/models/modelUtils.js mode change 100644 => 100755 src/services/account.js mode change 100644 => 100755 src/services/application.js mode change 100644 => 100755 src/services/company.js mode change 100644 => 100755 src/services/offer.js mode change 100644 => 100755 src/setupTests.js mode change 100644 => 100755 test/data/logo-niaefeup-10mb.png mode change 100644 => 100755 test/data/logo-niaefeup.png mode change 100644 => 100755 test/data/not-a-logo.txt delete mode 100644 test/end-to-end/applications/company/:id/approve.js delete mode 100644 test/end-to-end/applications/company/:id/reject.js mode change 100644 => 100755 test/end-to-end/applications/company/search.js mode change 100644 => 100755 test/end-to-end/apply/company.js mode change 100644 => 100755 test/end-to-end/auth/login.js mode change 100644 => 100755 test/end-to-end/auth/me.js delete mode 100644 test/end-to-end/auth/recover/:token/confirm.js mode change 100644 => 100755 test/end-to-end/auth/recover/request.js mode change 100644 => 100755 test/end-to-end/auth/register.js delete mode 100644 test/end-to-end/company/:id/block.js delete mode 100644 test/end-to-end/company/:id/delete.js delete mode 100644 test/end-to-end/company/:id/disable.js delete mode 100644 test/end-to-end/company/:id/edit.js delete mode 100644 test/end-to-end/company/:id/enable.js delete mode 100644 test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js delete mode 100644 test/end-to-end/company/:id/index.js delete mode 100644 test/end-to-end/company/:id/unblock.js mode change 100644 => 100755 test/end-to-end/company/application/finish.js mode change 100644 => 100755 test/end-to-end/company/index.js mode change 100644 => 100755 test/end-to-end/offer.js delete mode 100644 test/end-to-end/offer/:id/archive.js delete mode 100644 test/end-to-end/offer/:id/disable.js delete mode 100644 test/end-to-end/offer/:id/enable.js delete mode 100644 test/end-to-end/offer/:id/hide.js delete mode 100644 test/end-to-end/offer/:id/index.js delete mode 100644 test/end-to-end/offer/company/:companyId/index.js delete mode 100644 test/end-to-end/offer/edit/:offerId/index.js mode change 100644 => 100755 test/end-to-end/offer/index.js mode change 100644 => 100755 test/schema/AccountSchema.js mode change 100644 => 100755 test/schema/CompanyApplicationSchema.js mode change 100644 => 100755 test/schema/CompanySchema.js mode change 100644 => 100755 test/schema/OfferSchema.js mode change 100644 => 100755 test/unit/EmailService.js mode change 100644 => 100755 test/unit/auth.js mode change 100644 => 100755 test/unit/token.js mode change 100644 => 100755 test/unit/utils.js mode change 100644 => 100755 test/utils/GodToken.js mode change 100644 => 100755 test/utils/SchemaTester.js mode change 100644 => 100755 test/utils/TimeConstants.js mode change 100644 => 100755 test/utils/ValidatorTester.js diff --git a/.dockerignore b/.dockerignore old mode 100644 new mode 100755 diff --git a/.env b/.env old mode 100644 new mode 100755 diff --git a/.env.test b/.env.test old mode 100644 new mode 100755 diff --git a/.eslintrc b/.eslintrc old mode 100644 new mode 100755 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 diff --git a/Dockerfile-prod b/Dockerfile-prod old mode 100644 new mode 100755 diff --git a/Dockerfile-test b/Dockerfile-test old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/__mocks__/nodemailer.js b/__mocks__/nodemailer.js old mode 100644 new mode 100755 diff --git a/babel.config.json b/babel.config.json old mode 100644 new mode 100755 diff --git a/certs/.gitignore b/certs/.gitignore old mode 100644 new mode 100755 diff --git a/codecov.yaml b/codecov.yaml old mode 100644 new mode 100755 diff --git a/docker-compose.yml b/docker-compose.yml old mode 100644 new mode 100755 diff --git a/documentation/.gitignore b/documentation/.gitignore old mode 100644 new mode 100755 diff --git a/documentation/README.md b/documentation/README.md old mode 100644 new mode 100755 diff --git a/documentation/babel.config.js b/documentation/babel.config.js old mode 100644 new mode 100755 diff --git a/documentation/docs-index.js b/documentation/docs-index.js old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/approve.md b/documentation/docs/applications/approve.md old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/create.md b/documentation/docs/applications/create.md old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/reject.md b/documentation/docs/applications/reject.md old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/search.md b/documentation/docs/applications/search.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/confirm.md b/documentation/docs/auth/confirm.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/finish-recovery.md b/documentation/docs/auth/finish-recovery.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/login.md b/documentation/docs/auth/login.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/logout.md b/documentation/docs/auth/logout.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/me.md b/documentation/docs/auth/me.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/recover.md b/documentation/docs/auth/recover.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/register.md b/documentation/docs/auth/register.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/block.md b/documentation/docs/companies/block.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/concurrent-offers.md b/documentation/docs/companies/concurrent-offers.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/delete.md b/documentation/docs/companies/delete.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/disable.md b/documentation/docs/companies/disable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/enable.md b/documentation/docs/companies/enable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/finish-registration.md b/documentation/docs/companies/finish-registration.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/list.md b/documentation/docs/companies/list.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/unblock.md b/documentation/docs/companies/unblock.md old mode 100644 new mode 100755 diff --git a/documentation/docs/intro.md b/documentation/docs/intro.md old mode 100644 new mode 100755 diff --git a/documentation/docs/intro/getting-started.md b/documentation/docs/intro/getting-started.md old mode 100644 new mode 100755 diff --git a/documentation/docs/intro/how-to-docs.md b/documentation/docs/intro/how-to-docs.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/archive.md b/documentation/docs/offers/archive.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/create.md b/documentation/docs/offers/create.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/disable.md b/documentation/docs/offers/disable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/edit.md b/documentation/docs/offers/edit.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/enable.md b/documentation/docs/offers/enable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/get-company.md b/documentation/docs/offers/get-company.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/get.md b/documentation/docs/offers/get.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/hide.md b/documentation/docs/offers/hide.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/search.md b/documentation/docs/offers/search.md old mode 100644 new mode 100755 diff --git a/documentation/docusaurus.config.js b/documentation/docusaurus.config.js old mode 100644 new mode 100755 diff --git a/documentation/package-lock.json b/documentation/package-lock.json old mode 100644 new mode 100755 diff --git a/documentation/package.json b/documentation/package.json old mode 100644 new mode 100755 diff --git a/documentation/src/css/custom.css b/documentation/src/css/custom.css old mode 100644 new mode 100755 diff --git a/documentation/src/highlight.js b/documentation/src/highlight.js old mode 100644 new mode 100755 diff --git a/documentation/static/.nojekyll b/documentation/static/.nojekyll old mode 100644 new mode 100755 diff --git a/documentation/static/img/favicon.ico b/documentation/static/img/favicon.ico old mode 100644 new mode 100755 diff --git a/documentation/static/img/logo_2018.svg b/documentation/static/img/logo_2018.svg old mode 100644 new mode 100755 diff --git a/jest.config.js b/jest.config.js old mode 100644 new mode 100755 diff --git a/netlify.toml b/netlify.toml old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 diff --git a/package.json b/package.json old mode 100644 new mode 100755 diff --git a/src/api/APIErrorTypes.js b/src/api/APIErrorTypes.js old mode 100644 new mode 100755 diff --git a/src/api/index.js b/src/api/index.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/auth.js b/src/api/middleware/auth.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/company.js b/src/api/middleware/company.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/errorHandler.js b/src/api/middleware/errorHandler.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/files.js b/src/api/middleware/files.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/offer.js b/src/api/middleware/offer.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/utils.js b/src/api/middleware/utils.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/application.js b/src/api/middleware/validators/application.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/auth.js b/src/api/middleware/validators/auth.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/company.js b/src/api/middleware/validators/company.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/validationReasons.js b/src/api/middleware/validators/validationReasons.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/validatorUtils.js b/src/api/middleware/validators/validatorUtils.js old mode 100644 new mode 100755 diff --git a/src/api/routes/application.js b/src/api/routes/application.js old mode 100644 new mode 100755 diff --git a/src/api/routes/auth.js b/src/api/routes/auth.js old mode 100644 new mode 100755 diff --git a/src/api/routes/company.js b/src/api/routes/company.js old mode 100644 new mode 100755 diff --git a/src/api/routes/offer.js b/src/api/routes/offer.js old mode 100644 new mode 100755 diff --git a/src/api/routes/review.js b/src/api/routes/review.js old mode 100644 new mode 100755 diff --git a/src/config/env.js b/src/config/env.js old mode 100644 new mode 100755 diff --git a/src/config/multer.js b/src/config/multer.js old mode 100644 new mode 100755 diff --git a/src/config/passport.js b/src/config/passport.js old mode 100644 new mode 100755 diff --git a/src/email-templates/accountManagement.js b/src/email-templates/accountManagement.js old mode 100644 new mode 100755 diff --git a/src/email-templates/approval_notification.handlebars b/src/email-templates/approval_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/companyApplicationApproval.js b/src/email-templates/companyApplicationApproval.js old mode 100644 new mode 100755 diff --git a/src/email-templates/companyManagement.js b/src/email-templates/companyManagement.js old mode 100644 new mode 100755 diff --git a/src/email-templates/companyOfferDisabled.js b/src/email-templates/companyOfferDisabled.js old mode 100644 new mode 100755 diff --git a/src/email-templates/company_blocked_notification.handlebars b/src/email-templates/company_blocked_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_deleted_notification.handlebars b/src/email-templates/company_deleted_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_disabled_notification.handlebars b/src/email-templates/company_disabled_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_enabled_notification.handlebars b/src/email-templates/company_enabled_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_unblocked_notification.handlebars b/src/email-templates/company_unblocked_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/layouts/main.handlebars b/src/email-templates/layouts/main.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/new_company_application_admins.handlebars b/src/email-templates/new_company_application_admins.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/new_company_application_company.handlebars b/src/email-templates/new_company_application_company.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/offer_disabled_notification.handlebars b/src/email-templates/offer_disabled_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/rejection_notification.handlebars b/src/email-templates/rejection_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/request_password_recovery.handlebars b/src/email-templates/request_password_recovery.handlebars old mode 100644 new mode 100755 diff --git a/src/index.js b/src/index.js old mode 100644 new mode 100755 diff --git a/src/lib/emailService.js b/src/lib/emailService.js old mode 100644 new mode 100755 diff --git a/src/lib/passwordHashing.js b/src/lib/passwordHashing.js old mode 100644 new mode 100755 diff --git a/src/lib/token.js b/src/lib/token.js old mode 100644 new mode 100755 diff --git a/src/loaders/emailService.js b/src/loaders/emailService.js old mode 100644 new mode 100755 diff --git a/src/loaders/express.js b/src/loaders/express.js old mode 100644 new mode 100755 diff --git a/src/loaders/index.js b/src/loaders/index.js old mode 100644 new mode 100755 diff --git a/src/loaders/mongoose.js b/src/loaders/mongoose.js old mode 100644 new mode 100755 diff --git a/src/loaders/requestEnhancers.js b/src/loaders/requestEnhancers.js old mode 100644 new mode 100755 diff --git a/src/loaders/static.js b/src/loaders/static.js old mode 100644 new mode 100755 diff --git a/src/models/Account.js b/src/models/Account.js old mode 100644 new mode 100755 diff --git a/src/models/Company.js b/src/models/Company.js old mode 100644 new mode 100755 diff --git a/src/models/CompanyApplication.js b/src/models/CompanyApplication.js old mode 100644 new mode 100755 diff --git a/src/models/Offer.js b/src/models/Offer.js old mode 100644 new mode 100755 diff --git a/src/models/Point.js b/src/models/Point.js old mode 100644 new mode 100755 diff --git a/src/models/constants/Account.js b/src/models/constants/Account.js old mode 100644 new mode 100755 diff --git a/src/models/constants/ApplicationStatus.js b/src/models/constants/ApplicationStatus.js old mode 100644 new mode 100755 diff --git a/src/models/constants/Company.js b/src/models/constants/Company.js old mode 100644 new mode 100755 diff --git a/src/models/constants/CompanyApplication.js b/src/models/constants/CompanyApplication.js old mode 100644 new mode 100755 diff --git a/src/models/constants/FieldTypes.js b/src/models/constants/FieldTypes.js old mode 100644 new mode 100755 diff --git a/src/models/constants/JobTypes.js b/src/models/constants/JobTypes.js old mode 100644 new mode 100755 diff --git a/src/models/constants/Offer.js b/src/models/constants/Offer.js old mode 100644 new mode 100755 diff --git a/src/models/constants/TechnologyTypes.js b/src/models/constants/TechnologyTypes.js old mode 100644 new mode 100755 diff --git a/src/models/constants/TimeConstants.js b/src/models/constants/TimeConstants.js old mode 100644 new mode 100755 diff --git a/src/models/modelUtils.js b/src/models/modelUtils.js old mode 100644 new mode 100755 diff --git a/src/services/account.js b/src/services/account.js old mode 100644 new mode 100755 diff --git a/src/services/application.js b/src/services/application.js old mode 100644 new mode 100755 diff --git a/src/services/company.js b/src/services/company.js old mode 100644 new mode 100755 diff --git a/src/services/offer.js b/src/services/offer.js old mode 100644 new mode 100755 diff --git a/src/setupTests.js b/src/setupTests.js old mode 100644 new mode 100755 diff --git a/test/data/logo-niaefeup-10mb.png b/test/data/logo-niaefeup-10mb.png old mode 100644 new mode 100755 diff --git a/test/data/logo-niaefeup.png b/test/data/logo-niaefeup.png old mode 100644 new mode 100755 diff --git a/test/data/not-a-logo.txt b/test/data/not-a-logo.txt old mode 100644 new mode 100755 diff --git a/test/end-to-end/applications/company/:id/approve.js b/test/end-to-end/applications/company/:id/approve.js deleted file mode 100644 index 96a9e088..00000000 --- a/test/end-to-end/applications/company/:id/approve.js +++ /dev/null @@ -1,205 +0,0 @@ -jest.mock("../../../../../src/lib/emailService"); -import { StatusCodes } from "http-status-codes"; -import { ErrorTypes } from "../../../../../src/api/middleware/errorHandler"; -import { APPROVAL_NOTIFICATION } from "../../../../../src/email-templates/companyApplicationApproval"; -import EmailService, { EmailService as EmailServiceClass } from "../../../../../src/lib/emailService"; -import hash from "../../../../../src/lib/passwordHashing"; -import Account from "../../../../../src/models/Account"; -import CompanyApplication, { CompanyApplicationRules } from "../../../../../src/models/CompanyApplication"; -import ApplicationStatus from "../../../../../src/models/constants/ApplicationStatus"; -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); - -import mongoose from "mongoose"; - -const { ObjectId } = mongoose.Types; - -describe("POST /applications/company/:id/approve", () => { - - const test_agent = agent(); - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await CompanyApplication.deleteMany({}); - }); - - beforeEach(async () => { - // default login - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - }); - - describe("ID Validation", () => { - test("Should fail if trying to approve inexistent application", async () => { - - const id = new ObjectId(); - - await test_agent - .post(`/applications/company/${id}/approve`) - .expect(StatusCodes.NOT_FOUND); - - }); - }); - - describe("Without previous applications", () => { - - const pendingApplication1Data = { - email: "pending1@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - const pendingApplication2Data = { - email: "pending2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - let pendingApplication1, pendingApplication2; - - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - - [ - pendingApplication1, - pendingApplication2, - ] = await CompanyApplication.create([ - pendingApplication1Data, - pendingApplication2Data, - ]); - }); - - afterAll(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should approve pending application", async () => { - - const res = await test_agent - .post(`/applications/company/${pendingApplication1._id}/approve`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("email", pendingApplication1Data.email); - expect(res.body).toHaveProperty("companyName", pendingApplication1Data.companyName); - }); - - test("Should send approval email to company email", async () => { - - await test_agent - .post(`/applications/company/${pendingApplication2._id}/approve`) - .expect(StatusCodes.OK); - - const emailOptions = APPROVAL_NOTIFICATION(pendingApplication2.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: pendingApplication2.email, - template: emailOptions.template, - context: emailOptions.context, - }); - }); - }); - - describe("With previous applications", () => { - - const approvedApplicationData = { - email: "approved@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - approvedAt: new Date("2019-11-26"), - rejectReason: null - }; - const rejectedApplicationData = { - email: "rejected@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - rejectedAt: new Date("2019-11-26"), - rejectReason: "test-reason" - }; - - const sameEmail = "some@email.com"; - const sameEmailApplicationData = { - email: sameEmail, - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - let approvedApplication, rejectedApplication, sameEmailApplication; - - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - - [ - approvedApplication, - rejectedApplication, - sameEmailApplication, - ] = await CompanyApplication.create([ - approvedApplicationData, - rejectedApplicationData, - sameEmailApplicationData, - ]); - }); - - afterAll(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should fail if trying to approve already approved application", async () => { - await test_agent - .post(`/applications/company/${approvedApplication._id}/approve`) - .expect(StatusCodes.CONFLICT); - }); - - test("Should fail if trying to approve already rejected application", async () => { - await test_agent - .post(`/applications/company/${rejectedApplication._id}/approve`) - .expect(StatusCodes.CONFLICT); - }); - - test("Should fail if approving application with an existing account with same email, and then rollback", async () => { - await Account.create({ email: sameEmail, password: "passwordHashedButNotReally", isAdmin: true }); - - const res = await test_agent - .post(`/applications/company/${sameEmailApplication._id}/approve`); - - expect(res.status).toBe(StatusCodes.CONFLICT); - expect(res.body.error_code).toBe(ErrorTypes.VALIDATION_ERROR); - expect(res.body.errors).toEqual(expect.arrayContaining( - [ - expect.objectContaining({ - msg: CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg - }) - ] - )); - - const result_application = await CompanyApplication.findById(sameEmailApplication._id); - expect(result_application.state).toBe(ApplicationStatus.PENDING); - }); - }); -}); diff --git a/test/end-to-end/applications/company/:id/reject.js b/test/end-to-end/applications/company/:id/reject.js deleted file mode 100644 index 21b8e6c6..00000000 --- a/test/end-to-end/applications/company/:id/reject.js +++ /dev/null @@ -1,184 +0,0 @@ -jest.mock("../../../../../src/lib/emailService"); -import EmailService, { EmailService as EmailServiceClass } from "../../../../../src/lib/emailService"; -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); -import { StatusCodes } from "http-status-codes"; -import Account from "../../../../../src/models/Account"; -import CompanyApplication from "../../../../../src/models/CompanyApplication"; -import hash from "../../../../../src/lib/passwordHashing"; -import { REJECTION_NOTIFICATION } from "../../../../../src/email-templates/companyApplicationApproval"; - -import mongoose from "mongoose"; - -const { ObjectId } = mongoose.Types; - -describe("POST /applications/company/:id/reject", () => { - - const test_agent = agent(); - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await CompanyApplication.deleteMany({}); - }); - - beforeEach(async () => { - // default login - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - }); - - describe("ID Validation", () => { - test("Should fail if trying to reject inexistent application", async () => { - - const id = new ObjectId(); - - await test_agent - .post(`/applications/company/${id}/reject`) - .send({ rejectReason: "Some reason which is valid" }) - .expect(StatusCodes.NOT_FOUND); - }); - }); - - describe("Without previous applications", () => { - - const pendingApplication1Data = { - email: "pending1@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - const pendingApplication2Data = { - email: "pending2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - let pendingApplication1, pendingApplication2; - - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - - [ - pendingApplication1, - pendingApplication2, - ] = await CompanyApplication.create([ - pendingApplication1Data, - pendingApplication2Data, - ]); - }); - - afterAll(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should fail if no rejectReason provided", async () => { - const res = await test_agent - .post(`/applications/company/${pendingApplication1._id}/reject`); - - expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); - expect(res.body.errors[0]).toStrictEqual({ location: "body", msg: "required", param: "rejectReason" }); - }); - - test("Should reject pending application", async () => { - - const res = await test_agent - .post(`/applications/company/${pendingApplication1._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("email", pendingApplication1Data.email); - expect(res.body).toHaveProperty("companyName", pendingApplication1Data.companyName); - }); - - test("Should send rejection email to company email", async () => { - - await test_agent - .post(`/applications/company/${pendingApplication2._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }) - .expect(StatusCodes.OK); - - const emailOptions = REJECTION_NOTIFICATION(pendingApplication2.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: pendingApplication2.email, - template: emailOptions.template, - context: emailOptions.context, - }); - }); - }); - - describe("With previous applications", () => { - - const approvedApplicationData = { - email: "approved@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - approvedAt: new Date("2019-11-26"), - rejectReason: null - }; - const rejectedApplicationData = { - email: "rejected@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - rejectedAt: new Date("2019-11-26"), - rejectReason: "test-reason" - }; - - let approvedApplication, rejectedApplication; - - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - - [ - approvedApplication, - rejectedApplication, - ] = await CompanyApplication.create([ - approvedApplicationData, - rejectedApplicationData, - ]); - }); - - afterAll(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should fail if trying to reject already approved application", async () => { - await test_agent - .post(`/applications/company/${approvedApplication._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }) - .expect(StatusCodes.CONFLICT); - }); - - test("Should fail if trying to reject already rejected application", async () => { - await test_agent - .post(`/applications/company/${rejectedApplication._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }) - .expect(StatusCodes.CONFLICT); - }); - }); -}); diff --git a/test/end-to-end/applications/company/search.js b/test/end-to-end/applications/company/search.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/apply/company.js b/test/end-to-end/apply/company.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/auth/login.js b/test/end-to-end/auth/login.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/auth/me.js b/test/end-to-end/auth/me.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/auth/recover/:token/confirm.js b/test/end-to-end/auth/recover/:token/confirm.js deleted file mode 100644 index 236d3340..00000000 --- a/test/end-to-end/auth/recover/:token/confirm.js +++ /dev/null @@ -1,188 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import Account from "../../../../../src/models/Account"; -import ValidatorTester from "../../../../utils/ValidatorTester"; -import ValidationReasons from "../../../../../src/api/middleware/validators/validationReasons"; -import hash from "../../../../../src/lib/passwordHashing"; -import AccountConstants, { RECOVERY_LINK_EXPIRATION } from "../../../../../src/models/constants/Account"; -import * as token from "../../../../../src/lib/token"; -import env from "../../../../../src/config/env"; -import { SECOND_IN_MS } from "../../../../../src/models/constants/TimeConstants"; -import { generateToken } from "../../../../../src/lib/token"; - -const generateTokenSpy = jest.spyOn(token, "generateToken"); -jest.spyOn(token, "verifyAndDecodeToken"); - -describe("GET /auth/recover/:token/confirm", () => { - - const test_account = { - email: "recover_email@gmail.com", - password: "password123", - }; - - beforeEach(async () => { - await Account.deleteMany({ email: test_account.email }); - - await Account.create({ - email: test_account.email, - password: await hash(test_account.password), - isAdmin: true, - }); - - jest.clearAllMocks(); - }); - - test("should fail if invalid token", async () => { - const res = await request() - .get("/auth/recover/token/confirm"); - - expect(res.status).toBe(StatusCodes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - }); - - test("should accept if valid token", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(StatusCodes.OK); - - res = await request() - .get(`/auth/recover/${generatedToken}/confirm`); - - expect(res.status).toBe(StatusCodes.OK); - }); - - test("should fail if valid token expired", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(StatusCodes.OK); - - - const realTime = Date.now; - const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); - Date.now = () => mockDate; - - res = await request() - .get(`/auth/recover/${generatedToken}/confirm`); - - expect(res.status).toBe(StatusCodes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - - Date.now = realTime; - }); -}); - -describe("POST /auth/recover/:token/confirm", () => { - - const test_account = { - email: "recover_email@gmail.com", - password: "password123", - }; - - const newPassword = "new_password_123"; - - beforeEach(async () => { - await Account.deleteMany({ email: test_account.email }); - - await Account.create({ - email: test_account.email, - password: await hash(test_account.password), - isAdmin: true, - }); - - jest.clearAllMocks(); - }); - - afterAll(async () => { - await Account.deleteMany({}); - }); - - describe("Input Validation", () => { - describe("password", () => { - let generatedToken; - - beforeAll(async () => { - await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - generatedToken = generateTokenSpy.mock.results[0].value; - }); - - const EndpointValidatorTester = - ValidatorTester((params) => request().post(`/auth/recover/${generatedToken}/confirm`).send(params)); - const BodyValidatorTester = EndpointValidatorTester("body"); - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); - FieldValidatorTester.hasNumber(); - }); - }); - - test("should fail if invalid token", async () => { - const res = await request() - .post("/auth/recover/token/confirm") - .send({ password: newPassword }); - - expect(res.status).toBe(StatusCodes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - }); - - test("should accept if valid token", async () => { - const generatedToken = generateToken({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(StatusCodes.OK); - }); - - test("should fail if valid token expired", async () => { - - const generatedToken = generateToken({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const realTime = Date.now; - const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); - Date.now = () => mockDate; - - const res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(StatusCodes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - - Date.now = realTime; - }); - - test("should succeed to complete the whole password recovery process", async () => { - const generatedToken = generateToken({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }) - .expect(StatusCodes.OK); - - await request() - .post("/auth/login") - .send({ email: test_account.email, password: newPassword }) - .expect(StatusCodes.OK); - - }); -}); diff --git a/test/end-to-end/auth/recover/request.js b/test/end-to-end/auth/recover/request.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/auth/register.js b/test/end-to-end/auth/register.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/company/:id/block.js b/test/end-to-end/company/:id/block.js deleted file mode 100644 index 99023a34..00000000 --- a/test/end-to-end/company/:id/block.js +++ /dev/null @@ -1,406 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import hash from "../../../../src/lib/passwordHashing"; -import Account from "../../../../src/models/Account"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import OfferConstants from "../../../../src/models/constants/Offer"; -import { COMPANY_BLOCKED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; -import withGodToken from "../../../utils/GodToken"; -import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; - -jest.mock("../../../../src/lib/emailService"); -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); - -describe("PUT /company/block", () => { - - const test_agent = agent(); - - beforeAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - }); - - afterAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - }); - - describe("ID Validation", () => { - const adminReason = "An admin reason!"; - - test("should fail if not a valid id", async () => { - const res = await test_agent - .put("/company/123/block") - .send(withGodToken({ adminReason })) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.OBJECT_ID - }) - ])); - }); - - test("should fail if company does not exist", async () => { - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/block`) - .send(withGodToken({ adminReason })) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND(id) - }) - ])); - }); - }); - - describe("Without auth", () => { - - let test_company; - const test_company_data = { - name: "Company Ltd", - hasFinishedRegistration: true, - }; - - const test_user = { - email: "no-auth@email.com", - password: "password123" - }; - - beforeAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - - test_company = await Company.create(test_company_data); - - // Need to create the account because of the mail notification - await Account.create({ - email: test_user.email, - password: await hash(test_user.password), - company: test_company._id - }); - }); - - afterAll(async () => { - await Company.deleteMany({ _id: test_company._id }); - await Account.deleteMany({ email: test_user.email }); - }); - - test("should fail if not logged in", async () => { - const res = await test_agent - .put(`/company/${test_company.id}/block`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - }); - - describe("With auth", () => { - - let test_company_1, test_company_2, test_company_mail; - const company_data = { - name: "Company Ltd", - hasFinishedRegistration: true, - }; - - const test_user_1 = { - email: "company1@email.com", - password: "password123" - }; - - const test_user_2 = { - email: "company2@email.com", - password: "password123" - }; - - const test_user_mail = { - email: "company-mail@email.com", - password: "password123" - }; - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - const adminReason = "An admin reason!"; - - beforeAll(async () => { - [test_company_1, test_company_2, test_company_mail] = await Company.create([ - company_data, - company_data, - company_data - ]); - - await Account.create([ - { - email: test_user_1.email, - password: await hash(test_user_1.password), - company: test_company_1._id - }, { - email: test_user_2.email, - password: await hash(test_user_2.password), - company: test_company_2._id - }, { - email: test_user_mail.email, - password: await hash(test_user_mail.password), - company: test_company_mail._id - }, { - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - } - ]); - }); - - afterAll(async () => { - await Company.deleteMany({ _id: { $in: [test_company_1._id, test_company_2._id, test_company_mail._id] } }); - await Account.deleteMany({ email: { $in: [test_user_1.email, test_user_2.email, test_user_mail.email] } }); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - test("should fail if logged in as company", async () => { - await test_agent - .post("/auth/login") - .send(test_user_1) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - - test("should fail if admin reason not provided", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "adminReason", - msg: ValidationReasons.REQUIRED - }) - ])); - }); - - test("should allow if logged in as admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .send({ adminReason }) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - isBlocked: true, - adminReason - })); - }); - - test("should allow with god token", async () => { - const res = await test_agent - .put(`/company/${test_company_2.id}/block`) - .send(withGodToken({ adminReason })) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - isBlocked: true, - adminReason - })); - }); - - test("should send an email to the company user when it is blocked", async () => { - await test_agent - .put(`/company/${test_company_mail._id}/block`) - .send(withGodToken({ adminReason })) - .expect(StatusCodes.OK); - - const emailOptions = COMPANY_BLOCKED_NOTIFICATION( - test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - - describe("With offers", () => { - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - - let test_company_with_offers; - const test_company_with_offers_data = { - name: "Company With Offers", - logo: "https://www.google.com/image.jpg", - hasFinishedRegistration: true, - }; - - const test_user_with_offers = { - email: "offers@email.com", - password: "password123" - }; - - beforeAll(async () => { - await Offer.deleteMany({}); - - test_company_with_offers = await Company.create(test_company_with_offers_data); - - await Account.create({ - email: test_user_with_offers.email, - password: await hash(test_user_with_offers.password), - company: test_company_with_offers._id - }); - }); - - afterAll(async () => { - await Offer.deleteMany({}); - }); - - afterEach(async () => { - await test_agent - .put(`/company/${test_company_with_offers.id}/unblock`) - .send(withGodToken({})) - .expect(StatusCodes.OK); - }); - - describe("With active offers", () => { - - let test_active_offers; - - beforeAll(async () => { - test_active_offers = await Offer.create(Array(3).fill(generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - owner: test_company_with_offers._id, - ownerName: test_company_with_offers.name, - ownerLogo: test_company_with_offers.logo - }))); - }); - - afterAll(async () => { - await Offer.deleteMany({ _id: { $in: test_active_offers.map((offer) => offer._id) } }); - }); - - test("should block active offers", async () => { - - const res = await test_agent - .put(`/company/${test_company_with_offers.id}/block`) - .send(withGodToken({ adminReason })) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - isBlocked: true, - adminReason - })); - - const offers = await Offer.find({ _id: { $in: test_active_offers.map((offer) => offer._id) } }); - - expect(offers).toHaveLength(test_active_offers.length); - expect(offers).not.toEqual(expect.arrayContaining([ - expect.objectContaining({ - isHidden: false, // we can check on just this attribute since both are set at the same time - }) - ])); - }); - }); - - describe("With hidden offers", () => { - - let test_hidden_offers; - - beforeAll(async () => { - test_hidden_offers = await Offer.create(Array(3).fill(generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - owner: test_company_with_offers._id, - ownerName: test_company_with_offers.name, - ownerLogo: test_company_with_offers.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK - }))); - }); - - afterAll(async () => { - await Offer.deleteMany({ _id: { $in: test_hidden_offers.map((offer) => offer._id) } }); - }); - - test("should not override offers already hidden", async () => { - - const res = await test_agent - .put(`/company/${test_company_with_offers.id}/block`) - .send(withGodToken({ adminReason })) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - isBlocked: true, - adminReason - })); - - const offers = await Offer.find({ _id: { $in: test_hidden_offers.map((offer) => offer._id) } }); - - expect(offers).toHaveLength(test_hidden_offers.length); - expect(offers).not.toEqual(expect.arrayContaining([ - expect.objectContaining({ - isHidden: false, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED - }) - ])); - }); - }); - }); - }); -}); diff --git a/test/end-to-end/company/:id/delete.js b/test/end-to-end/company/:id/delete.js deleted file mode 100644 index b45a4975..00000000 --- a/test/end-to-end/company/:id/delete.js +++ /dev/null @@ -1,313 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import { COMPANY_DELETED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; -import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; -import hash from "../../../../src/lib/passwordHashing"; -import Account from "../../../../src/models/Account"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import withGodToken from "../../../utils/GodToken"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; -jest.mock("../../../../src/lib/emailService"); -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); - -describe("POST /company/delete", () => { - - const test_agent = agent(); - - beforeAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - await Offer.deleteMany({}); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - - const res = await test_agent - .post("/company/123/delete") - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "param": "companyId", - "msg": ValidationReasons.OBJECT_ID, - }) - ])); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .post(`/company/${id}/delete`) - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "param": "companyId", - "msg": ValidationReasons.COMPANY_NOT_FOUND(id), - }) - ])); - }); - }); - - describe("Without auth", () => { - - let test_company; - - const companyData = { - name: "Test Company", - hasFinishedRegistration: true - }; - - beforeAll(async () => { - test_company = await Company.create(companyData); - }); - - afterAll(async () => { - await Company.deleteMany({ _id: test_company._id }); - }); - - test("should fail to delete company if not logged", async () => { - - const res = await test_agent - .post(`/company/${test_company._id}/delete`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - { // use an object literal since we are expecting an exact match - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS, - } - ])); - }); - }); - - describe("With auth", () => { - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - let test_company_1, test_company_2, test_company_offers, test_company_mail; - - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - const test_user_company_offers = { - email: "offers@email.com", - password: "password123", - }; - const test_user_company_mail = { - email: "email@email.com", - password: "password123", - }; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeAll(async () => { - [test_company_1, test_company_2, test_company_offers, test_company_mail] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, - { - name: "test-company-2", - hasFinishedRegistration: true, - }, - { - name: "test-company-offers", - hasFinishedRegistration: true, - logo: "https://test.com/logo.png", - }, - { - name: "test-company-mail", - hasFinishedRegistration: true, - } - ]); - - await Account.create([ - { - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }, - { - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }, - { - email: test_user_company_offers.email, - password: await hash(test_user_company_offers.password), - company: test_company_offers._id - }, - { - email: test_user_company_mail.email, - password: await hash(test_user_company_mail.password), - company: test_company_mail._id - } - ]); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - - const offer = generateTestOffer({ - owner: test_company_offers._id, - ownerName: test_company_offers.name, - ownerLogo: test_company_offers.logo, - }); - - await Offer.create([offer, offer]); - }); - - afterAll(async () => { - await Company.deleteMany({ _id: test_company_1._id }); - await Company.deleteMany({ _id: test_company_2._id }); - await Company.deleteMany({ _id: test_company_offers._id }); - await Company.deleteMany({ _id: test_company_mail._id }); - await Account.deleteMany({ email: test_user_company_1.email }); - await Account.deleteMany({ email: test_user_company_2.email }); - await Account.deleteMany({ email: test_user_company_offers.email }); - await Account.deleteMany({ email: test_user_company_mail.email }); - await Account.deleteMany({ email: test_user_admin.email }); - await Offer.deleteMany({}); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - test("should fail to delete company if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(StatusCodes.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - { - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS, - } - ])); - }); - - test("should fail to delete company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - { - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS, - } - ])); - }); - - test("Should delete company if god token is sent", async () => { - - await test_agent - .post(`/company/${test_company_1._id}/delete`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(await Company.exists({ _id: test_company_1._id })).toBeNull(); - expect(await Account.exists({ company: test_company_1._id })).toBeNull(); - }); - - test("Should delete company if logged as the same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(StatusCodes.OK); - - await test_agent - .post(`/company/${test_company_2._id}/delete`) - .expect(StatusCodes.OK); - - expect(await Company.exists({ _id: test_company_2._id })).toBeNull(); - expect(await Account.exists({ company: test_company_2._id })).toBeNull(); - }); - - test("Should delete company's offers when it is deleted", async () => { - expect(await Offer.exists({ owner: test_company_offers._id })).not.toBeNull(); - - await test_agent - .post(`/company/${test_company_offers._id}/delete`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(await Company.exists({ _id: test_company_offers._id })).toBeNull(); - expect(await Account.exists({ company: test_company_offers._id })).toBeNull(); - expect(await Offer.exists({ owner: test_company_offers._id })).toBeNull(); - }); - - test("should send an email to the company user when it is deleted", async () => { - await test_agent - .post(`/company/${test_company_mail._id}/delete`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - const emailOptions = COMPANY_DELETED_NOTIFICATION( - test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_company_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); -}); diff --git a/test/end-to-end/company/:id/disable.js b/test/end-to-end/company/:id/disable.js deleted file mode 100644 index 0ce61154..00000000 --- a/test/end-to-end/company/:id/disable.js +++ /dev/null @@ -1,330 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import { COMPANY_DISABLED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; -import hash from "../../../../src/lib/passwordHashing"; -import Account from "../../../../src/models/Account"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import OfferConstants from "../../../../src/models/constants/Offer"; -import withGodToken from "../../../utils/GodToken"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; -import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; - -jest.mock("../../../../src/lib/emailService"); -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); - -describe("PUT /company/disable", () => { - - const test_agent = agent(); - - beforeAll(async () => { - await Company.deleteMany({}); - await Offer.deleteMany({}); - await Account.deleteMany({}); - }); - - afterAll(async () => { - await Company.deleteMany({}); - await Offer.deleteMany({}); - await Account.deleteMany({}); - }); - - describe("ID Validation", () => { - test("Should fail if id is not a valid ObjectID", async () => { - const id = "123"; - const res = await test_agent - .put(`/company/${id}/disable`) - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "location": "params", - "msg": ValidationReasons.OBJECT_ID, - "param": "companyId", - "value": id - }) - ])); - }); - - test("Should fail if id is not a valid company", async () => { - const id = "111111111111111111111111"; - - const res = await test_agent - .put(`/company/${id}/disable`) - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "location": "params", - "msg": ValidationReasons.COMPANY_NOT_FOUND(id), - "param": "companyId", - "value": id - }) - ])); - }); - }); - - describe("Without auth", () => { - - let company; - const company_data = { - name: "test-company-no-auth", - hasFinishedRegistration: true - }; - - beforeAll(async () => { - company = await Company.create(company_data); - }); - - afterAll(async () => { - await Company.deleteMany({ name: company_data.name }); - }); - - test("Should not disable company if not authenticated", async () => { - const res = await test_agent - .put(`/company/${company._id}/disable`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - }); - - describe("With auth", () => { - - let test_company_1, test_company_2, test_company_mail; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - - const test_user_company_mail = { - email: "company_mail@email.com", - password: "password123", - }; - - beforeAll(async () => { - [test_company_1, test_company_2, test_company_mail] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true - }, { - name: "test-company-main", - hasFinishedRegistration: true - } - ]); - - await Account.create([ - { - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }, { - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }, { - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }, { - email: test_user_company_mail.email, - password: await hash(test_user_company_mail.password), - company: test_company_mail._id - } - ]); - }); - - afterAll(async () => { - await Company.deleteMany({ name: { $in: [test_company_1._id, test_company_2._id] } }); - await Account.deleteMany({ - email: { - $in: [ - test_user_admin.email, - test_user_company_1.email, - test_user_company_2.email, - test_user_company_mail.email - ] - } - }); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - test("should fail to disable company if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS - }) - ])); - }); - - test("should fail to disable company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - - test("Should disable company if god token is sent", async () => { - - const res = await test_agent - .put(`/company/${test_company_2._id}/disable`) - .send(withGodToken()); - - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.isDisabled).toBe(true); - }); - - test("Should disable company if logged as same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.isDisabled).toBe(true); - }); - - describe("With offers", () => { - const assertOfferList = (offers, expectedIsHidden, expectedHiddenReason) => { - expect(offers).not.toEqual(expect.arrayContaining([ - expect.objectContaining({ - isHidden: !expectedIsHidden, - hiddenReason: !expectedHiddenReason - }) - ])); - }; - - let company_with_offers; - const company_with_offers_data = { - name: "test-company-with-offers", - logo: "http://awebsite.com/alogo.jpg", - hasFinishedRegistration: true - }; - const account_with_offers_data = { - email: "withOffers@mail.com", - password: "password123", - }; - - beforeAll(async () => { - company_with_offers = await Company.create(company_with_offers_data); - await Account.create({ - email: account_with_offers_data.email, - password: await hash(account_with_offers_data.password), - company: company_with_offers._id - }); - - const offer = { - title: "Test Offer", - publishDate: new Date(Date.now()), - publishEndDate: new Date(Date.now() + (DAY_TO_MS)), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 2, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - requirements: ["The candidate must be tested", "Fluent in testJS"], - owner: company_with_offers._id, - ownerName: company_with_offers.name, - ownerLogo: company_with_offers.logo, - }; - - await Offer.create([offer, offer]); - }); - - afterAll(async () => { - await Account.deleteMany({ email: account_with_offers_data.email }); - await Company.deleteMany({ name: company_with_offers_data.name }); - await Offer.deleteMany({ owner: company_with_offers._id }); - }); - - test("should change offers' 'isHidden' on company disable", async () => { - - const offersBefore = await Offer.find({ owner: company_with_offers._id }); - - assertOfferList(offersBefore, false, undefined); - - const res = await test_agent - .put(`/company/${company_with_offers._id}/disable`) - .send(withGodToken()); - - expect(res.status).toBe(StatusCodes.OK); - expect(res.body.isDisabled).toBe(true); - - const offersAfter = await Offer.find({ owner: company_with_offers._id }); - - assertOfferList(offersAfter, true, OfferConstants.HiddenOfferReasons.COMPANY_DISABLED); - }); - }); - - test("should send an email to the company user when it is disabled", async () => { - await test_agent - .put(`/company/${test_company_mail._id}/disable`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - const emailOptions = COMPANY_DISABLED_NOTIFICATION( - test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_company_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); -}); diff --git a/test/end-to-end/company/:id/edit.js b/test/end-to-end/company/:id/edit.js deleted file mode 100644 index 52bc5177..00000000 --- a/test/end-to-end/company/:id/edit.js +++ /dev/null @@ -1,609 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { MAX_FILE_SIZE_MB } from "../../../../src/api/middleware/utils"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import hash from "../../../../src/lib/passwordHashing"; -import Account from "../../../../src/models/Account"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import CompanyConstants from "../../../../src/models/constants/Company"; -import withGodToken from "../../../utils/GodToken"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; -import ValidatorTester from "../../../utils/ValidatorTester"; - -describe("PUT /company/edit", () => { - - const generateTestCompany = (params) => ({ - name: "Big Company", - bio: "Big Company Bio", - logo: "http://awebsite.com/alogo.jpg", - contacts: ["112", "122"], - hasFinishedRegistration: true, - ...params, - }); - - const test_agent = agent(); - - const edit_payload = { - name: "Changed name", - bio: "Changed bio", - logo: "test/data/logo-niaefeup.png", - contacts: ["123", "456"], - }; - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - await Offer.deleteMany({}); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true, - }); - }); - - afterAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("ID Validation", () => { - test("Should fail if id is not a valid ObjectID", async () => { - const id = "123"; - const res = await test_agent - .put(`/company/${id}/edit`) - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "location": "params", - "msg": ValidationReasons.OBJECT_ID, - "param": "companyId", - "value": id - }) - ])); - }); - - test("Should fail if id is not a valid company", async () => { - const id = "111111111111111111111111"; - - const res = await test_agent - .put(`/company/${id}/edit`) - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "location": "params", - "msg": ValidationReasons.COMPANY_NOT_FOUND(id), - "param": "companyId", - "value": id - }) - ])); - }); - }); - - describe("Field Validation", () => { - - const company_data = { - name: "Test Company", - logo: "http://awebsite.com/alogo.jpg", - }; - - let company; - - beforeAll(async () => { - company = await Company.create(company_data); - }); - - afterAll(async () => { - await Company.deleteMany({ name: company.name }); - }); - - const EndpointValidatorTester = ValidatorTester( - (params) => request().put(`/company/${company._id}/edit`).send(withGodToken(params)) - ); - const BodyValidatorTester = EndpointValidatorTester("body"); - - describe("name", () => { - const FieldValidatorTester = BodyValidatorTester("name"); - - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMaxLength(CompanyConstants.companyName.max_length); - FieldValidatorTester.hasMinLength(CompanyConstants.companyName.min_length); - }); - - describe("bio", () => { - const FieldValidatorTester = BodyValidatorTester("bio"); - - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMaxLength(CompanyConstants.bio.max_length); - }); - - describe("contacts", () => { - const FieldValidatorTester = BodyValidatorTester("contacts"); - - FieldValidatorTester.mustBeArray(); - // FieldValidatorTester.mustHaveAtLeast(CompanyConstants.contacts.min_length); - FieldValidatorTester.mustBeArrayBetween(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length); - }); - - describe("logo", () => { - // TODO: Add tests for logo when the route has multer middleware to handle file uploads - }); - }); - - describe("Without auth", () => { - - const company_data = generateTestCompany({ - name: "Test Company", - }); - let test_company; - - beforeAll(async () => { - test_company = await Company.create(company_data); - }); - - afterAll(async () => { - await Company.deleteMany({ name: test_company.name }); - }); - - test("Should fail if not logged in", async () => { - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send({ - bio: edit_payload.bio, - contacts: edit_payload.contacts, - }) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - }); - - describe("With auth", () => { - - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - - const test_company_1_data = generateTestCompany({ - name: "Test Company 1", - }); - const test_company_2_data = generateTestCompany({ - name: "Test Company 2", - }); - const test_company_god_data = generateTestCompany({ - name: "Test Company God", - }); - - let test_company_1, test_company_2, test_company_god; - - beforeAll(async () => { - - [ - test_company_1, - test_company_2, - test_company_god, - ] = await Company.create([ - test_company_1_data, - test_company_2_data, - test_company_god_data, - ]); - - await Account.create([ - { - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id, - }, - { - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id, - }, - ]); - }); - - afterAll(async () => { - await Company.deleteMany({ - _id: { - $in: [ - test_company_god._id, - test_company_1._id, - test_company_2._id, - ] - } - }); - await Account.deleteMany({ - email: { - $in: [ - test_user_admin.email, - test_user_company_1.email, - test_user_company_2.email, - ] - } - }); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - test("Should fail if logged in as different user", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_2._id}/edit`) - .send({ - name: edit_payload.name, - }) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS - }) - ])); - }); - - test("Should succeed if god", async () => { - const res = await test_agent - .put(`/company/${test_company_god._id}/edit`) - .send(withGodToken({ - name: edit_payload.name, - bio: edit_payload.bio, - })) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - _id: test_company_god._id.toString(), - name: edit_payload.name, - bio: edit_payload.bio, - })); - }); - - test("Should succeed if admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/edit`) - .send({ - name: edit_payload.name - }) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - name: edit_payload.name, - })); - }); - - test("Should succeed if same company", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_2._id}/edit`) - .send({ - name: edit_payload.name, - }) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - name: edit_payload.name, - })); - }); - - describe("Blocked company", () => { - - const test_user_company_blocked = { - email: "blocked@email.com", - password: "password123", - }; - - const test_company_blocked_data = generateTestCompany({ - name: "Test Company God", - isBlocked: true - }); - let test_company_blocked; - - beforeAll(async () => { - test_company_blocked = await Company.create(test_company_blocked_data); - - await Account.create({ - email: test_user_company_blocked.email, - password: await hash(test_user_company_blocked.password), - company: test_company_blocked._id, - }); - }); - - afterAll(async () => { - await Company.deleteMany({ - _id: test_company_blocked._id - }); - await Account.deleteMany({ email: test_user_company_blocked.email }); - }); - - test("Should fail if company is blocked (god)", async () => { - const res = await test_agent - .put(`/company/${test_company_blocked._id}/edit`) - .send(withGodToken({ - name: "Changing Blocked Company", - })) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.COMPANY_BLOCKED - }) - ])); - }); - - test("Should fail if company is blocked (user)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_blocked) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_blocked._id}/edit`) - .send({ - name: "Changing Blocked Company", - }) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.COMPANY_BLOCKED - }) - ])); - }); - }); - - describe("Disabled company", () => { - - const test_user_company_disabled = { - email: "disabled@email.com", - password: "password123", - }; - - const test_company_disabled_data = generateTestCompany({ - name: "Test Company God", - isDisabled: true - }); - let test_company_disabled; - - beforeAll(async () => { - test_company_disabled = await Company.create(test_company_disabled_data); - - await Account.create({ - email: test_user_company_disabled.email, - password: await hash(test_user_company_disabled.password), - company: test_company_disabled._id, - }); - }); - - afterAll(async () => { - await Company.deleteMany({ - _id: test_company_disabled._id - }); - await Account.deleteMany({ email: test_user_company_disabled.email }); - }); - - test("Should fail if company is disabled (god)", async () => { - const res = await test_agent - .put(`/company/${test_company_disabled._id}/edit`) - .send(withGodToken({ - name: "Changing Disabled Company", - })) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.COMPANY_DISABLED - }) - ])); - }); - - test("Should fail if company is disabled (user)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_disabled) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_disabled._id}/edit`) - .send({ - bio: "As user", - }) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "msg": ValidationReasons.COMPANY_DISABLED - }) - ])); - }); - }); - - describe("With Offers", () => { - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - const test_user_company_with_offers = { - email: "offers@email.com", - password: "password123", - }; - - const test_company_with_offers_data = generateTestCompany({ - name: "Test Company God", - logo: "https://test.com/logo.png", - }); - let test_company_with_offers; - let offer; - - beforeAll(async () => { - test_company_with_offers = await Company.create(test_company_with_offers_data); - - await Account.create({ - email: test_user_company_with_offers.email, - password: await hash(test_user_company_with_offers.password), - company: test_company_with_offers._id, - }); - - offer = await Offer.create( - generateTestOffer({ - owner: test_company_with_offers._id, - ownerName: test_company_with_offers.name, - ownerLogo: test_company_with_offers.logo, - }) - ); - }); - - afterAll(async () => { - await Company.deleteMany({ - _id: test_company_with_offers._id - }); - await Account.deleteMany({ email: test_user_company_with_offers.email }); - await Offer.deleteMany({ owner: test_company_with_offers._id }); - }); - - test("Offer should be updated", async () => { - - let test_offer = await Offer.findById(offer._id); - - expect(test_offer).not.toHaveProperty("ownerName", edit_payload.name); - expect(test_offer).not.toHaveProperty("contacts", edit_payload.contacts); - - await test_agent - .post("/auth/login") - .send(test_user_company_with_offers) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_with_offers._id}/edit`) - .send({ - name: edit_payload.name, - contacts: edit_payload.contacts, - }) - .expect(StatusCodes.OK); - - expect(res.body).toEqual(expect.objectContaining({ - name: edit_payload.name, - contacts: edit_payload.contacts, - })); - - test_offer = await Offer.findById(offer._id); - - expect(test_offer).toHaveProperty("ownerName", edit_payload.name); - expect(test_offer).toHaveProperty("contacts", edit_payload.contacts); - }); - }); - - describe("Updating company logo", () => { - - let company_with_logo; - const company_with_logo_data = generateTestCompany({ - name: "Test Company With Logo", - logo: "https://test.com/logo.png", - }); - - beforeAll(async () => { - company_with_logo = await Company.create(company_with_logo_data); - }); - - afterAll(async () => { - await Company.deleteMany({ _id: company_with_logo._id }); - }); - - beforeEach(async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - test("Should fail if not an image", async () => { - const res = await test_agent - .put(`/company/${company_with_logo._id}/edit`) - .attach("logo", "test/data/not-a-logo.txt") - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.IMAGE_FORMAT, - "param": "logo" - }); - }); - - test("Should fail if image is too big", async () => { - const res = await test_agent - .put(`/company/${company_with_logo._id}/edit`) - .attach("logo", "test/data/logo-niaefeup-10mb.png") - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), - "param": "logo" - }); - }); - - test("Should succeed if image is valid", async () => { - const res = await test_agent - .put(`/company/${company_with_logo._id}/edit`) - .attach("logo", edit_payload.logo) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("logo"); - }); - }); - }); -}); diff --git a/test/end-to-end/company/:id/enable.js b/test/end-to-end/company/:id/enable.js deleted file mode 100644 index 1c61b2b8..00000000 --- a/test/end-to-end/company/:id/enable.js +++ /dev/null @@ -1,369 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import OfferConstants from "../../../../src/models/constants/Offer"; -import Account from "../../../../src/models/Account"; -import hash from "../../../../src/lib/passwordHashing"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; -import withGodToken from "../../../utils/GodToken"; -import { COMPANY_ENABLED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; -import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; - -jest.mock("../../../../src/lib/emailService"); -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); - -describe("PUT /company/enable", () => { - - const test_agent = agent(); - - beforeAll(async () => { - await Company.deleteMany({}); - await Offer.deleteMany({}); - await Account.deleteMany({}); - }); - - afterAll(async () => { - await Company.deleteMany({}); - await Offer.deleteMany({}); - await Account.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - const res = await test_agent - .put("/company/123/enable") - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.OBJECT_ID - }) - ])); - }); - - test("Should fail if company does not exist", async () => { - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/enable`) - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND(id) - }) - ])); - }); - }); - - describe("Without auth", () => { - - let disabled_company; - const disabled_company_data = { - name: "disabled-company", - isDisabled: true, - hasFinishedRegistration: true - }; - - beforeAll(async () => { - disabled_company = await Company.create(disabled_company_data); - }); - - afterAll(async () => { - await Company.deleteMany({ name: disabled_company_data.name }); - }); - - test("should fail to enable if not logged", async () => { - - const res = await test_agent - .put(`/company/${disabled_company._id}/enable`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - }); - - describe("With auth", () => { - - let disabled_test_company_1, disabled_test_company_2, disabled_test_company_3, disabled_test_company_mail; - - const disabled_test_company_1_data = { - name: "disabled-test-company-1", - isDisabled: true, - hasFinishedRegistration: true - }; - const disabled_test_company_2_data = { - name: "disabled-test-company-2", - isDisabled: true, - hasFinishedRegistration: true - }; - const disabled_test_company_3_data = { - name: "disabled-test-company-3", - isDisabled: true, - hasFinishedRegistration: true - }; - const disabled_test_company_mail_data = { - name: "disabled-test-company-mail", - isDisabled: true, - hasFinishedRegistration: true - }; - - const disabled_account_1 = { - email: "disabled1@email.com", - password: "password123" - }; - const disabled_account_2 = { - email: "disabled2@email.com", - password: "password123" - }; - const disabled_account_3 = { - email: "disabled3@email.com", - password: "password123" - }; - const disabled_account_email = { - email: "disabled.mail@email.com", - password: "password123" - }; - const test_user_admin = { - email: "admin@email.com", - password: "password123" - }; - - beforeAll(async () => { - [ - disabled_test_company_1, - disabled_test_company_2, - disabled_test_company_3, - disabled_test_company_mail - ] = await Company.create([ - disabled_test_company_1_data, - disabled_test_company_2_data, - disabled_test_company_3_data, - disabled_test_company_mail_data - ]); - - await Account.create([ - { - email: disabled_account_1.email, - password: await hash(disabled_account_1.password), - company: disabled_test_company_1._id - }, - { - email: disabled_account_2.email, - password: await hash(disabled_account_2.password), - company: disabled_test_company_2._id - }, - { - email: disabled_account_3.email, - password: await hash(disabled_account_3.password), - company: disabled_test_company_3._id - }, - { - email: disabled_account_email.email, - password: await hash(disabled_account_email.password), - company: disabled_test_company_mail._id - }, - { - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true, - } - ]); - }); - - afterAll(async () => { - await Company.deleteMany({ - _id: { - $in: [ - disabled_account_1._id, - disabled_account_2._id, - disabled_account_3._id, - disabled_account_email._id - ] - } - }); - await Account.deleteMany({ - email: { - $in: [ - disabled_account_1.email, - disabled_account_2.email, - disabled_account_3.email, - disabled_account_email.email, - test_user_admin.email - ] - } - }); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - test("should fail to enable if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(disabled_account_2) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS - }) - ])); - }); - - test("Should enable company if god token is sent", async () => { - - const res = await test_agent - .put(`/company/${disabled_test_company_3._id}/enable`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body.isDisabled).toBe(false); - }); - - test("Should enable company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_2._id}/enable`) - .expect(StatusCodes.OK); - - expect(res.body.isDisabled).toBe(false); - }); - - test("Should enable company if logged as same company", async () => { - - await test_agent - .post("/auth/login") - .send(disabled_account_1) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`) - .expect(StatusCodes.OK); - - expect(res.body.isDisabled).toBe(false); - }); - - describe("With offers", () => { - const assertOfferList = (offers, expectedIsHidden, expectedHiddenReason) => { - expect(offers).not.toEqual(expect.arrayContaining([ - expect.objectContaining({ - isHidden: !expectedIsHidden, - hiddenReason: !expectedHiddenReason - }) - ])); - }; - - let disabled_company_with_offers; - const disabled_company_with_offers_data = { - name: "company-with-offers", - isDisabled: true, - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg", - }; - const account_with_offers = { - email: "offers@email.com", - password: "password123", - }; - - beforeAll(async () => { - disabled_company_with_offers = await Company.create(disabled_company_with_offers_data); - await Account.create({ - email: account_with_offers.email, - password: await hash(account_with_offers.password), - company: disabled_company_with_offers._id - }); - - const offer = { - title: "Test Offer", - publishDate: new Date(Date.now()), - publishEndDate: new Date(Date.now() + (DAY_TO_MS)), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 2, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - requirements: ["The candidate must be tested", "Fluent in testJS"], - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_DISABLED, - owner: disabled_company_with_offers._id, - ownerName: disabled_company_with_offers.name, - ownerLogo: disabled_company_with_offers.logo, - }; - - await Offer.create([offer, offer]); - }); - - afterAll(async () => { - await Account.deleteMany({ email: account_with_offers.email }); - await Company.deleteMany({ name: disabled_company_with_offers_data.name }); - await Offer.deleteMany({ owner: disabled_company_with_offers._id }); - }); - - test("should change offers' 'isHidden' on company enable", async () => { - - const offersBefore = await Offer.find({ owner: disabled_company_with_offers._id }); - - assertOfferList(offersBefore, true, OfferConstants.HiddenOfferReasons.COMPANY_DISABLED); - - const res = await test_agent - .put(`/company/${disabled_company_with_offers._id}/enable`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body.isDisabled).toBe(false); - - const offersAfter = await Offer.find({ owner: disabled_company_with_offers._id }); - - assertOfferList(offersAfter, false, undefined); - }); - }); - - test("should send an email to the company user when it is enabled", async () => { - await test_agent - .put(`/company/${disabled_test_company_mail._id}/enable`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - const emailOptions = COMPANY_ENABLED_NOTIFICATION( - disabled_test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: disabled_account_email.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); -}); diff --git a/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js b/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js deleted file mode 100644 index 4977d437..00000000 --- a/test/end-to-end/company/:id/hasReachedMaxConcurrentOffersBetweenDates.js +++ /dev/null @@ -1,354 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import hash from "../../../../src/lib/passwordHashing"; -import Account from "../../../../src/models/Account"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import CompanyConstants from "../../../../src/models/constants/Company"; -import withGodToken from "../../../utils/GodToken"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; -import ValidatorTester from "../../../utils/ValidatorTester"; - -describe("GET /company/:companyId/hasReachedMaxConcurrentOffersBetweenDates", () => { - - const test_agent = agent(); - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - let test_company_1, test_company_2; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - - const publishDate = (new Date(Date.now())).toISOString(); - const publishEndDate = (new Date(Date.now() + (2 * DAY_TO_MS))).toISOString(); - - beforeAll(async () => { - await Account.deleteMany({}); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - - await Company.deleteMany({}); - [test_company_1, test_company_2] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true, - logo: "http://oniebuedafixe.com/wow.png" - } - ]); - - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }); - - const testOffers = Array(CompanyConstants.offers.max_concurrent) - .fill(generateTestOffer({ - owner: test_company_2._id, - ownerName: test_company_2.name, - ownerLogo: test_company_2.logo, - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - })); - - await Offer.deleteMany({}); - await Offer.create(testOffers); - }); - - beforeEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using an invalid id", async () => { - - const res = await test_agent - .get("/company/123/hasReachedMaxConcurrentOffersBetweenDates") - .send(withGodToken({ publishDate, publishEndDate })) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining( - [ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.OBJECT_ID - }) - ] - )); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .get(`/company/${id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate })) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND(id) - }) - ])); - }); - }); - - describe("Input validation", () => { - - const testValidationUser = { - email: "validation@email.com", - password: "password123", - }; - let validationTestCompany; - const testValidationCompanyData = { - name: "validation-test-company", - hasFinishedRegistration: true - }; - - const EndpointValidatorTester = ValidatorTester( - (params) => test_agent.get(`/company/${validationTestCompany._id}/hasReachedMaxConcurrentOffersBetweenDates`).send(params) - ); - const BodyValidatorTester = EndpointValidatorTester("body"); - - beforeAll(async () => { - validationTestCompany = await Company.create(testValidationCompanyData); - - await Account.create({ - email: testValidationUser.email, - password: await hash(testValidationUser.password), - company: validationTestCompany._id - }); - }); - - afterAll(async () => { - await Account.deleteMany({ email: testValidationUser.email }); - await Company.deleteMany({ name: testValidationCompanyData.name }); - }); - - beforeEach(async () => { - await test_agent - .post("/auth/login") - .send(testValidationUser) - .expect(StatusCodes.OK); - }); - - describe("publishDate", () => { - const FieldValidatorTester = BodyValidatorTester("publishDate"); - FieldValidatorTester.mustBeDate(); - }); - - describe("publishEndDate", () => { - const FieldValidatorTester = BodyValidatorTester("publishEndDate"); - FieldValidatorTester.mustBeDate(); - FieldValidatorTester.mustBeAfter("publishDate"); - }); - }); - - describe("Auth", () => { - test("Should fail if not logged in", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - // TODO: change to use expect's helpers - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("Should fail if logged as a different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS); - }); - - test("Should succeed if god token is sent", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate })) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if logged as an admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if logged as the same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - }); - - test("Should succeed if publishDate is not specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishEndDate })) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if publishEndDate is not specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate })) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if neither publishDate or publishEndDate are specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should fail if publishDate is after publishEndDate", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ - publishDate: publishEndDate, - publishEndDate: publishDate, - })) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_AFTER("publishDate")); - }); - - test("Should fail if publishDate doesn't have a date format", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate: "123", publishEndDate })) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); - }); - - test("Should fail if publishEndDate doesn't have a date format", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate: "123" })) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); - }); - - test("Should return true if the company has reached max offers in the time interval", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_company_2._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("maxOffersReached", true); - }); -}); diff --git a/test/end-to-end/company/:id/index.js b/test/end-to-end/company/:id/index.js deleted file mode 100644 index 6621929a..00000000 --- a/test/end-to-end/company/:id/index.js +++ /dev/null @@ -1,776 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import hash from "../../../../src/lib/passwordHashing"; -import Account from "../../../../src/models/Account"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import CompanyConstants from "../../../../src/models/constants/Company"; -import withGodToken from "../../../utils/GodToken"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; - -describe("GET /company/:companyId", () => { - - const test_agent = agent(); - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - const test_company_data = { - name: "test-company", - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg", - }; - - beforeAll(async () => { - await Offer.deleteMany({}); - await Account.deleteMany({}); - await Company.deleteMany({}); - }); - - afterAll(async () => { - await Offer.deleteMany({}); - await Account.deleteMany({}); - await Company.deleteMany({}); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(StatusCodes.OK); - }); - - describe("Id Validation", () => { - test("should fail if invalid id", async () => { - const res = await test_agent - .get("/company/123") - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.OBJECT_ID, - }), - ]) - ); - }); - - test("should fail if company does not exist", async () => { - const id = "111111111111111111111111"; - - const res = await test_agent - .get(`/company/${id}`) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND(id), - }), - ]) - ); - }); - }); - - describe("Without Auth", () => { - - describe("Without offers", () => { - - let test_company_without_offers; - - beforeAll(async () => { - test_company_without_offers = await Company.create(test_company_data); - }); - - afterAll(async () => { - await Company.deleteMany({ _id: test_company_without_offers._id }); - }); - - test("should succeed when the company has no offers", async () => { - const res = await test_agent - .get(`/company/${test_company_without_offers.id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers", []); - expect(res.body).toHaveProperty( - "company._id", - test_company_without_offers._id.toString() - ); - }); - }); - - describe("With offers", () => { - - const createTestOffers = (length, company) => - Promise.all( - Array.from({ length }, () => - Offer.create( - generateTestOffer({ - publishDate: new Date( - Date.now() - DAY_TO_MS - ).toISOString(), - publishEndDate: new Date( - Date.now() + DAY_TO_MS - ).toISOString(), - owner: company._id, - ownerName: company.name, - ownerLogo: company.logo, - }) - ) - ) - ); - - describe("Below limit", () => { - - let test_company_with_offers_below_limit; - let offers; - - beforeAll(async () => { - test_company_with_offers_below_limit = await Company.create(test_company_data); - - offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible - 1, - test_company_with_offers_below_limit - ); - }); - - afterAll(async () => { - // await Offer.deleteMany({ _id: { $in: offers.map((x) => x._id) } }); prevent wasting time to compute the set of ids - await Offer.deleteMany({ owner: test_company_with_offers_below_limit._id }); - await Company.deleteMany({ _id: test_company_with_offers_below_limit._id }); - }); - - test("should return all offers when below limit", async () => { - const res = await test_agent - .get(`/company/${test_company_with_offers_below_limit._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - 1 - ); - expect(res.body.offers.map((x) => x._id).sort()).toEqual( - offers.map((x) => x._id.toString()).sort() - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_below_limit._id.toString() - ); - }); - }); - - describe("At limit", () => { - - let test_company_with_offers_at_limit; - let offers; - - beforeAll(async () => { - test_company_with_offers_at_limit = await Company.create(test_company_data); - - offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible, - test_company_with_offers_at_limit - ); - }); - - afterAll(async () => { - // await Offer.deleteMany({ _id: { $in: offers.map((x) => x._id) } }); prevent wasting time to compute the set of ids - await Offer.deleteMany({ owner: test_company_with_offers_at_limit._id }); - await Company.deleteMany({ _id: test_company_with_offers_at_limit._id }); - }); - - test("should return all offers when at limit", async () => { - const res = await test_agent - .get(`/company/${test_company_with_offers_at_limit._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - ); - expect(res.body.offers.map((x) => x._id).sort()).toEqual( - offers.map((x) => x._id.toString()).sort() - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_at_limit._id.toString() - ); - }); - }); - - describe("Above limit", () => { - - let test_company_with_offers_above_limit; - let offers; - - beforeAll(async () => { - test_company_with_offers_above_limit = await Company.create(test_company_data); - - offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible + 1, - test_company_with_offers_above_limit - ); - }); - - afterAll(async () => { - // await Offer.deleteMany({ _id: { $in: offers.map((x) => x._id) } }); prevent wasting time to compute the set of ids - await Offer.deleteMany({ owner: test_company_with_offers_above_limit._id }); - await Company.deleteMany({ _id: test_company_with_offers_above_limit._id }); - }); - - test("should limit number of offers", async () => { - - const res = await test_agent - .get(`/company/${test_company_with_offers_above_limit._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - ); - expect(offers.map((x) => x._id.toString())).toEqual( - expect.arrayContaining(res.body.offers.map((x) => x._id)) - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_above_limit._id.toString() - ); - }); - }); - }); - - describe("With hidden offer", () => { - - let test_company_with_hidden_offer; - let test_hidden_offer; - - beforeAll(async () => { - test_company_with_hidden_offer = await Company.create({ ...test_company_data }); - - test_hidden_offer = await Offer.create( - generateTestOffer({ - isHidden: true, - owner: test_company_with_hidden_offer._id.toString(), - ownerName: test_company_with_hidden_offer.name, - ownerLogo: test_company_with_hidden_offer.logo, - }) - ); - }); - - afterAll(async () => { - await Offer.deleteMany({ _id: test_hidden_offer._id }); - await Company.deleteOne({ _id: test_company_with_hidden_offer._id }); - }); - - - test("should not return hidden offers", async () => { - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers", []); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - }); - - describe("With disabled company", () => { - - let test_disabled_company; - - beforeAll(async () => { - test_disabled_company = await Company.create({ ...test_company_data, isBlocked: true }); - }); - - afterAll(async () => { - await Company.deleteOne({ _id: test_disabled_company._id }); - }); - - test("should fail if company is disabled", async () => { - const res = await test_agent - .get(`/company/${test_disabled_company._id}`) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_disabled_company._id - ), - }), - ]) - ); - }); - }); - - describe("With blocked company", () => { - - let test_blocked_company; - - beforeAll(async () => { - test_blocked_company = await Company.create({ ...test_company_data, isBlocked: true }); - }); - - afterAll(async () => { - await Company.deleteOne({ _id: test_blocked_company._id }); - }); - - test("should fail if company is blocked", async () => { - const res = await test_agent - .get(`/company/${test_blocked_company._id}`) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_blocked_company._id - ), - }), - ]) - ); - }); - }); - - describe("With company that hasn't finished registration", () => { - - let test_registration_unfinished_company; - - beforeAll(async () => { - test_registration_unfinished_company = await Company.create({ ...test_company_data, hasFinishedRegistration: false }); - }); - - afterAll(async () => { - await Company.deleteOne({ _id: test_registration_unfinished_company._id }); - }); - - test("should fail if company hasn't finished registration", async () => { - const res = await test_agent - .get( - `/company/${test_registration_unfinished_company._id}` - ) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_registration_unfinished_company._id - ), - }), - ]) - ); - }); - }); - }); - - describe("With Auth", () => { - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeAll(async () => { - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true, - }); - }); - - afterAll(async () => { - // since we only created one account, which happens to be an admin, this should be fine - await Account.deleteMany({ isAdmin: true }); - }); - - describe("With hidden offers", () => { - - const test_user_with_hidden_offer = { - email: "hidden@email.com", - password: "password123", - }; - - let test_company_with_hidden_offer; - let test_hidden_offer; - - beforeAll(async () => { - test_company_with_hidden_offer = await Company.create({ ...test_company_data }); - - await Account.create({ - email: test_user_with_hidden_offer.email, - password: await hash(test_user_with_hidden_offer.password), - company: test_company_with_hidden_offer._id, - }); - - test_hidden_offer = await Offer.create( - generateTestOffer({ - isHidden: true, - owner: test_company_with_hidden_offer._id.toString(), - ownerName: test_company_with_hidden_offer.name, - ownerLogo: test_company_with_hidden_offer.logo, - }) - ); - }); - - afterAll(async () => { - await Offer.deleteMany({ _id: test_hidden_offer._id }); - await Account.deleteOne({ email: test_user_with_hidden_offer.email }); - await Company.deleteOne({ _id: test_company_with_hidden_offer._id }); - }); - - test("should return hidden offers when user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_with_hidden_offer) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_hidden_offer._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should return hidden offers when user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_hidden_offer._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should return hidden offers when user is god", async () => { - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("offers", expect.arrayContaining([ - expect.objectContaining({ - _id: test_hidden_offer._id.toString(), - }), - ])); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - }); - - describe("With disabled company", () => { - - const test_user_disabled_company = { - email: "disabled@email.com", - password: "password123", - }; - - let test_disabled_company; - - beforeAll(async () => { - test_disabled_company = await Company.create({ ...test_company_data, isDisabled: true }); - - await Account.create({ - email: test_user_disabled_company.email, - password: await hash(test_user_disabled_company.password), - company: test_disabled_company._id, - }); - }); - - afterAll(async () => { - await Account.deleteOne({ email: test_user_disabled_company.email }); - await Company.deleteOne({ _id: test_disabled_company._id }); - }); - - test("should succeed if company is disabled and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_disabled_company) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_disabled_company._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_disabled_company._id.toString() - ); - }); - - test("should succeed if company is disabled and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_disabled_company._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_disabled_company._id.toString() - ); - }); - - test("should succeed if company is disabled and user is god", async () => { - const res = await test_agent - .get(`/company/${test_disabled_company._id}`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_disabled_company._id.toString() - ); - }); - }); - - describe("With blocked company", () => { - - const test_user_blocked_company = { - email: "blocked@email.com", - password: "password123", - }; - - let test_blocked_company; - - beforeAll(async () => { - test_blocked_company = await Company.create({ ...test_company_data, isBlocked: true }); - - await Account.create({ - email: test_user_blocked_company.email, - password: await hash(test_user_blocked_company.password), - company: test_blocked_company._id, - }); - }); - - afterAll(async () => { - await Account.deleteOne({ email: test_user_blocked_company.email }); - await Company.deleteOne({ _id: test_blocked_company._id }); - }); - - test("should fail if company is blocked and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_blocked_company) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_blocked_company._id}`) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.COMPANY_BLOCKED, - }), - ]) - ); - }); - - test("should succeed if company is blocked and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .get(`/company/${test_blocked_company._id}`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_blocked_company._id.toString() - ); - }); - - test("should succeed if company is blocked and user is god", async () => { - const res = await test_agent - .get(`/company/${test_blocked_company._id}`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_blocked_company._id.toString() - ); - }); - }); - - describe("With company that hasn't finished registration", () => { - - const test_user_with_unfinished_registration = { - email: "unfinished@email.com", - password: "password123", - }; - - let test_registration_unfinished_company; - - beforeAll(async () => { - test_registration_unfinished_company = await Company.create({ ...test_company_data, hasFinishedRegistration: false }); - - await Account.create({ - email: test_user_with_unfinished_registration.email, - password: await hash(test_user_with_unfinished_registration.password), - company: test_registration_unfinished_company._id, - }); - }); - - afterAll(async () => { - await Account.deleteOne({ email: test_user_with_unfinished_registration.email }); - await Company.deleteOne({ _id: test_registration_unfinished_company._id }); - }); - - test("should fail if company hasn't finished registration and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_with_unfinished_registration) - .expect(StatusCodes.OK); - - const res = await test_agent - .get( - `/company/${test_registration_unfinished_company._id}` - ) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - - test("should fail if company hasn't finished registration and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .get( - `/company/${test_registration_unfinished_company._id}` - ) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - - test("should fail if company hasn't finished registration and user is god", async () => { - const res = await test_agent - .get( - `/company/${test_registration_unfinished_company._id}` - ) - .send(withGodToken()) - .expect(StatusCodes.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - }); - }); -}); diff --git a/test/end-to-end/company/:id/unblock.js b/test/end-to-end/company/:id/unblock.js deleted file mode 100644 index 6c78c58a..00000000 --- a/test/end-to-end/company/:id/unblock.js +++ /dev/null @@ -1,423 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { ErrorTypes } from "../../../../src/api/middleware/errorHandler"; -import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; -import { COMPANY_UNBLOCKED_NOTIFICATION } from "../../../../src/email-templates/companyManagement"; -import EmailService, { EmailService as EmailServiceClass } from "../../../../src/lib/emailService"; -import hash from "../../../../src/lib/passwordHashing"; -import Account from "../../../../src/models/Account"; -import Company from "../../../../src/models/Company"; -import Offer from "../../../../src/models/Offer"; -import OfferConstants from "../../../../src/models/constants/Offer"; -import withGodToken from "../../../utils/GodToken"; -import { DAY_TO_MS } from "../../../utils/TimeConstants"; - -jest.mock("../../../../src/lib/emailService"); -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); - -describe("PUT /company/:companyId/unblock", () => { - const test_agent = agent(); - - beforeAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - await Offer.deleteMany({}); - }); - - afterAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("ID Validation", () => { - test("should fail if not a valid id", async () => { - const res = await test_agent - .put("/company/123/unblock") - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.OBJECT_ID - }) - ])); - }); - - test("should fail if company does not exist", async () => { - const id = "111111111111111111111111"; - - const res = await test_agent - .put(`/company/${id}/unblock`) - .send(withGodToken()) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND(id) - }) - ])); - }); - }); - - describe("Without auth", () => { - - const test_user = { - email: "email@email.com", - password: "password123", - }; - const test_company_data = { - name: "Company Ltd", - hasFinishedRegistration: true, - isBlocked: true - }; - - let test_company; - - beforeAll(async () => { - test_company = await Company.create(test_company_data); - - await Account.create({ - email: test_user.email, - password: await hash(test_user.password), - company: test_company._id - }); - }); - - afterAll(async () => { - await Account.deleteMany({ email: test_user.email }); - await Company.deleteMany({ _id: test_company._id }); - }); - - test("should fail if not logged in", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company.id}/unblock`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - }); - - describe("With auth", () => { - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - const test_user_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_company_1_data = { - name: "Company Ltd", - hasFinishedRegistration: true, - isBlocked: true - }; - - const test_user_2 = { - email: "company2@email.com", - password: "password123", - }; - const test_company_2_data = { - name: "Company Ltd", - hasFinishedRegistration: true, - isBlocked: true - }; - - const test_user_email = { - email: "companyemail@email.com", - password: "password123", - }; - const test_company_email_data = { - name: "Company Ltd", - hasFinishedRegistration: true, - isBlocked: true - }; - - let test_company_1, test_company_2, test_company_email; - - beforeAll(async () => { - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - - [ - test_company_1, - test_company_2, - test_company_email, - ] = await Company.create([ - test_company_1_data, - test_company_2_data, - test_company_email_data, - ]); - - await Account.create([ - { - email: test_user_1.email, - password: await hash(test_user_1.password), - company: test_company_1._id - }, - { - email: test_user_2.email, - password: await hash(test_user_2.password), - company: test_company_2._id - }, - { - email: test_user_email.email, - password: await hash(test_user_email.password), - company: test_company_email._id - } - ]); - }); - - afterAll(async () => { - await Account.deleteMany({ email: test_user_admin.email }); - await Account.deleteMany({ email: test_user_1.email }); - await Account.deleteMany({ email: test_user_2.email }); - - await Company.deleteMany({ name: test_company_1_data.name }); - await Company.deleteMany({ name: test_company_2_data.name }); - }); - - afterEach(async () => { - await test_agent - .del("/auth/login") - .expect(StatusCodes.OK); - }); - - test("should fail if logged in as company", async () => { - await test_agent - .post("/auth/login") - .send(test_user_1) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(StatusCodes.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.INSUFFICIENT_PERMISSIONS - }) - ])); - }); - - test("should allow if logged in as admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("isBlocked", false); - expect(res.body).not.toHaveProperty("adminReason"); - }); - - test("should allow with god token", async () => { - const res = await test_agent - .put(`/company/${test_company_2.id}/unblock`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("isBlocked", false); - }); - - test("should send an email to the company user when it is unblocked", async () => { - - await test_agent - .put(`/company/${test_company_email._id}/unblock`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - const emailOptions = COMPANY_UNBLOCKED_NOTIFICATION( - test_company_email.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_email.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - - describe("With offers", () => { - - const companyData = { - name: "Company Ltd", - hasFinishedRegistration: true, - isBlocked: true, - logo: "http://logo.com/alogo.png" - }; - - const test_user_with_company_hidden_offer = { - email: "with_company_hidden_offer@email.com", - password: "password123", - }; - - const test_user_with_admin_hidden_offer = { - email: "with_admin_hidden_offer@email.com", - password: "password123", - }; - - const test_user_with_blocked_company_hidden_offer = { - email: "with_blocked_company_hidden_offer@email.com", - password: "password123", - }; - - let company_with_company_hidden_offer, company_with_admin_hidden_offer, company_with_blocked_company_hidden_offer; - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: true, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - beforeAll(async () => { - [ - company_with_company_hidden_offer, - company_with_admin_hidden_offer, - company_with_blocked_company_hidden_offer - ] = await Company.create([ - companyData, - companyData, - companyData, - ]); - - await Account.create([ - { - email: test_user_with_company_hidden_offer.email, - password: await hash(test_user_with_company_hidden_offer.password), - company: company_with_company_hidden_offer._id - }, - { - email: test_user_with_admin_hidden_offer.email, - password: await hash(test_user_with_admin_hidden_offer.password), - company: company_with_admin_hidden_offer._id - }, - { - email: test_user_with_blocked_company_hidden_offer.email, - password: await hash(test_user_with_blocked_company_hidden_offer.password), - company: company_with_blocked_company_hidden_offer._id - } - ]); - - await Offer.create( - generateTestOffer({ - owner: company_with_company_hidden_offer._id, - ownerName: company_with_company_hidden_offer.name, - ownerLogo: company_with_company_hidden_offer.logo, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_REQUEST - }) - ); - - await Offer.create( - generateTestOffer({ - owner: company_with_admin_hidden_offer._id, - ownerName: company_with_admin_hidden_offer.name, - ownerLogo: company_with_admin_hidden_offer.logo, - hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK - }) - ); - - await Offer.create( - generateTestOffer({ - owner: company_with_blocked_company_hidden_offer._id, - ownerName: company_with_blocked_company_hidden_offer.name, - ownerLogo: company_with_blocked_company_hidden_offer.logo, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED - }) - ); - }); - - afterAll(async () => { - await Company.deleteMany({ name: companyData.name }); - - await Account.deleteMany({ email: test_user_with_company_hidden_offer.email }); - await Account.deleteMany({ email: test_user_with_admin_hidden_offer.email }); - await Account.deleteMany({ email: test_user_with_blocked_company_hidden_offer.email }); - - await Offer.deleteMany({ owner: company_with_company_hidden_offer._id }); - await Offer.deleteMany({ owner: company_with_admin_hidden_offer._id }); - await Offer.deleteMany({ owner: company_with_blocked_company_hidden_offer._id }); - }); - - test("should unblock offers blocked by company block", async () => { - const res = await test_agent - .put(`/company/${company_with_blocked_company_hidden_offer.id}/unblock`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("isBlocked", false); - - const offers = await Offer.find({ owner: company_with_blocked_company_hidden_offer._id }); - expect(offers).not.toEqual(expect.arrayContaining([ - expect.objectContaining({ - isHidden: true, - }) - ])); - }); - - test("should not unblock offers hidden by admin request", async () => { - const res = await test_agent - .put(`/company/${company_with_admin_hidden_offer.id}/unblock`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("isBlocked", false); - - const offers = await Offer.find({ owner: company_with_admin_hidden_offer._id }); - expect(offers).not.toEqual(expect.arrayContaining([ - expect.objectContaining({ - isHidden: false, - }) - ])); - }); - - test("should not unblock offers blocked by company request", async () => { - const res = await test_agent - .put(`/company/${company_with_company_hidden_offer.id}/unblock`) - .send(withGodToken()) - .expect(StatusCodes.OK); - - expect(res.body).toHaveProperty("isBlocked", false); - - const offers = await Offer.find({ owner: company_with_company_hidden_offer._id }); - expect(offers).not.toEqual(expect.arrayContaining([ - expect.objectContaining({ - isHidden: false, - }) - ])); - }); - }); - }); -}); diff --git a/test/end-to-end/company/application/finish.js b/test/end-to-end/company/application/finish.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/company/index.js b/test/end-to-end/company/index.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js old mode 100644 new mode 100755 diff --git a/test/end-to-end/offer/:id/archive.js b/test/end-to-end/offer/:id/archive.js deleted file mode 100644 index 491247ec..00000000 --- a/test/end-to-end/offer/:id/archive.js +++ /dev/null @@ -1,3 +0,0 @@ -test("bruh", () => { - expect(true).toBe(true); -}); diff --git a/test/end-to-end/offer/:id/disable.js b/test/end-to-end/offer/:id/disable.js deleted file mode 100644 index 491247ec..00000000 --- a/test/end-to-end/offer/:id/disable.js +++ /dev/null @@ -1,3 +0,0 @@ -test("bruh", () => { - expect(true).toBe(true); -}); diff --git a/test/end-to-end/offer/:id/enable.js b/test/end-to-end/offer/:id/enable.js deleted file mode 100644 index 491247ec..00000000 --- a/test/end-to-end/offer/:id/enable.js +++ /dev/null @@ -1,3 +0,0 @@ -test("bruh", () => { - expect(true).toBe(true); -}); diff --git a/test/end-to-end/offer/:id/hide.js b/test/end-to-end/offer/:id/hide.js deleted file mode 100644 index 491247ec..00000000 --- a/test/end-to-end/offer/:id/hide.js +++ /dev/null @@ -1,3 +0,0 @@ -test("bruh", () => { - expect(true).toBe(true); -}); diff --git a/test/end-to-end/offer/:id/index.js b/test/end-to-end/offer/:id/index.js deleted file mode 100644 index 491247ec..00000000 --- a/test/end-to-end/offer/:id/index.js +++ /dev/null @@ -1,3 +0,0 @@ -test("bruh", () => { - expect(true).toBe(true); -}); diff --git a/test/end-to-end/offer/company/:companyId/index.js b/test/end-to-end/offer/company/:companyId/index.js deleted file mode 100644 index a44c8aa5..00000000 --- a/test/end-to-end/offer/company/:companyId/index.js +++ /dev/null @@ -1,196 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import ValidationReasons from "../../../../../src/api/middleware/validators/validationReasons"; -import Offer from "../../../../../src/models/Offer"; -// import { DAY_TO_MS } from "../../../../utils/TimeConstants"; -import Company from "../../../../../src/models/Company"; -import Account from "../../../../../src/models/Account"; -import { ErrorTypes } from "../../../../../src/api/middleware/errorHandler"; - -describe("GET /offers/company/:companyId", () => { - - /* const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date(Date.now())).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobMinDuration: 1, - jobMaxDuration: 6, - jobType: "SUMMER INTERNSHIP", - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - isArchived: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - vacancies: 2, - ...params, - }); */ - - beforeAll(async () => { - await Offer.deleteMany({}); - await Company.deleteMany({}); - await Account.deleteMany({}); - }); - - afterAll(async () => { - await Offer.deleteMany({}); - await Company.deleteMany({}); - await Account.deleteMany({}); - }); - - describe("Id Validation", () => { - test("should fail if requested an invalid companyId", async () => { - const res = await request() - .get("/offers/company/123") - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "param": "companyId", - "msg": ValidationReasons.OBJECT_ID, - }) - ])); - }); - - test("should fail if there isn't a company with that id", async () => { - const missingCompanyId = "60ddb0bb2849830020883f91"; - - const res = await request() - .get(`/offers/company/${missingCompanyId}`) - .expect(StatusCodes.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors", expect.arrayContaining([ - expect.objectContaining({ - "param": "companyId", - "msg": ValidationReasons.COMPANY_NOT_FOUND(missingCompanyId), - }) - ])); - }); - }); - - describe("Without auth", () => { }); - - describe("With auth", () => { }); - - /* - describe("Get offer by companyId", () => { - const test_offers = [{}, {}, {}, {}]; - const test_agent = agent(); - - beforeAll(async () => { - await Offer.deleteMany({}); - - const createOffer = async (offer) => { - const { _id, owner, ownerName, ownerLogo } = await Offer.create({ - ...offer, - owner: test_company._id.toString(), - ownerName: test_company.name, - ownerLogo: test_company.logo, - }); - return { - ...offer, - owner: owner.toString(), - ownerName, - ownerLogo, - _id: _id.toString() - }; - }; - - (await Promise.all(test_offers - .map((_, i) => createOffer({ ...generateTestOffer(), isHidden: i === 2 })))) - .forEach((elem, i) => { - test_offers[i] = elem; - }); - }); - - test("should return hidden company offers as company", async () => { - // Login wiht test_user_company - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(StatusCodes.OK); - - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(StatusCodes.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - - // Logout - await test_agent - .del("/auth/login") - .expect(StatusCodes.OK); - }); - - test("should return non-hidden offers", async () => { - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(StatusCodes.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() - ); - }); - - test("should return non-hidden offers, even if target owner is set", async () => { - const res = await test_agent - .get(`/offers/company/${test_company._id}`) - .send({ - owner: test_company._id - }); - - expect(res.status).toBe(StatusCodes.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() - ); - }); - - test("should return hidden company offers as admin", async () => { - // Login with test_user_company - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(StatusCodes.OK); - - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(StatusCodes.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - - // Logout - await test_agent - .del("/auth/login") - .expect(StatusCodes.OK); - }); - - test("should return hidden company offers with god token", async () => { - // Send request with god token - const res = await test_agent - .get(`/offers/company/${test_company._id}`) - .send(withGodToken()); - - expect(res.status).toBe(StatusCodes.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - }); - }); - */ -}); diff --git a/test/end-to-end/offer/edit/:offerId/index.js b/test/end-to-end/offer/edit/:offerId/index.js deleted file mode 100644 index 491247ec..00000000 --- a/test/end-to-end/offer/edit/:offerId/index.js +++ /dev/null @@ -1,3 +0,0 @@ -test("bruh", () => { - expect(true).toBe(true); -}); diff --git a/test/end-to-end/offer/index.js b/test/end-to-end/offer/index.js old mode 100644 new mode 100755 diff --git a/test/schema/AccountSchema.js b/test/schema/AccountSchema.js old mode 100644 new mode 100755 diff --git a/test/schema/CompanyApplicationSchema.js b/test/schema/CompanyApplicationSchema.js old mode 100644 new mode 100755 diff --git a/test/schema/CompanySchema.js b/test/schema/CompanySchema.js old mode 100644 new mode 100755 diff --git a/test/schema/OfferSchema.js b/test/schema/OfferSchema.js old mode 100644 new mode 100755 diff --git a/test/unit/EmailService.js b/test/unit/EmailService.js old mode 100644 new mode 100755 diff --git a/test/unit/auth.js b/test/unit/auth.js old mode 100644 new mode 100755 diff --git a/test/unit/token.js b/test/unit/token.js old mode 100644 new mode 100755 diff --git a/test/unit/utils.js b/test/unit/utils.js old mode 100644 new mode 100755 diff --git a/test/utils/GodToken.js b/test/utils/GodToken.js old mode 100644 new mode 100755 diff --git a/test/utils/SchemaTester.js b/test/utils/SchemaTester.js old mode 100644 new mode 100755 diff --git a/test/utils/TimeConstants.js b/test/utils/TimeConstants.js old mode 100644 new mode 100755 diff --git a/test/utils/ValidatorTester.js b/test/utils/ValidatorTester.js old mode 100644 new mode 100755