diff --git a/.gitignore b/.gitignore index ad46b30..8fc1f5d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ lib-cov # Coverage directory used by tools like istanbul coverage +# Newman tests +newman + # nyc test coverage .nyc_output @@ -44,6 +47,7 @@ typings/ # Optional eslint cache .eslintcache +.eslintrc.y*ml # Optional REPL history .node_repl_history diff --git a/README.md b/README.md index 86f4379..b066022 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,9 @@ It starts Elasticsearch, DynamoDB and S3 compatible server. 5. Seed/Insert data to ES and DB: `npm run seed-data` 6. View DB table data: `npm run view-db-data `, ModelName can be `Member`, `MemberTrait`, `MemberDistributionStats`, `MemberHistoryStats`, `MemberStats`, `MemberSkill` or `MemberFinancial` 7. View ES data: `npm run view-es-data `, IndexName can be `member`, `member_trait` +8. Start mock api: `npm run mock-api` +9. Start newman tests: `npm run test:newman` +10. Clear data generated during newman tests: `npm run test:newman:clear` ## Local Deployment @@ -131,10 +134,10 @@ docker-compose up 5. When you are running the application for the first time, It will take some time initially to download the image and install the dependencies - ## Running tests ### Prepare + - Various config parameters should be properly set. - Start Local services. - Create DynamoDB tables. @@ -144,6 +147,7 @@ docker-compose up ### Running unit tests + To run unit tests alone ```bash @@ -155,21 +159,78 @@ To run unit tests with coverage report ```bash npm run test:cov ``` - -### Running integration tests -To run integration tests alone - -```bash -npm run e2e -``` - -To run integration tests with coverage report - -```bash -npm run e2e:cov -``` +### Running E2E Automated Postman Tests + +1. In the `member-api` root directory create `.env` file with the next environment variables. + + ```bash + # Auth0 config + AUTH_SECRET= + AUTH0_URL= + AUTH0_AUDIENCE= + AUTH0_CLIENT_ID= + AUTH0_CLIENT_SECRET= + AUTH_V2_URL= + AUTH_V2_CLIENT_ID= + AUTH_V3_URL= + # Following configs are used to generate tokens. + ADMIN_CREDENTIALS_USERNAME= + ADMIN_CREDENTIALS_PASSWORD= + MANAGER_CREDENTIALS_USERNAME= + MANAGER_CREDENTIALS_PASSWORD= + COPILOT_CREDENTIALS_USERNAME= + COPILOT_CREDENTIALS_PASSWORD= + USER_CREDENTIALS_USERNAME= + USER_CREDENTIALS_PASSWORD= + # Locally deployed services + IS_LOCAL_DB=true + IS_LOCAL_S3=true + PHOTO_S3_BUCKET=photos + AWS_ACCESS_KEY_ID=FAKE_ACCESS_KEY + AWS_SECRET_ACCESS_KEY=FAKE_SECRET_ACCESS_KEY + MOCK_API_PORT=4000 + AUTOMATED_TESTING_NAME_PREFIX=POSTMANE2E- + S3_ENDPOINT=http://localhost:9000 + API_BASE_URL=http://localhost:3000 + BUSAPI_URL=http://localhost:4000/v5 + TAGS_BASE_URL=http://localhost:4000 + TAGS_API_VERSION=/v3 + TAGS_FILTER=/tags + ``` +1. Install npm dependencies + ```bash + npm install + ``` +1. Start Elasticsearch, DynamoDB and S3 + ```bash + cd local + docker-compose up -d + ``` +1. Initialize Database and Elasticsearch + ```bash + npm run create-tables + npm run init-db + npm run init-es + npm run seed-data + ``` +1. login minio at http://localhost:9000 and create bucket with name `photos` +1. Start mock api and member api in TEST mode + ```bash + npm run mock-api + npm run start:test + ``` +1. Run tests + ```bash + npm run test:newman + ``` +1. To run the tests again, clear data and seed again + ```bash + npm run test:newman:clear + npm run seed-data + ``` ## Verification + Refer to the verification document `Verification.md` ## Notes @@ -189,4 +250,3 @@ Refer to the verification document `Verification.md` - the tests use mock S3 service to upload member photo, so you may use the provided mock AWS credential for tests, but Postman tests require using real AWS S3, you need to follow README.md to create S3 bucket and provide your own AWS credential so that the upload photo API works - diff --git a/Verification.md b/Verification.md index a0f02cf..86e7a76 100644 --- a/Verification.md +++ b/Verification.md @@ -6,6 +6,12 @@ - import Postman collection and environment in the docs folder to Postman - run the Postman tests +## Clear Test Data Service Verification +- After running newman tests, clear data with `npm run test:newman:clear` +- To check if data cleared properly, run following commands +- `npm run view-db-data ` +- `npm run view-es-data ` + ## DynamoDB Verification Run command `npm run view-db-data ` to view table data, ModelName can be `Member`, `MemberTrait`, `MemberDistributionStats`, `MemberHistoryStats`, `MemberStats`, `MemberSkill` or `MemberFinancial` diff --git a/app-bootstrap.js b/app-bootstrap.js index 664c76c..6eb4458 100644 --- a/app-bootstrap.js +++ b/app-bootstrap.js @@ -7,4 +7,4 @@ const Joi = require('joi') Joi.page = () => Joi.number().integer().min(1).default(1) Joi.perPage = () => Joi.number().integer().min(1).max(100).default(50) Joi.size = () => Joi.number().integer().min(1).max(1000).default(500) -Joi.sort = () => Joi.string().default("asc") \ No newline at end of file +Joi.sort = () => Joi.string().default('asc') diff --git a/app-routes.js b/app-routes.js index 9167695..b8af818 100644 --- a/app-routes.js +++ b/app-routes.js @@ -41,7 +41,7 @@ module.exports = (app) => { // add Authenticator/Authorization check if route has auth actions.push((req, res, next) => { // When authorization token is not provided and allow no token is enabled then bypass - if(!_.get(req, 'headers.authorization') && def.allowNoToken) { + if (!_.get(req, 'headers.authorization') && def.allowNoToken) { next() } else { authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, res, next) @@ -50,7 +50,7 @@ module.exports = (app) => { actions.push((req, res, next) => { // When authorization token is not provided and allow no token is enabled then bypass - if(!_.get(req, 'headers.authorization') && def.allowNoToken) { + if (!_.get(req, 'headers.authorization') && def.allowNoToken) { next() } else { if (req.authUser.isMachine) { diff --git a/config/default.js b/config/default.js index b945355..47e4bcd 100644 --- a/config/default.js +++ b/config/default.js @@ -1,7 +1,7 @@ /** * The configuration file. */ - +require('dotenv').config() module.exports = { LOG_LEVEL: process.env.LOG_LEVEL || 'debug', PORT: process.env.PORT || 3000, @@ -25,15 +25,16 @@ module.exports = { TAGS: { TAGS_BASE_URL: process.env.TAGS_BASE_URL || 'https://api.topcoder-dev.com', TAGS_API_VERSION: process.env.TAGS_API_VERSION || '/v3', - TAGS_FILTER: process.env.TAGS_FILTER || '/tags/?filter=domain%3DSKILLS%26status%3DAPPROVED&limit=1000', + TAGS_FILTER: process.env.TAGS_FILTER || '/tags/?filter=domain%3DSKILLS%26status%3DAPPROVED&limit=1000' }, - + // aws config params AMAZON: { AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY, AWS_REGION: process.env.AWS_REGION || 'us-east-1', IS_LOCAL_DB: process.env.IS_LOCAL_DB ? process.env.IS_LOCAL_DB === 'true' : false, + IS_LOCAL_S3: process.env.IS_LOCAL_S3 ? process.env.IS_LOCAL_S3 === 'true' : false, DYNAMODB_URL: process.env.DYNAMODB_URL || 'http://localhost:7777', PHOTO_S3_BUCKET: process.env.PHOTO_S3_BUCKET || 'topcoder-dev-media/member/profile', S3_API_VERSION: process.env.S3_API_VERSION || '2006-03-01' @@ -48,7 +49,7 @@ module.exports = { MEMBER_PROFILE_ES_INDEX: process.env.MEMBER_PROFILE_ES_INDEX || 'members-2020-01', // member type, ES 6.x accepts only 1 Type per index and it's mandatory to define it MEMBER_PROFILE_ES_TYPE: process.env.MEMBER_PROFILE_ES_TYPE || 'profiles', - MEMBER_TRAIT_ES_INDEX: process.env.MEMBER_TRAIT_ES_INDEX || 'members-2020-01', + MEMBER_TRAIT_ES_INDEX: process.env.MEMBER_TRAIT_ES_INDEX || 'membertraits-2020-01', MEMBER_TRAIT_ES_TYPE: process.env.MEMBER_TRAIT_ES_TYPE || 'profiletraits', MEMBER_STATS_ES_INDEX: process.env.MEMBER_STATS_ES_INDEX || 'memberstats-2020-01', MEMBER_STATS_ES_TYPE: process.env.MEMBER_STATS_ES_TYPE || 'stats', @@ -62,12 +63,12 @@ module.exports = { // file upload max size in bytes FILE_UPLOAD_SIZE_LIMIT: process.env.FILE_UPLOAD_SIZE_LIMIT ? Number(process.env.FILE_UPLOAD_SIZE_LIMIT) : 10 * 1024 * 1024, // 10M - + // photo URL template, its will be replaced with S3 object key, // the URL is specific to AWS region and bucket, you may go to AWS console S3 service to // see bucket object URL to get the URL structure PHOTO_URL_TEMPLATE: process.env.PHOTO_URL_TEMPLATE || 'https://topcoder-dev-media.s3.us-east-1.amazonaws.com/member/profile/', - + // verify token expiration in minutes VERIFY_TOKEN_EXPIRATION: process.env.VERIFY_TOKEN_EXPIRATION || 60, @@ -84,33 +85,33 @@ module.exports = { READ: process.env.SCOPE_MEMBERS_READ || 'read:user_profiles', UPDATE: process.env.SCOPE_MEMBERS_UPDATE || 'update:user_profiles', DELETE: process.env.SCOPE_MEMBERS_DELETE || 'delete:user_profiles', - ALL: process.env.SCOPE_MEMBERS_ALL || 'all:user_profiles', + ALL: process.env.SCOPE_MEMBERS_ALL || 'all:user_profiles' } }, - + // Member identifiable info fields, only admin, M2M, or member himself can get these fields - MEMBER_SECURE_FIELDS: process.env.MEMBER_SECURE_FIELDS - ? process.env.MEMBER_SECURE_FIELDS.split(',') + MEMBER_SECURE_FIELDS: process.env.MEMBER_SECURE_FIELDS + ? process.env.MEMBER_SECURE_FIELDS.split(',') : ['firstName', 'lastName', 'email', 'addresses', 'createdBy', 'updatedBy'], // Member traits identifiable info fields, only admin, M2M, or member himself can fetch these fields - MEMBER_TRAIT_SECURE_FIELDS: process.env.MEMBER_TRAIT_SECURE_FIELDS - ? process.env.MEMBER_TRAIT_SECURE_FIELDS.split(',') + MEMBER_TRAIT_SECURE_FIELDS: process.env.MEMBER_TRAIT_SECURE_FIELDS + ? process.env.MEMBER_TRAIT_SECURE_FIELDS.split(',') : ['createdBy', 'updatedBy'], // Misc identifiable info fields, only admin, M2M, or member himself can fetch these fields - MISC_SECURE_FIELDS: process.env.MISC_SECURE_FIELDS - ? process.env.MISC_SECURE_FIELDS.split(',') + MISC_SECURE_FIELDS: process.env.MISC_SECURE_FIELDS + ? process.env.MISC_SECURE_FIELDS.split(',') : ['createdBy', 'updatedBy'], - + // Member Search identifiable info fields, only admin, M2M, or member himself can fetch these fields - SEARCH_SECURE_FIELDS: process.env.SEARCH_SECURE_FIELDS - ? process.env.SEARCH_SECURE_FIELDS.split(',') - : ['firstName', 'lastName', 'email', 'addresses', 'createdBy', 'updatedBy'], - + SEARCH_SECURE_FIELDS: process.env.SEARCH_SECURE_FIELDS + ? process.env.SEARCH_SECURE_FIELDS.split(',') + : ['firstName', 'lastName', 'email', 'addresses', 'createdBy', 'updatedBy'], + // Member Statistics identifiable info fields, only admin, M2M, or member himself can fetch these fields STATISTICS_SECURE_FIELDS: process.env.STATISTICS_SECURE_FIELDS ? process.env.STATISTICS_SECURE_FIELDS.split(',') : ['createdBy', 'updatedBy'] - + } diff --git a/config/test.js b/config/test.js index f7d627c..8c7f07f 100644 --- a/config/test.js +++ b/config/test.js @@ -1,14 +1,33 @@ /** * Configuration file to be used while running tests */ - +require('dotenv').config() module.exports = { - ADMIN_TOKEN: process.env.ADMIN_TOKEN || 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29ubmVjdCBTdXBwb3J0IiwiYWRtaW5pc3RyYXRvciIsInRlc3RSb2xlIiwiYWFhIiwidG9ueV90ZXN0XzEiLCJDb25uZWN0IE1hbmFnZXIiLCJDb25uZWN0IEFkbWluIiwiY29waWxvdCIsIkNvbm5lY3QgQ29waWxvdCBNYW5hZ2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJUb255SiIsImV4cCI6MTY5Mjc5NTIxMSwidXNlcklkIjoiODU0Nzg5OSIsImlhdCI6MTU0OTc5MTYxMSwiZW1haWwiOiJ0amVmdHMrZml4QHRvcGNvZGVyLmNvbSIsImp0aSI6ImY5NGQxZTI2LTNkMGUtNDZjYS04MTE1LTg3NTQ1NDRhMDhmMSJ9.sAku5sLBpfTkq4OANJA-eiZCiOxx4u6U6OgpTlk_OU4', - USER_TOKEN: process.env.USER_TOKEN || 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJkZW5pcyIsImV4cCI6MTY4MjgwMDE2OSwidXNlcklkIjoiMjUxMjgwIiwiaWF0IjoxNTQ5Nzk5NTY5LCJlbWFpbCI6ImVtYWlsQGRvbWFpbi5jb20ueiIsImp0aSI6IjljNDUxMWM1LWMxNjUtNGExYi04OTllLWI2NWFkMGUwMmI1NSJ9.BCF6xW3aQfHDDFbgGvvOKzvwEXVLWGf-TgF5JrtM9Tg', - EXPIRED_TOKEN: process.env.EXPIRED_TOKEN || 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJjb3BpbG90IiwiQ29ubmVjdCBTdXBwb3J0Il0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJHaG9zdGFyIiwiZXhwIjoxNTQ5ODAwMDc3LCJ1c2VySWQiOiIxNTE3NDMiLCJpYXQiOjE1NDk3OTk0NzcsImVtYWlsIjoiZW1haWxAZG9tYWluLmNvbS56IiwianRpIjoiMTJjMWMxMGItOTNlZi00NTMxLTgzMDUtYmE2NjVmYzRlMWI0In0.2n8k9pb16sE7LOLF_7mjAvEVKgggzS-wS3_8n2-R4RU', - INVALID_TOKEN: process.env.INVALID_TOKEN || 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJBZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6IlRvbnlKIiwiZXhwIjo1NTUzMDE5OTI1OSwidXNlcklkIjoiNDA0MzMyODgiLCJpYXQiOjE1MzAxOTg2NTksImVtYWlsIjoiYWRtaW5AdG9wY29kZXIuY29tIiwianRpIjoiYzNhYzYwOGEtNTZiZS00NWQwLThmNmEtMzFmZTk0Yjk1NjFjIn0.ePREgnJrBixP4URf1dd8FHISN2_6eRM5gjCReS0ZMK4', - M2M_FULL_ACCESS_TOKEN: process.env.M2M_FULL_ACCESS_TOKEN || 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZW5qdzE4MTBlRHozWFR3U08yUm4yWTljUVRyc3BuM0JAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTUwOTA2Mzg4LCJleHAiOjE2ODA5OTI3ODgsImF6cCI6ImVuancxODEwZUR6M1hUd1NPMlJuMlk5Y1FUcnNwbjNCIiwic2NvcGUiOiJhbGw6bWVtYmVycyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.Eo_cyyPBQfpWp_8-NSFuJI5MvkEV3UJZ3ONLcFZedoA', - M2M_READ_ACCESS_TOKEN: process.env.M2M_READ_ACCESS_TOKEN || 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZW5qdzE4MTBlRHozWFR3U08yUm4yWTljUVRyc3BuM0JAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTUwOTA2Mzg4LCJleHAiOjE2ODA5OTI3ODgsImF6cCI6ImVuancxODEwZUR6M1hUd1NPMlJuMlk5Y1FUcnNwbjNCIiwic2NvcGUiOiJyZWFkOm1lbWJlcnMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.F-dEZXJC7Ue7dHCi3XQdEvxhtr69hU4MwTcr-APHnK4', - M2M_UPDATE_ACCESS_TOKEN: process.env.M2M_UPDATE_ACCESS_TOKEN || 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZW5qdzE4MTBlRHozWFR3U08yUm4yWTljUVRyc3BuM0JAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTUwOTA2Mzg4LCJleHAiOjE2ODA5OTI3ODgsImF6cCI6ImVuancxODEwZUR6M1hUd1NPMlJuMlk5Y1FUcnNwbjNCIiwic2NvcGUiOiJ1cGRhdGU6bWVtYmVycyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.wImcvhkF9QPOCSEfZ01U-YxYM8NZi1yqgRmw3eiNn1Q', - S3_ENDPOINT: process.env.S3_ENDPOINT || 'localhost:9000' + LOG_LEVEL: process.env.LOG_LEVEL || 'info', + AUTH_V2_URL: process.env.AUTH_V2_URL, + AUTH_V2_CLIENT_ID: process.env.AUTH_V2_CLIENT_ID, + AUTH_V3_URL: process.env.AUTH_V3_URL, + MOCK_API_PORT: process.env.MOCK_API_PORT || 4000, + ADMIN_CREDENTIALS_USERNAME: process.env.ADMIN_CREDENTIALS_USERNAME, + ADMIN_CREDENTIALS_PASSWORD: process.env.ADMIN_CREDENTIALS_PASSWORD, + MANAGER_CREDENTIALS_USERNAME: process.env.MANAGER_CREDENTIALS_USERNAME, + MANAGER_CREDENTIALS_PASSWORD: process.env.MANAGER_CREDENTIALS_PASSWORD, + COPILOT_CREDENTIALS_USERNAME: process.env.COPILOT_CREDENTIALS_USERNAME, + COPILOT_CREDENTIALS_PASSWORD: process.env.COPILOT_CREDENTIALS_PASSWORD, + USER_CREDENTIALS_USERNAME: process.env.USER_CREDENTIALS_USERNAME, + USER_CREDENTIALS_PASSWORD: process.env.USER_CREDENTIALS_PASSWORD, + WAIT_TIME: 6000, + AUTOMATED_TESTING_REPORTERS_FORMAT: process.env.AUTOMATED_TESTING_REPORTERS_FORMAT + ? process.env.AUTOMATED_TESTING_REPORTERS_FORMAT.split(',') + : ['cli', 'html'], + AUTOMATED_TESTING_NAME_PREFIX: process.env.AUTOMATED_TESTING_NAME_PREFIX || 'POSTMANE2E-', + S3_ENDPOINT: process.env.S3_ENDPOINT || 'http://localhost:9000', + API_BASE_URL: process.env.API_BASE_URL || 'http://localhost:3000', + BUSAPI_URL: process.env.BUSAPI_URL || 'http://localhost:4000/v5', + TAGS: { + TAGS_BASE_URL: process.env.TAGS_BASE_URL || 'http://localhost:4000', + TAGS_API_VERSION: process.env.TAGS_API_VERSION || '/v3', + TAGS_FILTER: process.env.TAGS_FILTER || '/tags' + } + } diff --git a/local/docker-compose.yml b/local/docker-compose.yml index 5f62a8e..b7f7f98 100644 --- a/local/docker-compose.yml +++ b/local/docker-compose.yml @@ -15,7 +15,10 @@ services: image: minio/minio ports: - "9000:9000" + - "9001:9001" + volumes: + - ~/minio/data:/data environment: MINIO_ACCESS_KEY: "FAKE_ACCESS_KEY" MINIO_SECRET_KEY: "FAKE_SECRET_ACCESS_KEY" - command: "server /data" + command: "server --console-address :9001 /data" diff --git a/mock-api/mock-api.js b/mock-api/mock-api.js new file mode 100644 index 0000000..fbc4f3e --- /dev/null +++ b/mock-api/mock-api.js @@ -0,0 +1,103 @@ +/** + * The mock APIs. + */ + +const config = require('config') +const express = require('express') +const cors = require('cors') +const logger = require('../src/common/logger') +const _ = require('lodash') +const constants = require('../app-constants') +const helper = require('../src/common/helper') +const esClient = helper.getESClient() + +const tags = [{ 'id': 286, 'name': 'Node.js', 'domain': 'SKILLS', 'status': 'APPROVED', 'categories': ['develop'], 'priority': 0, 'synonyms': null, 'createdAt': '2016-05-18', 'updatedAt': '2016-05-18', 'createdBy': 1, 'updatedBy': 1 }, + { 'id': 380, 'name': 'VB.NET', 'domain': 'SKILLS', 'status': 'APPROVED', 'categories': ['develop'], 'priority': 0, 'synonyms': null, 'createdAt': '2016-05-18', 'updatedAt': '2016-05-18', 'createdBy': 1, 'updatedBy': 1 }, + { 'id': 186, 'name': 'Force.Com Sites', 'domain': 'SKILLS', 'status': 'APPROVED', 'categories': ['develop'], 'priority': 0, 'synonyms': null, 'createdAt': '2016-05-18', 'updatedAt': '2016-05-18', 'createdBy': 1, 'updatedBy': 1 }, + { 'id': 382, 'name': 'Vert.X', 'domain': 'SKILLS', 'status': 'APPROVED', 'categories': ['develop'], 'priority': 0, 'synonyms': null, 'createdAt': '2016-05-18', 'updatedAt': '2016-05-18', 'createdBy': 1, 'updatedBy': 1 }, + { 'id': 311, 'name': 'Python', 'domain': 'SKILLS', 'status': 'APPROVED', 'categories': ['develop'], 'priority': 12, 'synonyms': null, 'createdAt': '2016-05-18', 'updatedAt': '2016-05-18', 'createdBy': 1, 'updatedBy': 1 }, + { 'id': 212, 'name': 'HTML', 'domain': 'SKILLS', 'status': 'APPROVED', 'categories': ['develop'], 'priority': 0, 'synonyms': null, 'createdAt': '2016-05-18', 'updatedAt': '2016-05-18', 'createdBy': 1, 'updatedBy': 1 }] + +const app = express() +app.set('port', config.MOCK_API_PORT || 4000) +app.use(cors()) +app.use(express.json()) +app.use((req, res, next) => { + logger.info({ message: `${req.method} ${req.url}` }) + next() +}) + +app.get('/v3/tags', (req, res) => { + res.status(200).json({ result: { content: tags } }) +}) + +app.post('/v5/bus/events', async (req, res, next) => { + try { + if (req.body.topic === constants.TOPICS.MemberTraitCreated) { + await esClient.create({ + index: config.ES.MEMBER_TRAIT_ES_INDEX, + type: config.ES.MEMBER_TRAIT_ES_TYPE, + id: req.body.payload.userId.toString() + '_' + req.body.payload.traitId, + body: req.body.payload, + refresh: 'true' + }) + } else if (req.body.topic === constants.TOPICS.MemberTraitDeleted) { + await Promise.all(_.map(req.body.payload.memberProfileTraitIds, async id => + esClient.delete({ + index: config.ES.MEMBER_TRAIT_ES_INDEX, + type: config.ES.MEMBER_TRAIT_ES_TYPE, + id: req.body.payload.userId.toString() + '_' + id, + refresh: 'true' + }))) + } else if (req.body.topic === constants.TOPICS.MemberTraitUpdated) { + await esClient.update({ + index: config.ES.MEMBER_TRAIT_ES_INDEX, + type: config.ES.MEMBER_TRAIT_ES_TYPE, + id: req.body.payload.userId.toString() + '_' + req.body.payload.traitId, + body: { + doc: req.body.payload + }, + refresh: 'true' + }) + } else if (req.body.topic === constants.TOPICS.EmailChanged) { + // do nothing + } else if (req.body.topic === constants.TOPICS.MemberUpdated) { + await esClient.update({ + index: config.ES.MEMBER_PROFILE_ES_INDEX, + type: config.ES.MEMBER_PROFILE_ES_TYPE, + id: req.body.payload.handleLower, + body: { + doc: req.body.payload + }, + refresh: 'true' + }) + } + res.status(200).end() + } catch (err) { + next(err) + } +}) + +app.get('/members/:handle/verify', async (req, res) => { + const token = await esClient.get({ + index: config.ES.MEMBER_PROFILE_ES_INDEX, + type: config.ES.MEMBER_PROFILE_ES_TYPE, + id: req.params.handle.toLowerCase() + }) + res.status(200).json({ emailVerifyToken: token._source.emailVerifyToken, newEmailVerifyToken: token._source.newEmailVerifyToken }) +}) + +app.use((req, res) => { + res.status(404).json({ error: 'route not found' }) +}) + +app.use((err, req, res, next) => { + logger.logFullError(err, { signature: `${req.method}_${req.url}` }) + res.status(500).json({ + error: err.message + }) +}) + +app.listen(app.get('port'), '0.0.0.0', () => { + logger.info({ message: `Mock Api listening on port ${app.get('port')}` }) +}) diff --git a/package-lock.json b/package-lock.json index 5965819..580eb7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -173,6 +173,62 @@ "kuler": "^2.0.0" } }, + "@hapi/hoek": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", + "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==", + "dev": true + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@postman/form-data": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", + "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "@postman/tunnel-agent": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", + "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "@sideway/address": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", + "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "dev": true + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -653,6 +709,15 @@ "concat-map": "0.0.1" } }, + "brotli": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.2.tgz", + "integrity": "sha1-UlqcrU/LqWR119OI9q7LE+7VL0Y=", + "dev": true, + "requires": { + "base64-js": "^1.1.2" + } + }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -710,6 +775,15 @@ "write-file-atomic": "^2.4.2" } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -783,6 +857,12 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "charset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", + "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", + "dev": true + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -804,6 +884,107 @@ "restore-cursor": "^2.0.0" } }, + "cli-progress": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.0.tgz", + "integrity": "sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "cli-width": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", @@ -917,6 +1098,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1016,6 +1203,58 @@ "safe-buffer": "^5.0.1" } }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", @@ -1031,6 +1270,12 @@ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" }, + "csv-parse": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.0.tgz", + "integrity": "sha512-Zb4tGPANH4SW0LgC9+s9Mnequs9aqn7N3/pCqNbVjs2XhEF6yWNU2Vm4OGl1v2Go9nw8rXt87Cm2QN/o6Vpqgg==", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1039,6 +1284,12 @@ "assert-plus": "^1.0.0" } }, + "dbug": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/dbug/-/dbug-0.4.2.tgz", + "integrity": "sha1-MrSzEF6IYQQ6b5rHVdgOVC02WzE=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1170,6 +1421,11 @@ "esutils": "^2.0.2" } }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, "dtrace-provider": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", @@ -1353,8 +1609,112 @@ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", "requires": { "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + } + } + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + } } } } @@ -1882,6 +2242,12 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -1961,6 +2327,12 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, + "faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", + "dev": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2006,6 +2378,18 @@ "object-assign": "^4.0.1" } }, + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + }, + "filesize": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.3.0.tgz", + "integrity": "sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g==", + "dev": true + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -2092,6 +2476,12 @@ } } }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, "fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -2184,6 +2574,16 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-parameter-names": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/get-parameter-names/-/get-parameter-names-0.3.0.tgz", @@ -2234,6 +2634,27 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2264,6 +2685,11 @@ "ansi-regex": "^2.0.0" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2362,6 +2788,12 @@ } } }, + "http-reasons": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", + "integrity": "sha1-qVPKZwB4Zp3eFCzomUAbnW6F07Q=", + "dev": true + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -2377,6 +2809,22 @@ "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.4.0.tgz", "integrity": "sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==" }, + "httpntlm": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.7.7.tgz", + "integrity": "sha512-Pv2Rvrz8H0qv1Dne5mAdZ9JegG1uc6Vu5lwLflIY6s8RKHdZQbW39L4dYswSgqMDT0pkJILUTKjeyU0VPNRZjA==", + "dev": true, + "requires": { + "httpreq": ">=0.4.22", + "underscore": "~1.12.1" + } + }, + "httpreq": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.5.2.tgz", + "integrity": "sha512-2Jm+x9WkExDOeFRrdBCBSpLPT5SokTcRHkunV3pjKmX/cx6av8zQ0WtHUMDrYb6O4hBFzNU6sxJEypvRUVYKnw==", + "dev": true + }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -2535,6 +2983,20 @@ } } }, + "intel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/intel/-/intel-1.2.0.tgz", + "integrity": "sha1-EdEUfraz9Fgr31M3s31UFYTp5B4=", + "dev": true, + "requires": { + "chalk": "^1.1.0", + "dbug": "~0.4.2", + "stack-trace": "~0.0.9", + "strftime": "~0.10.0", + "symbol": "~0.3.1", + "utcstring": "~0.1.0" + } + }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -2551,6 +3013,19 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==" + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "requires": { + "call-bind": "^1.0.2" + } + }, "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", @@ -2596,6 +3071,11 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==" + }, "is-regex": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", @@ -2618,8 +3098,7 @@ "is-string": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" }, "is-symbol": { "version": "1.0.3", @@ -2813,6 +3292,12 @@ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" }, + "js-sha512": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", + "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3016,6 +3501,12 @@ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, + "liquid-json": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", + "integrity": "sha1-kVWhgTbYprJhXl8W+aJEira1Duo=", + "dev": true + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -3240,6 +3731,15 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, + "mime-format": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.1.tgz", + "integrity": "sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==", + "dev": true, + "requires": { + "charset": "^1.0.0" + } + }, "mime-types": { "version": "2.1.27", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", @@ -3411,39 +3911,200 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, "nested-error-stacks": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", "dev": true }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "newman": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/newman/-/newman-5.2.4.tgz", + "integrity": "sha512-RFo3Bteh8CMDD4/eld+ItQ1trrtLN9Mlwk/6mnk2/lBKG08U+ysREwk8Jy74X+a0vmpVCljlGk5MifdcCdkkRg==", "dev": true, "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" + "async": "3.2.0", + "chardet": "1.3.0", + "cli-progress": "3.9.0", + "cli-table3": "0.6.0", + "colors": "1.4.0", + "commander": "7.2.0", + "csv-parse": "4.16.0", + "eventemitter3": "4.0.7", + "filesize": "6.3.0", + "lodash": "4.17.21", + "mkdirp": "1.0.4", + "postman-collection": "4.0.0", + "postman-collection-transformer": "4.1.2", + "postman-request": "2.88.1-postman.30", + "postman-runtime": "7.28.2", + "pretty-ms": "7.0.1", + "semver": "7.3.5", + "serialised-error": "1.1.3", + "tough-cookie": "3.0.1", + "word-wrap": "1.2.3", + "xmlbuilder": "15.1.1" + }, + "dependencies": { + "chardet": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.3.0.tgz", + "integrity": "sha512-cyTQGGptIjIT+CMGT5J/0l9c6Fb+565GCFjjeUTKxUO7w3oR+FcNCMEKTn5xtVKaLFmladN7QF68IiQsv5Fbdw==", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "newman-reporter-html": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/newman-reporter-html/-/newman-reporter-html-1.0.5.tgz", + "integrity": "sha512-Kz8ejzJqDaasyqNuP8F7bBYzsts7JP3wBfdRQDOYPCUchVQF63KsbxtxbGadyzOeXcZsXs6YT3pe4FFlN51jcw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } + "filesize": "6.0.1", + "handlebars": "4.5.3", + "lodash": "4.17.15", + "pretty-ms": "5.1.0" + }, + "dependencies": { + "filesize": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz", + "integrity": "sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg==", + "dev": true + }, + "handlebars": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "pretty-ms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-5.1.0.tgz", + "integrity": "sha512-4gaK1skD2gwscCfkswYQRmddUb2GJZtzDGRjHWadVHtK/DIKFufa12MvES6/xu1tVbUYeia5bmLcwJtZJQUqnw==", + "dev": true, + "requires": { + "parse-ms": "^2.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node-oauth1": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-oauth1/-/node-oauth1-1.3.0.tgz", + "integrity": "sha512-0yggixNfrA1KcBwvh/Hy2xAS1Wfs9dcg6TdFf2zN7gilcAigMdrtZ4ybrBSXBgLvGDw9V1p2MRnGBMq7XjTWLg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } }, "nyc": { "version": "14.1.1", @@ -3513,6 +4174,12 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "dev": true + }, "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", @@ -3583,6 +4250,30 @@ "mimic-fn": "^1.0.0" } }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -3655,6 +4346,12 @@ "json-parse-better-errors": "^1.0.1" } }, + "parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3809,6 +4506,309 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, + "postman-collection": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.0.0.tgz", + "integrity": "sha512-vDrXG/dclSu6RMqPqBz4ZqoQBwcj/a80sJYsQZmzWJ6dWgXiudPhwu6Vm3C1Hy7zX5W8A6am1Z6vb/TB4eyURA==", + "dev": true, + "requires": { + "faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mime-format": "2.0.1", + "mime-types": "2.1.31", + "postman-url-encoder": "3.0.1", + "semver": "7.3.5", + "uuid": "8.3.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "mime-db": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "dev": true, + "requires": { + "mime-db": "1.48.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "postman-collection-transformer": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.2.tgz", + "integrity": "sha512-3mRM18QALmasbza1LznlwQkymAYM0H79VY1LkR1BUAe8B7hwELofltMNSmpDn27MPGf0w9rPJJOcRSI3MWCCZg==", + "dev": true, + "requires": { + "commander": "7.2.0", + "inherits": "2.0.4", + "intel": "1.2.0", + "lodash": "4.17.21", + "semver": "7.3.5", + "strip-json-comments": "3.1.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "postman-request": { + "version": "2.88.1-postman.30", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.30.tgz", + "integrity": "sha512-zsGvs8OgNeno1Q44zTgGP2IL7kCqUy4DAtl8/ms0AQpqkIoysrxzR/Zg4kM1Kz8/duBvwxt8NN717wB7SMNm6w==", + "dev": true, + "requires": { + "@postman/form-data": "~3.1.1", + "@postman/tunnel-agent": "^0.6.3", + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "brotli": "~1.3.2", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "har-validator": "~5.1.3", + "http-signature": "~1.3.1", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "stream-length": "^1.0.2", + "tough-cookie": "~2.5.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "http-signature": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.5.tgz", + "integrity": "sha512-NwoTQYSJoFt34jSBbwzDHDofoA61NGXzu6wXh95o1Ry62EnmKjXb/nR/RknLeZ3G/uGwrlKNY2z7uPt+Cdl7Tw==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.14.1" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + } + } + }, + "postman-runtime": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.28.2.tgz", + "integrity": "sha512-YjcJwipcOfDFMOTM1/z9t/UB1zhF3ONZuffGhaP+FMG9aDAH2J1moO+PuUbLIcddggWR9ljtDk+O3TBxBmmvtQ==", + "dev": true, + "requires": { + "async": "2.6.3", + "aws4": "1.11.0", + "eventemitter3": "4.0.7", + "handlebars": "4.7.7", + "http-reasons": "0.1.0", + "httpntlm": "1.7.7", + "inherits": "2.0.4", + "js-sha512": "0.8.0", + "lodash": "4.17.21", + "node-oauth1": "1.3.0", + "performance-now": "2.1.0", + "postman-collection": "4.0.0", + "postman-request": "2.88.1-postman.30", + "postman-sandbox": "4.0.3", + "postman-url-encoder": "3.0.1", + "resolve-from": "5.0.0", + "serialised-error": "1.1.3", + "tough-cookie": "3.0.1", + "uuid": "3.4.0" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "postman-sandbox": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.0.3.tgz", + "integrity": "sha512-FCPJsiyxCKU0Rtyz9SnXXCzR54LUlln8u913JHyt0SkqMEbM/Y0qe+6m8zRt/SPxx+rzAZOqWfrCk5oiDyuTsg==", + "dev": true, + "requires": { + "lodash": "4.17.21", + "teleport-javascript": "1.0.0", + "uvm": "2.0.2" + }, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + } + } + }, + "postman-url-encoder": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.1.tgz", + "integrity": "sha512-dMPqXnkDlstM2Eya+Gw4MIGWEan8TzldDcUKZIhZUsJ/G5JjubfQPhFhVWKzuATDMvwvrWbSjF+8VmAvbu6giw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, "precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", @@ -3820,6 +4820,15 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, + "requires": { + "parse-ms": "^2.1.0" + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4164,6 +5173,25 @@ } } }, + "serialised-error": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/serialised-error/-/serialised-error-1.1.3.tgz", + "integrity": "sha512-vybp3GItaR1ZtO2nxZZo8eOo7fnVaNtP3XE2vJKgzkKR2bagCkdJ1EpYYhEMd3qu/80DwQk9KjsNSxE3fXWq0g==", + "dev": true, + "requires": { + "object-hash": "^1.1.2", + "stack-trace": "0.0.9", + "uuid": "^3.0.0" + }, + "dependencies": { + "stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=", + "dev": true + } + } + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -4367,11 +5395,34 @@ "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==" }, + "stream-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", + "integrity": "sha1-gnfzy+5JpNqrz9tOL0qbXp8snwA=", + "dev": true, + "requires": { + "bluebird": "^2.6.2" + }, + "dependencies": { + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", + "dev": true + } + } + }, "streamsearch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" }, + "strftime": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.0.tgz", + "integrity": "sha1-s/D6QZKVICpaKJ9ta+n0kJphcZM=", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -4546,6 +5597,12 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, + "symbol": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/symbol/-/symbol-0.3.1.tgz", + "integrity": "sha1-tvmpANSWpX8CQI8iGYwQndoGMEE=", + "dev": true + }, "symbol-observable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", @@ -4597,9 +5654,114 @@ } } }, + "tc-api-testing-lib": { + "version": "git+https://github.com/mfrank37/api-automated-testing.git#e4758eeaf434301320a80fef8be4b9f62dad93d7", + "from": "git+https://github.com/mfrank37/api-automated-testing.git", + "dev": true, + "requires": { + "axios": "^0.21.1", + "config": "^3.3.6", + "handlebars": "^4.7.7", + "joi": "^17.4.0", + "lodash": "^4.17.21", + "newman": "^5.2.3", + "newman-reporter-html": "^1.0.5", + "tc-core-library-js": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", + "winston": "^3.3.3" + }, + "dependencies": { + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dev": true, + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "config": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/config/-/config-3.3.6.tgz", + "integrity": "sha512-Hj5916C5HFawjYJat1epbyY2PlAgLpBtDUlr0MxGLgo3p5+7kylyvnRY18PqJHgnNWXcdd0eWDemT7eYWuFgwg==", + "dev": true, + "requires": { + "json5": "^2.1.1" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "dev": true + }, + "joi": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.1.tgz", + "integrity": "sha512-gDPOwQ5sr+BUxXuPDGrC1pSNcVR/yGGcTI0aCnjYxZEa3za60K/iCQ+OFIkEHWZGVCUcUlXlFKvMmrlmxrG6UQ==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.0", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "tc-core-library-js": { + "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", + "from": "github:appirio-tech/tc-core-library-js#v2.6.4", + "dev": true, + "requires": { + "axios": "^0.19.0", + "bunyan": "^1.8.12", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^1.6.0", + "lodash": "^4.17.15", + "millisecond": "^0.1.2", + "r7insight_node": "^1.8.4", + "request": "^2.88.0" + }, + "dependencies": { + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + } + } + } + } + } + }, "tc-core-library-js": { - "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", - "from": "github:appirio-tech/tc-core-library-js#v2.6.4", + "version": "github:appirio-tech/tc-core-library-js#081138e1f5eae76171abeff34b8f326b3fb2b504", + "from": "github:appirio-tech/tc-core-library-js#v2.6.5", "requires": { "axios": "^0.19.0", "bunyan": "^1.8.12", @@ -4611,6 +5773,12 @@ "request": "^2.88.0" } }, + "teleport-javascript": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/teleport-javascript/-/teleport-javascript-1.0.0.tgz", + "integrity": "sha512-j1llvWVFyEn/6XIFDfX5LAU43DXe0GCt3NfXDwJ8XpRRMkS+i50SAkonAONBy+vxwPFBd50MFU8a2uj8R/ccLg==", + "dev": true + }, "test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -4684,7 +5852,7 @@ "joi": "^13.4.0", "lodash": "^4.17.10", "superagent": "^3.8.3", - "tc-core-library-js": "github:appirio-tech/tc-core-library-js#v2.6" + "tc-core-library-js": "github:appirio-tech/tc-core-library-js#d16413db30b1eed21c0cf426e185bedb2329ddab" }, "dependencies": { "axios": { @@ -4802,6 +5970,37 @@ "mime-types": "~2.1.24" } }, + "uglify-js": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", + "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "dev": true, + "optional": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + } + } + }, + "underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", + "dev": true + }, "unfetch": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", @@ -4847,6 +6046,12 @@ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" }, + "utcstring": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/utcstring/-/utcstring-0.1.0.tgz", + "integrity": "sha1-Qw/VEKt/yVtdWRDJAteYgMIIQ2s=", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4862,6 +6067,15 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, + "uvm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.0.2.tgz", + "integrity": "sha512-Ra+aPiS5GXAbwXmyNExqdS42sTqmmx4XWEDF8uJlsTfOkKf9Rd9xNgav1Yckv4HfVEZg4iOFODWHFYuJ+9Fzfg==", + "dev": true, + "requires": { + "flatted": "3.1.1" + } + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4896,6 +6110,18 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -4959,6 +6185,12 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", diff --git a/package.json b/package.json index b83a69a..174f141 100644 --- a/package.json +++ b/package.json @@ -15,19 +15,23 @@ "view-db-data": "node src/scripts/view-db-data.js", "view-es-data": "node src/scripts/view-es-data.js", "test": "mocha --require test/prepare.js -t 20000 test/unit/*.test.js --exit", - "e2e": "mocha --require test/prepare.js -t 20000 test/e2e/*.test.js --exit", "test:cov": "nyc --reporter=html --reporter=text npm test", - "e2e:cov": "nyc --reporter=html --reporter=text npm run e2e" + "start:test": "cross-env NODE_ENV=test node app.js", + "mock-api": "cross-env NODE_ENV=test node mock-api/mock-api", + "test:newman": "cross-env NODE_ENV=test node test/postman/newman.js", + "test:newman:clear": "cross-env NODE_ENV=test node test/postman/clearTestData.js" }, "author": "TCSCODER", "license": "none", "devDependencies": { "chai": "^4.2.0", "chai-http": "^4.2.1", + "cross-env": "^7.0.3", "mocha": "^6.1.4", "mocha-prepare": "^0.1.0", "nyc": "^14.0.0", - "standard": "^12.0.1" + "standard": "^12.0.1", + "tc-api-testing-lib": "git+https://github.com/mfrank37/api-automated-testing.git" }, "dependencies": { "aws-sdk": "^2.466.0", @@ -35,6 +39,7 @@ "body-parser": "^1.15.1", "config": "^3.0.1", "cors": "^2.7.1", + "dotenv": "^10.0.0", "dynamoose": "^1.8.0", "elasticsearch": "^16.1.1", "express": "^4.15.4", @@ -46,12 +51,12 @@ "joi": "^14.0.0", "jsonwebtoken": "^8.3.0", "lodash": "^4.17.19", + "moment": "^2.27.0", + "request": "^2.88.2", "tc-core-library-js": "github:appirio-tech/tc-core-library-js.git#v2.6.5", "topcoder-bus-api-wrapper": "^1.0.1", "uuid": "^3.3.2", - "winston": "^3.1.0", - "request": "^2.88.2", - "moment": "^2.27.0" + "winston": "^3.1.0" }, "standard": { "env": [ diff --git a/src/common/eshelper.js b/src/common/eshelper.js index 53e9fff..f6a1d92 100644 --- a/src/common/eshelper.js +++ b/src/common/eshelper.js @@ -3,15 +3,14 @@ */ const _ = require('lodash') const config = require('config') -const helper = require('../common/helper') -const { response } = require('express') +require('../common/helper') /** * Fetch members profile form ES * @param {Object} query the HTTP request query * @returns {Object} members and total */ -async function getMembers(query, esClient, currentUser) { +async function getMembers (query, esClient, currentUser) { const handles = _.isArray(query.handles) ? query.handles : [] const handlesLower = _.isArray(query.handlesLower) ? query.handlesLower : [] var userIds = _.isArray(query.userIds) ? query.userIds : [] @@ -44,7 +43,7 @@ async function getMembers(query, esClient, currentUser) { if (handles.length > 0) { boolQueryMembers.push({ query: { terms: { handle: handles } } }) } - boolQueryMembers.push({ match_phrase: { status: "ACTIVE" } }) + boolQueryMembers.push({ match_phrase: { status: 'ACTIVE' } }) if (boolQueryMembers.length > 0) { esQueryMembers.body.query = { bool: { @@ -62,7 +61,7 @@ async function getMembers(query, esClient, currentUser) { * @param {Object} query the HTTP request query * @returns {Object} members skills */ -async function getMembersSkills(query, esClient) { +async function getMembersSkills (query, esClient) { // construct ES query for skills const esQuerySkills = { index: config.get('ES.MEMBER_SKILLS_ES_INDEX'), @@ -74,7 +73,7 @@ async function getMembersSkills(query, esClient) { const boolQuerySkills = [] if (query.handlesLower) { - boolQuerySkills.push({ query: { terms: { handleLower: query.handlesLower } } }) + boolQuerySkills.push({ terms: { handleLower: query.handlesLower } }) } esQuerySkills.body.query = { bool: { @@ -91,7 +90,7 @@ async function getMembersSkills(query, esClient) { * @param {Object} query the HTTP request query * @returns {Object} members stats */ -async function getMembersStats(query, esClient) { +async function getMembersStats (query, esClient) { // construct ES query for stats const esQueryStats = { index: config.get('ES.MEMBER_STATS_ES_INDEX'), @@ -102,7 +101,7 @@ async function getMembersStats(query, esClient) { } const boolQueryStats = [] if (query.handlesLower) { - boolQueryStats.push({ query: { terms: { handleLower: query.handlesLower } } }) + boolQueryStats.push({ terms: { handleLower: query.handlesLower } }) boolQueryStats.push({ match_phrase: { groupId: 10 } }) } esQueryStats.body.query = { @@ -120,7 +119,7 @@ async function getMembersStats(query, esClient) { * @param {Object} query the HTTP request query * @returns {Object} suggestion */ -async function getSuggestion(query, esClient, currentUser) { +async function getSuggestion (query, esClient, currentUser) { // construct ES query for members profile suggestion let esSuggestionMembers = { index: config.get('ES.MEMBER_PROFILE_ES_INDEX'), @@ -131,11 +130,11 @@ async function getSuggestion(query, esClient, currentUser) { } if (query.term) { esSuggestionMembers.body.suggest = { - "handle-suggestion": { + 'handle-suggestion': { text: query.term, completion: { size: query.size, - field: "handleSuggest" + field: 'handleSuggest' } } } @@ -150,8 +149,8 @@ async function getSuggestion(query, esClient, currentUser) { * @param {Object} docs the HTTP request query * @returns {Object} total */ -function getTotal(docs) { - const total = docs.hits.total +function getTotal (docs) { + let total = docs.hits.total if (_.isObject(total)) { total = total.value || 0 } diff --git a/src/common/helper.js b/src/common/helper.js index ef644d1..702acb7 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -9,27 +9,27 @@ const AWS = require('aws-sdk') const config = require('config') const busApi = require('topcoder-bus-api-wrapper') const elasticsearch = require('elasticsearch') -const uuid = require('uuid/v4') +// const uuid = require('uuid/v4') const querystring = require('querystring') -const request = require('request'); +const request = require('request') // Color schema for Ratings const RATING_COLORS = [{ color: '#9D9FA0' /* Grey */, - limit: 900, + limit: 900 }, { color: '#69C329' /* Green */, - limit: 1200, + limit: 1200 }, { color: '#616BD5' /* Blue */, - limit: 1500, + limit: 1500 }, { color: '#FCD617' /* Yellow */, - limit: 2200, + limit: 2200 }, { color: '#EF3A3A' /* Red */, - limit: Infinity, -}]; + limit: Infinity +}] // Bus API Client let busApiClient @@ -45,6 +45,11 @@ if (config.AMAZON.AWS_ACCESS_KEY_ID && config.AMAZON.AWS_SECRET_ACCESS_KEY) { awsConfig.accessKeyId = config.AMAZON.AWS_ACCESS_KEY_ID awsConfig.secretAccessKey = config.AMAZON.AWS_SECRET_ACCESS_KEY } +if (config.AMAZON.IS_LOCAL_S3) { + awsConfig.endpoint = config.S3_ENDPOINT + awsConfig.s3ForcePathStyle = true + awsConfig.signatureVersion = 'v4' +} AWS.config.update(awsConfig) const s3 = new AWS.S3() @@ -168,9 +173,9 @@ async function getEntityByHashKey (handle, modelName, hashKeyName, value, throwE */ async function getEntityByHashRangeKey (handle, modelName, hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue, throwError) { return new Promise((resolve, reject) => { - var param = {}; - param[hashKeyName] = hashKeyValue; - param[rangeKeyName] = rangeKeyValue; + var param = {} + param[hashKeyName] = hashKeyValue + param[rangeKeyName] = rangeKeyValue models[modelName].get(param, function (err, result) { if (err) { @@ -184,7 +189,7 @@ async function getEntityByHashRangeKey (handle, modelName, hashKeyName, hashKeyV return resolve({}) } } - ); + ) }) } @@ -216,16 +221,20 @@ async function getMemberByHandle (handle) { */ async function create (modelName, data) { const dbItem = new models[modelName](data) - if (dbItem.hasOwnProperty("traits")) { - if (typeof dbItem.traits == "object") { - dbItem.traits = JSON.stringify(dbItem.traits) + _.each(['traits', 'addresses', 'skills', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE'], property => { + if (dbItem.hasOwnProperty(property)) { + if (typeof dbItem[property] === 'object') { + dbItem[property] = JSON.stringify(dbItem[property]) + } } - } + }) var result = await itemSave(dbItem) - if (result.hasOwnProperty("traits")) { - result.traits = JSON.parse(result.traits) - result.originalItem().traits = JSON.parse(result.originalItem().traits) - } + _.each(['traits', 'addresses', 'skills', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE'], property => { + if (result.hasOwnProperty(property)) { + result[property] = JSON.parse(result[property]) + result.originalItem()[property] = JSON.parse(result.originalItem()[property]) + } + }) return result } @@ -239,31 +248,31 @@ async function update (dbItem, data) { Object.keys(data).forEach((key) => { dbItem[key] = data[key] }) - if (dbItem.hasOwnProperty("addresses")) { - if (typeof dbItem.addresses == "object") { + if (dbItem.hasOwnProperty('addresses')) { + if (typeof dbItem.addresses === 'object') { dbItem.addresses = JSON.stringify(dbItem.addresses) } } - if (dbItem.hasOwnProperty("traits")) { - if (typeof dbItem.traits == "object") { + if (dbItem.hasOwnProperty('traits')) { + if (typeof dbItem.traits === 'object') { dbItem.traits = JSON.stringify(dbItem.traits) } } - if (dbItem.hasOwnProperty("skills")) { - if (typeof dbItem.skills == "object") { + if (dbItem.hasOwnProperty('skills')) { + if (typeof dbItem.skills === 'object') { dbItem.skills = JSON.stringify(dbItem.skills) } } var result = await itemSave(dbItem) - if (result.hasOwnProperty("addresses")) { + if (result.hasOwnProperty('addresses')) { result.addresses = JSON.parse(result.addresses) result.originalItem().addresses = JSON.parse(result.originalItem().addresses) } - if (result.hasOwnProperty("traits")) { + if (result.hasOwnProperty('traits')) { result.traits = JSON.parse(result.traits) result.originalItem().traits = JSON.parse(result.originalItem().traits) } - if (result.hasOwnProperty("skills")) { + if (result.hasOwnProperty('skills')) { result.skills = JSON.parse(result.skills) result.originalItem().skills = JSON.parse(result.originalItem().skills) } @@ -491,26 +500,26 @@ function canManageMember (currentUser, member) { function cleanUpStatistics (stats, fields) { // cleanup - convert string to object - for (count = 0; count < stats.length; count++) { - if (stats[count].hasOwnProperty("maxRating")) { - if (typeof stats[count].maxRating == "string") { + for (let count = 0; count < stats.length; count++) { + if (stats[count].hasOwnProperty('maxRating')) { + if (typeof stats[count].maxRating === 'string') { stats[count].maxRating = JSON.parse(stats[count].maxRating) } // set the rating color stats[count].maxRating.ratingColor = this.getRatingColor(stats[count].maxRating.rating) } - if (stats[count].hasOwnProperty("DATA_SCIENCE")) { - if (typeof stats[count].DATA_SCIENCE == "string") { + if (stats[count].hasOwnProperty('DATA_SCIENCE')) { + if (typeof stats[count].DATA_SCIENCE === 'string') { stats[count].DATA_SCIENCE = JSON.parse(stats[count].DATA_SCIENCE) } } - if (stats[count].hasOwnProperty("DESIGN")) { - if (typeof stats[count].DESIGN == "string") { + if (stats[count].hasOwnProperty('DESIGN')) { + if (typeof stats[count].DESIGN === 'string') { stats[count].DESIGN = JSON.parse(stats[count].DESIGN) } } - if (stats[count].hasOwnProperty("DEVELOP")) { - if (typeof stats[count].DEVELOP == "string") { + if (stats[count].hasOwnProperty('DEVELOP')) { + if (typeof stats[count].DEVELOP === 'string') { stats[count].DEVELOP = JSON.parse(stats[count].DEVELOP) } } @@ -523,8 +532,8 @@ function cleanUpStatistics (stats, fields) { } function convertToObjectSkills (skill) { - if (skill.hasOwnProperty("skills")) { - if (typeof skill.skills == "string") { + if (skill.hasOwnProperty('skills')) { + if (typeof skill.skills === 'string') { skill.skills = JSON.parse(skill.skills) } } @@ -532,16 +541,16 @@ function convertToObjectSkills (skill) { } function cleanupSkills (memberEnteredSkill, member) { - if (memberEnteredSkill.hasOwnProperty("userHandle")) { + if (memberEnteredSkill.hasOwnProperty('userHandle')) { memberEnteredSkill.handle = memberEnteredSkill.userHandle } - if (!memberEnteredSkill.hasOwnProperty("userId")) { + if (!memberEnteredSkill.hasOwnProperty('userId')) { memberEnteredSkill.userId = member.userId } - if (!memberEnteredSkill.hasOwnProperty("handle")) { + if (!memberEnteredSkill.hasOwnProperty('handle')) { memberEnteredSkill.handle = member.handle } - if (!memberEnteredSkill.hasOwnProperty("handleLower")) { + if (!memberEnteredSkill.hasOwnProperty('handleLower')) { memberEnteredSkill.handleLower = member.handleLower } return memberEnteredSkill @@ -549,17 +558,17 @@ function cleanupSkills (memberEnteredSkill, member) { function mergeSkills (memberEnteredSkill, memberAggregatedSkill, allTags) { // process skills in member entered skill - if (memberEnteredSkill.hasOwnProperty("skills")) { - var tempSkill = {} + if (memberEnteredSkill.hasOwnProperty('skills')) { + let tempSkill = {} _.forIn(memberEnteredSkill.skills, (value, key) => { if (!value.hidden) { var tag = this.findTagById(allTags, Number(key)) - if(tag) { + if (tag) { value.tagName = tag.name - if (!value.hasOwnProperty("sources")) { + if (!value.hasOwnProperty('sources')) { value.sources = [ 'USER_ENTERED' ] } - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = 0 } tempSkill[key] = value @@ -573,8 +582,8 @@ function mergeSkills (memberEnteredSkill, memberAggregatedSkill, allTags) { memberEnteredSkill.skills = tempSkill } else { // process skills in member aggregated skill - if (memberAggregatedSkill.hasOwnProperty("skills")) { - var tempSkill = {} + if (memberAggregatedSkill.hasOwnProperty('skills')) { + let tempSkill = {} memberEnteredSkill.skills = mergeAggregatedSkill(memberAggregatedSkill, allTags, tempSkill) } else { memberEnteredSkill.skills = {} @@ -588,12 +597,12 @@ function mergeAggregatedSkill (memberAggregatedSkill, allTags, tempSkill) { var value = memberAggregatedSkill.skills[key] if (!value.hidden) { var tag = findTagById(allTags, Number(key)) - if(tag) { - if (value.hasOwnProperty("sources")) { - if(value.sources.includes("CHALLENGE")) { + if (tag) { + if (value.hasOwnProperty('sources')) { + if (value.sources.includes('CHALLENGE')) { if (tempSkill[key]) { value.tagName = tag.name - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = tempSkill[key].score } else { if (value.score <= tempSkill[key].score) { @@ -603,7 +612,7 @@ function mergeAggregatedSkill (memberAggregatedSkill, allTags, tempSkill) { value.sources.push(tempSkill[key].sources[0]) } else { value.tagName = tag.name - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = 0 } } @@ -616,7 +625,7 @@ function mergeAggregatedSkill (memberAggregatedSkill, allTags, tempSkill) { return tempSkill } -async function getAllTags(url) { +async function getAllTags (url) { return new Promise(function (resolve, reject) { request({ url: url }, function (error, response, body) { @@ -624,24 +633,24 @@ async function getAllTags(url) { reject(new errors.NotFoundError(`Tags not found. ` + error)) } var allTags = JSON.parse(body) - resolve(allTags.result.content); + resolve(allTags.result.content) } - ); + ) }) } function findTagById (data, id) { - return _.find(data, { 'id': id }); + return _.find(data, { 'id': id }) } -function getRatingColor(rating) { - let i = 0; const r = Number(rating); - while (RATING_COLORS[i].limit <= r) i += 1; - return RATING_COLORS[i].color || 'black'; +function getRatingColor (rating) { + let i = 0; const r = Number(rating) + while (RATING_COLORS[i].limit <= r) i += 1 + return RATING_COLORS[i].color || 'black' } -function paginate(array, page_size, page_number) { - return array.slice(page_number * page_size, page_number * page_size + page_size); +function paginate (array, pageSize, pageNumber) { + return array.slice(pageNumber * pageSize, pageNumber * pageSize + pageSize) } module.exports = { diff --git a/src/common/logger.js b/src/common/logger.js index 18289c4..c70a1ef 100644 --- a/src/common/logger.js +++ b/src/common/logger.js @@ -45,7 +45,7 @@ logger.logFullError = (err, signature) => { * @param {Object} obj the object * @returns {Object} the new object with removed properties * @private - */ + *//* const _sanitizeObject = (obj) => { try { return JSON.parse(JSON.stringify(obj, (name, value) => { @@ -58,7 +58,7 @@ const _sanitizeObject = (obj) => { return obj } } - +*/ /** * Convert array with arguments to object * @param {Array} params the name of parameters @@ -82,7 +82,7 @@ logger.decorateWithLogging = (service) => { return } _.each(service, (method, name) => { - const params = method.params || getParams(method) + // const params = method.params || getParams(method) service[name] = async function () { logger.debug(`ENTER ${name}`) // logger.debug('input arguments') diff --git a/src/controllers/CleanUpController.js b/src/controllers/CleanUpController.js new file mode 100644 index 0000000..c218573 --- /dev/null +++ b/src/controllers/CleanUpController.js @@ -0,0 +1,19 @@ +/** + * Controller for cleaning up test data + */ + +const service = require('../services/CleanUpService') + +/** + * Clear the postman test data + * @param {Object} req the request + * @param {Object} res the response + */ +async function cleanUpTestData (req, res) { + await service.cleanUpTestData(req.authUser) + res.status(200).end() +} + +module.exports = { + cleanUpTestData +} diff --git a/src/init-db.js b/src/init-db.js index 9af0204..837a6dd 100644 --- a/src/init-db.js +++ b/src/init-db.js @@ -12,24 +12,32 @@ const initDB = async () => { for (const item of members) { await item.delete() } - const memberStats = await helper.scan('MemberStats') - for (const item of memberStats) { - await item.delete() - } - const memberHistoryStats = await helper.scan('MemberHistoryStats') - for (const item of memberHistoryStats) { + const memberAggregatedSkills = await helper.scan('MemberAggregatedSkills') + for (const item of memberAggregatedSkills) { await item.delete() } const memberDistributionStats = await helper.scan('MemberDistributionStats') for (const item of memberDistributionStats) { await item.delete() } + const memberEnteredSkills = await helper.scan('MemberEnteredSkills') + for (const item of memberEnteredSkills) { + await item.delete() + } const memberFinancials = await helper.scan('MemberFinancial') for (const item of memberFinancials) { await item.delete() } - const memberSkills = await helper.scan('MemberSkill') - for (const item of memberSkills) { + const memberHistoryStats = await helper.scan('MemberHistoryStats') + for (const item of memberHistoryStats) { + await item.delete() + } + const memberHistoryStatsPrivate = await helper.scan('MemberHistoryStatsPrivate') + for (const item of memberHistoryStatsPrivate) { + await item.delete() + } + const memberStats = await helper.scan('MemberStats') + for (const item of memberStats) { await item.delete() } const traits = await helper.scan('MemberTrait') diff --git a/src/init-es.js b/src/init-es.js index ab15f35..301845c 100644 --- a/src/init-es.js +++ b/src/init-es.js @@ -26,6 +26,18 @@ const initES = async () => { } catch (err) { // ignore } + logger.info(`Delete index ${config.ES.MEMBER_STATS_ES_INDEX} if any.`) + try { + await client.indices.delete({ index: config.ES.MEMBER_STATS_ES_INDEX }) + } catch (err) { + // ignore + } + logger.info(`Delete index ${config.ES.MEMBER_SKILLS_ES_INDEX} if any.`) + try { + await client.indices.delete({ index: config.ES.MEMBER_SKILLS_ES_INDEX }) + } catch (err) { + // ignore + } } let exists = await client.indices.exists({ index: config.ES.MEMBER_PROFILE_ES_INDEX }) @@ -40,7 +52,8 @@ const initES = async () => { handleLower: { type: 'keyword' }, handle: { type: 'keyword' }, userId: { type: 'keyword' }, - status: { type: 'keyword' } + status: { type: 'keyword' }, + handleSuggest: { type: 'completion' } } } @@ -68,6 +81,47 @@ const initES = async () => { body }) } + exists = await client.indices.exists({ index: config.ES.MEMBER_STATS_ES_INDEX }) + if (exists) { + logger.info(`The index ${config.ES.MEMBER_STATS_ES_INDEX} exists.`) + } else { + logger.info(`The index ${config.ES.MEMBER_STATS_ES_INDEX} will be created.`) + + const body = { mappings: {} } + body.mappings[config.get('ES.MEMBER_STATS_ES_TYPE')] = { + properties: { + handleLower: { type: 'keyword' }, + handle: { type: 'keyword' }, + groupId: { type: 'keyword' }, + userId: { type: 'keyword' } + } + } + + await client.indices.create({ + index: config.ES.MEMBER_STATS_ES_INDEX, + body + }) + } + exists = await client.indices.exists({ index: config.ES.MEMBER_SKILLS_ES_INDEX }) + if (exists) { + logger.info(`The index ${config.ES.MEMBER_SKILLS_ES_INDEX} exists.`) + } else { + logger.info(`The index ${config.ES.MEMBER_SKILLS_ES_INDEX} will be created.`) + + const body = { mappings: {} } + body.mappings[config.get('ES.MEMBER_SKILLS_ES_TYPE')] = { + properties: { + handleLower: { type: 'keyword' }, + userHandle: { type: 'keyword' }, + userId: { type: 'keyword' } + } + } + + await client.indices.create({ + index: config.ES.MEMBER_SKILLS_ES_INDEX, + body + }) + } } initES().then(() => { diff --git a/src/models/Member.js b/src/models/Member.js index 2099f3d..2f6435e 100644 --- a/src/models/Member.js +++ b/src/models/Member.js @@ -25,10 +25,10 @@ const schema = new Schema({ type: String, required: false, index: [ - { - global: true, - name: 'email-index' - }] + { + global: true, + name: 'email-index' + }] }, maxRating: { type: Object, diff --git a/src/models/MemberStats.js b/src/models/MemberStats.js index 2596d9e..ad21ae5 100644 --- a/src/models/MemberStats.js +++ b/src/models/MemberStats.js @@ -21,7 +21,7 @@ const schema = new Schema({ required: true }, maxRating: { - type: String, + type: Object, required: false }, challenges: { @@ -69,4 +69,4 @@ const schema = new Schema({ throughput: { read: 4, write: 2 } }) -module.exports = schema \ No newline at end of file +module.exports = schema diff --git a/src/models/MemberStatsPrivate.js b/src/models/MemberStatsPrivate.js index 002def2..6395084 100644 --- a/src/models/MemberStatsPrivate.js +++ b/src/models/MemberStatsPrivate.js @@ -26,7 +26,7 @@ const schema = new Schema({ required: true }, maxRating: { - type: String, + type: Object, required: false }, challenges: { @@ -74,4 +74,4 @@ const schema = new Schema({ throughput: { read: 4, write: 2 } }) -module.exports = schema \ No newline at end of file +module.exports = schema diff --git a/src/routes.js b/src/routes.js index cfe99ec..08d075e 100644 --- a/src/routes.js +++ b/src/routes.js @@ -147,5 +147,13 @@ module.exports = { allowNoToken: true, scopes: [MEMBERS.READ, MEMBERS.ALL] } + }, + '/internal/jobs/clean': { + post: { + controller: 'CleanUpController', + method: 'cleanUpTestData', + auth: 'jwt', + scopes: [MEMBERS.ALL] + } } } diff --git a/src/scripts/seed-data.js b/src/scripts/seed-data.js index ff2ce5c..9825aeb 100644 --- a/src/scripts/seed-data.js +++ b/src/scripts/seed-data.js @@ -22,15 +22,10 @@ const members = [{ lastName: 'last name', description: 'desc', otherLangName: 'en', - handle: 'denis', - handleLower: 'denis', - status: 'active', + handle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', + status: 'ACTIVE', email: 'denis@topcoder.com', - newEmail: 'denis2@topcoder.com', - emailVerifyToken: 'abcdefg', - emailVerifyTokenDate: '2028-02-06T07:38:50.088Z', - newEmailVerifyToken: 'abc123123', - newEmailVerifyTokenDate: '2028-02-06T07:38:50.088Z', addresses: [ { streetAddr1: 'addr1', @@ -64,15 +59,10 @@ const members = [{ lastName: 'last name 2', description: 'desc 2', otherLangName: 'en', - handle: 'testing', - handleLower: 'testing', - status: 'active', + handle: 'POSTMANE2E-testing', + handleLower: 'postmane2e-testing', + status: 'ACTIVE', email: 'testing@topcoder.com', - newEmail: 'testing2@topcoder.com', - emailVerifyToken: 'abcdefg', - emailVerifyTokenDate: '2028-02-06T07:38:50.088Z', - newEmailVerifyToken: 'abc123123', - newEmailVerifyTokenDate: '2028-02-06T07:38:50.088Z', addresses: [ { streetAddr1: 'addr1', @@ -98,8 +88,8 @@ const members = [{ }] const distribution1 = { - track: 'develop', - subTrack: 'code', + track: 'POSTMANE2E-DEVELOP', + subTrack: 'CODE', distribution: { ratingRange0To099: 3, ratingRange100To199: 5 @@ -111,7 +101,7 @@ const distribution1 = { } const distribution2 = { - track: 'develop', + track: 'POSTMANE2E-DEVELOP', subTrack: 'F2F', distribution: { ratingRange0To099: 8, @@ -125,8 +115,61 @@ const distribution2 = { const historyStats = { userId: 123, - handle: 'denis', - handleLower: 'denis', + handle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', + DEVELOP: { + subTracks: [ + { + id: 1111, + name: 'name', + history: [ + { + challengeId: 789789, + challengeName: 'test', + ratingDate: '2020-02-15T14:04:22.544Z', + newRating: 1888 + } + ] + } + ] + }, + DATA_SCIENCE: { + SRM: { + history: [ + { + challengeId: 754545, + challengeName: 'test2', + date: '2020-02-15T14:04:22.544Z', + rating: 1565, + placement: 1, + percentile: 100 + } + ] + }, + MARATHON_MATCH: { + history: [ + { + challengeId: 121212, + challengeName: 'test3', + date: '2020-02-15T14:04:22.544Z', + rating: 1232, + placement: 2, + percentile: 80 + } + ] + } + }, + updatedAt: '2020-02-08T07:38:50.088Z', + createdAt: '2020-02-09T07:38:50.088Z', + createdBy: 'test1', + updatedBy: 'test2' +} + +const historyPrivateStats = { + userId: 123, + groupId: 20, + handle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', DEVELOP: { subTracks: [ { @@ -177,8 +220,8 @@ const historyStats = { const memberStats = { userId: 123, - handle: 'denis', - handleLower: 'denis', + handle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', maxRating: { rating: 1565, track: 'develop', @@ -186,7 +229,7 @@ const memberStats = { }, challenges: 10, wins: 8, - develop: { + DEVELOP: { challenges: 3, wins: 2, subTracks: [ @@ -233,7 +276,7 @@ const memberStats = { mostRecentEventDate: '2020-02-15T14:05:16.275Z', mostRecentSubmission: '2020-02-15T14:05:16.275Z' }, - design: { + DESIGN: { challenges: 1, wins: 2, subTracks: [ @@ -256,7 +299,181 @@ const memberStats = { mostRecentEventDate: '2020-02-15T14:05:16.275Z', mostRecentSubmission: '2020-02-15T14:05:16.275Z' }, - dataScience: { + DATA_SCIENCE: { + challenges: 10, + wins: 0, + srm: { + challenges: 1, + wins: 2, + rank: { + rating: 3, + percentile: 0, + rank: 1, + countryRank: 2, + schoolRank: 1, + volatility: 20, + maximumRating: 10, + minimumRating: 20, + defaultLanguage: 'EN', + competitions: 1, + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z' + }, + challengeDetails: [ + { + levelName: 'test', + challenges: 10, + failedChallenges: 20 + } + ], + division1: [ + { + levelName: 'level 1', + problemsSubmitted: 1, + problemsFailed: 2, + problemsSysByTest: 0 + } + ], + division2: [ + { + levelName: 'level 2', + problemsSubmitted: 1, + problemsFailed: 2, + problemsSysByTest: 0 + } + ], + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z', + mostRecentSubmission: '2020-02-15T14:05:16.276Z' + }, + marathonMatch: { + challenges: 1, + wins: 2, + rank: { + rating: 1, + competitions: 2, + avgRank: 1, + avgNumSubmissions: 0, + bestRank: 0, + topFiveFinishes: 0, + topTenFinishes: 0, + rank: 10, + percentile: 20, + volatility: 10, + minimumRating: 20, + maximumRating: 10, + countryRank: 20, + schoolRank: 10, + defaultLanguage: 'test', + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z' + }, + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z', + mostRecentSubmission: '2020-02-15T14:05:16.276Z' + }, + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z', + mostRecentSubmission: '2020-02-15T14:05:16.276Z' + }, + copilot: { + contests: 10, + projects: 20, + failures: 10, + reposts: 20, + activeContests: 10, + activeProjects: 30, + fulfillment: 40 + }, + updatedAt: '2020-02-08T07:38:50.088Z', + createdAt: '2020-02-09T07:38:50.088Z', + createdBy: 'test1', + updatedBy: 'test2' +} + +const memberPrivateStats = { + userId: 123, + groupId: 20, + handle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', + maxRating: { + rating: 1565, + track: 'develop', + subTrack: 'code' + }, + challenges: 10, + wins: 8, + DEVELOP: { + challenges: 3, + wins: 2, + subTracks: [ + { + id: 11111, + name: 'test1', + challenges: 20, + wins: 3, + rank: { + rating: 1212, + activePercentile: 80, + activeRank: 1, + activeCountryRank: 2, + activeSchoolRank: 1, + overallPercentile: 10, + overallRank: 2, + overallCountryRank: 1, + overallSchoolRank: 1, + volatility: 60, + reliability: 80, + maxRating: 1999, + minRating: 1200 + }, + submissions: { + numInquiries: 1, + submissions: 2, + submissionRate: 3, + passedScreening: 1, + screeningSuccessRate: 2, + passedReview: 3, + reviewSuccessRate: 1, + appeals: 2, + appealSuccessRate: 3, + maxScore: 1, + minScore: 2, + avgScore: 3, + avgPlacement: 1, + winPercent: 2 + }, + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + } + ], + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + }, + DESIGN: { + challenges: 1, + wins: 2, + subTracks: [ + { + id: 1, + name: 'test', + numInquiries: 1, + challenges: 2, + wins: 3, + winPercent: 1, + avgPlacement: 2, + submissions: 3, + submissionRate: 1, + passedScreening: 2, + screeningSuccessRate: 3, + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + } + ], + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + }, + DATA_SCIENCE: { challenges: 10, wins: 0, srm: { @@ -358,10 +575,10 @@ const memberFinancial = { updatedBy: 'test2' } -const memberSkills = { +const memberAggregatedSkills = { userId: 123, - handle: 'denis', - handleLower: 'denis', + handle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', skills: { Java: { tagName: 'code', @@ -376,12 +593,50 @@ const memberSkills = { sources: ['source3'] } }, - updatedAt: '2020-02-08T07:38:50.088Z', - createdAt: '2020-02-09T07:38:50.088Z', + updatedAt: 1621895619502, + createdAt: 1621895619502, createdBy: 'test1', updatedBy: 'test2' } +const memberEnteredSkills = { + userId: 123, + userHandle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', + skills: { + 286: { + hidden: false, + score: 1888, + sources: ['source1', 'source2'] + }, + 380: { + hidden: true, + score: 1555, + sources: ['source3'] + }, + 311: { + hidden: false + } + }, + updatedAt: 1621895619502, + createdAt: 1621895619502, + createdBy: 'test1', + updatedBy: 'test2' +} + +const memberTraits = { + userId: 123, + traitId: 'basic_info', + categoryName: 'Subscription', + traits: { + data: [{ test: 'abc' }] + }, + createdAt: '2020-02-06T07:38:50.088Z', + updatedAt: '2020-02-07T07:38:50.088Z', + createdBy: 123456, + updatedBy: 123456 +} + async function seedData () { // create member data in DB and ES for (let i = 0; i < members.length; i += 1) { @@ -396,33 +651,38 @@ async function seedData () { body: member, refresh: 'true' // refresh ES so that it is visible for read operations instantly }) + await esClient.update({ + index: config.ES.MEMBER_PROFILE_ES_INDEX, + type: config.ES.MEMBER_PROFILE_ES_TYPE, + id: member.handleLower, + body: { + doc: { 'handleSuggest': { + 'input': [ 'post', 'postman' ], + 'weight': 34 } + } + }, + refresh: 'true' // refresh ES so that it is visible for read operations instantly + }) } // create member traits data in ES await esClient.create({ index: config.ES.MEMBER_TRAIT_ES_INDEX, type: config.ES.MEMBER_TRAIT_ES_TYPE, - id: '123basic_id', - body: { - userId: 123, - traitId: 'basic_id', - categoryName: 'Subscription', - traits: { - data: [{ test: 'abc' }] - }, - createdAt: '2020-02-06T07:38:50.088Z', - updatedAt: '2020-02-07T07:38:50.088Z', - createdBy: 'test1', - updatedBy: 'test2' - }, + id: '123_basic_info', + body: memberTraits, refresh: 'true' // refresh ES so that it is visible for read operations instantly }) // create DB data await helper.create('MemberDistributionStats', distribution1) await helper.create('MemberDistributionStats', distribution2) await helper.create('MemberHistoryStats', historyStats) + await helper.create('MemberHistoryStatsPrivate', historyPrivateStats) await helper.create('MemberStats', memberStats) + await helper.create('MemberStatsPrivate', memberPrivateStats) await helper.create('MemberFinancial', memberFinancial) - await helper.create('MemberSkill', memberSkills) + await helper.create('MemberAggregatedSkills', memberAggregatedSkills) + await helper.create('MemberEnteredSkills', memberEnteredSkills) + await helper.create('MemberTrait', memberTraits) } seedData() diff --git a/src/scripts/view-es-data.js b/src/scripts/view-es-data.js index 68f85ca..92d9cc3 100644 --- a/src/scripts/view-es-data.js +++ b/src/scripts/view-es-data.js @@ -12,13 +12,18 @@ if (process.argv.length <= 2) { process.exit(1) } const indexName = process.argv[2] - +const typeMapping = { + [config.get('ES.MEMBER_PROFILE_ES_INDEX')]: config.get('ES.MEMBER_PROFILE_ES_TYPE'), + [config.get('ES.MEMBER_TRAIT_ES_INDEX')]: config.get('ES.MEMBER_TRAIT_ES_TYPE'), + [config.get('ES.MEMBER_STATS_ES_INDEX')]: config.get('ES.MEMBER_STATS_ES_TYPE'), + [config.get('ES.MEMBER_SKILLS_ES_INDEX')]: config.get('ES.MEMBER_SKILLS_ES_TYPE') +} const esClient = helper.getESClient() async function showESData () { const result = await esClient.search({ index: indexName, - type: config.get('ES.MEMBER_PROFILE_ES_TYPE') // type name is same for all indices + type: typeMapping[indexName] // type name is same for all indices }) return result.hits.hits || [] } diff --git a/src/services/CleanUpService.js b/src/services/CleanUpService.js new file mode 100644 index 0000000..1e1f106 --- /dev/null +++ b/src/services/CleanUpService.js @@ -0,0 +1,111 @@ +/** + * This service provides operations to clean up the environment for running automated tests. + */ + +const _ = require('lodash') +const config = require('config') +const models = require('../models') +const helper = require('../common/helper') +const logger = require('../common/logger') +const errors = require('../common/errors') +const Joi = require('joi') + +/** + * Delete the record from database by the given criteria. + * @param model the model + * @param keys the keys criteria + * @returns {Promise} + */ +async function deleteFromDb (model, keys) { + if (_.isEmpty(keys)) { + return + } + await models[model].batchDelete(keys) + logger.info({ message: `${model} test data deleted from DB` }) +} + +/** + * Delete the record from database by the given criteria. + * @param model the model + * @param key the search key criteria + * @param value the search value criteria + * @returns {Promise} + */ +async function deleteFromDbByQuery (model, key, value) { + if (_.isUndefined(value)) { + return + } + const items = await models[model].query(key).eq(value).exec() + for (const item of items) { + await item.delete() + } + logger.info({ message: `${model} test data deleted from DB` }) +} + +/** + * Delete the record from elasticsearch by the given criteria. + * @param index the index name + * @param key the search key criteria + * @param value the search value criteria + * @param name the entity name + * @returns {Promise} + */ +async function deleteFromES (index, key, value, name) { + if (_.isEmpty(value)) { + return + } + const esClient = helper.getESClient() + const result = await esClient.deleteByQuery({ + index: index, + body: { + query: { + terms: { [key]: value } + } + } + }) + logger.info({ message: `${result.deleted}/${result.total} ${name}s deleted from ES` }) +} + +/** + * Clear the postman test data. The main function of this class. + * @param {Object} currentUser the user who perform this operation + * @returns {Promise} + */ +async function cleanUpTestData (currentUser) { + if (!helper.hasAdminRole(currentUser)) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') + } + logger.info({ message: 'clear the test data from postman test!' }) + + const memberDb = await models.Member.scan('handleLower').beginsWith(config.AUTOMATED_TESTING_NAME_PREFIX.toLowerCase()).attributes(['userId', 'handleLower']).using('handleLower-index').exec() + const memberIdObjs = _.map(memberDb, member => { return { userId: member.userId } }) + const memberIds = _.map(memberIdObjs, 'userId') + const memberHandleLowers = _.map(memberDb, 'handleLower') + await deleteFromDb('Member', memberIdObjs) + await deleteFromDb('MemberAggregatedSkills', memberIdObjs) + await deleteFromDb('MemberEnteredSkills', memberIdObjs) + await deleteFromDb('MemberFinancial', memberIdObjs) + await deleteFromDb('MemberHistoryStats', memberIdObjs) + await deleteFromDb('MemberStats', memberIdObjs) + + for (const id of memberIds) { + await deleteFromDbByQuery('MemberHistoryStatsPrivate', 'userId', id) + await deleteFromDbByQuery('MemberStatsPrivate', 'userId', id) + await deleteFromDbByQuery('MemberTrait', 'userId', id) + } + const distStatsDb = await models.MemberDistributionStats.scan('track').beginsWith(config.AUTOMATED_TESTING_NAME_PREFIX).exec() + for (const item of distStatsDb) { + await item.delete() + } + await deleteFromES(config.get('ES.MEMBER_PROFILE_ES_INDEX'), '_id', memberHandleLowers, 'Member') + await deleteFromES(config.get('ES.MEMBER_TRAIT_ES_INDEX'), 'userId', memberIds, 'Trait') + + logger.info({ message: 'clear the test data from postman test completed!' }) +} +cleanUpTestData.schema = Joi.object().keys({ + currentUser: Joi.object().required() +}).required() + +module.exports = { + cleanUpTestData +} diff --git a/src/services/MemberService.js b/src/services/MemberService.js index e13a948..10dfc93 100644 --- a/src/services/MemberService.js +++ b/src/services/MemberService.js @@ -17,7 +17,7 @@ const esClient = helper.getESClient() const MEMBER_FIELDS = ['userId', 'handle', 'handleLower', 'firstName', 'lastName', 'tracks', 'status', 'addresses', 'description', 'email', 'homeCountryCode', 'competitionCountryCode', 'photoURL', 'maxRating', - 'createdAt', 'createdBy','updatedAt','updatedBy'] + 'createdAt', 'createdBy', 'updatedAt', 'updatedBy'] const INTERNAL_MEMBER_FIELDS = ['newEmail', 'emailVerifyToken', 'emailVerifyTokenDate', 'newEmailVerifyToken', 'newEmailVerifyTokenDate', 'handleSuggest'] @@ -32,10 +32,10 @@ function cleanMember (currentUser, members, selectFields) { var response if (Array.isArray(members)) { const mb = members[0].originalItem ? members[0].originalItem() : members[0] - response = omitMemberAttributes (currentUser, mb) + response = omitMemberAttributes(currentUser, mb) } else { const mb = members.originalItem ? members.originalItem() : members - response = omitMemberAttributes (currentUser, mb) + response = omitMemberAttributes(currentUser, mb) } // select fields if (selectFields) { @@ -74,8 +74,8 @@ async function getMember (currentUser, handle, query) { bool: { filter: [{ match_phrase: { handleLower: handle.toLowerCase() } }] } - }, - sort: [{ traitId: { order: 'asc' } }] + } + // sort: [{ traitId: { order: 'asc' } }] } } // Search with constructed query @@ -88,11 +88,11 @@ async function getMember (currentUser, handle, query) { // get the 'maxRating' from stats if (_.includes(selectFields, 'maxRating')) { for (let i = 0; i < members.length; i += 1) { - const memberStatsFields = { "fields": "userId,groupId,handleLower,maxRating" } - const memberStats = await statisticsService.getMemberStats(currentUser, members[i].handleLower, + const memberStatsFields = { 'fields': 'userId,groupId,handleLower,maxRating' } + const memberStats = await statisticsService.getMemberStats(currentUser, members[i].handleLower, memberStatsFields, false) - if(memberStats[0]) { - if (memberStats[0].hasOwnProperty("maxRating")) { + if (memberStats[0]) { + if (memberStats[0].hasOwnProperty('maxRating')) { members[i].maxRating = memberStats[0].maxRating } else { members[i].maxRating = {} @@ -141,14 +141,14 @@ async function updateMember (currentUser, handle, query, data) { query: { bool: { filter: [ { - match_phrase: { email : data.email } + match_phrase: { email: data.email } } ] } } } } let checkEmail = await esClient.count(esCheckEmail) - if (checkEmail.count == 0) { + if (checkEmail.count === 0) { data.newEmail = data.email delete data.email data.emailVerifyToken = uuid() @@ -302,7 +302,7 @@ async function uploadPhoto (currentUser, handle, files) { } MB.`) } var fileExt = file.name.substr(file.name.lastIndexOf('.')) - var fileName = handle + "-" + new Date().getTime() + fileExt + var fileName = handle + '-' + new Date().getTime() + fileExt // upload photo to S3 // const photoURL = await helper.uploadPhotoToS3(file.data, file.mimetype, file.name) const photoURL = await helper.uploadPhotoToS3(file.data, file.mimetype, fileName) diff --git a/src/services/MemberTraitService.js b/src/services/MemberTraitService.js index d39ed6f..27041d4 100644 --- a/src/services/MemberTraitService.js +++ b/src/services/MemberTraitService.js @@ -5,7 +5,7 @@ const _ = require('lodash') const Joi = require('joi') const config = require('config') -const moment = require('moment'); +const moment = require('moment') const helper = require('../common/helper') const logger = require('../common/logger') const errors = require('../common/errors') @@ -56,7 +56,7 @@ async function getTraits (currentUser, handle, query) { result = _.filter(result, (item) => _.includes(traitIds, item.traitId)) } // convert date time for traits data - _.filter(result, (item) => _.forEach(item.traits.data, function(value) { + _.filter(result, (item) => _.forEach(item.traits.data, function (value) { if (value.hasOwnProperty('birthDate')) { if (value.birthDate) { value.birthDate = moment(value.birthDate).toDate().toISOString() @@ -125,9 +125,9 @@ async function createTraits (currentUser, handle, data) { trait.createdAt = new Date().toISOString() trait.createdBy = Number(currentUser.userId || currentUser.sub) if (trait.traits) { - trait.traits = { "traitId": trait.traitId, "data": trait.traits.data } + trait.traits = { 'traitId': trait.traitId, 'data': trait.traits.data } } else { - trait.traits = { "traitId": trait.traitId, "data": [] } + trait.traits = { 'traitId': trait.traitId, 'data': [] } } // update db await helper.create('MemberTrait', trait) @@ -186,9 +186,9 @@ async function updateTraits (currentUser, handle, data) { existing.updatedAt = new Date().toISOString() existing.updatedBy = Number(currentUser.userId || currentUser.sub) if (trait.traits) { - existing.traits = { "traitId": trait.traitId, "data": trait.traits.data } + existing.traits = { 'traitId': trait.traitId, 'data': trait.traits.data } } else { - existing.traits = { "traitId": trait.traitId, "data": [] } + existing.traits = { 'traitId': trait.traitId, 'data': [] } } // update db var updateDb = await helper.update(existing, {}) diff --git a/src/services/MiscService.js b/src/services/MiscService.js index 6d197d2..0f46fae 100644 --- a/src/services/MiscService.js +++ b/src/services/MiscService.js @@ -2,13 +2,13 @@ * This service provides operations of statistics. */ -const _ = require('lodash') +// const _ = require('lodash') const Joi = require('joi') -const helper = require('../common/helper') +// const helper = require('../common/helper') const logger = require('../common/logger') -const errors = require('../common/errors') +// const errors = require('../common/errors') -const MEMBER_FINANCIAL_FIELDS = ['userId', 'amount', 'status', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] +// const MEMBER_FINANCIAL_FIELDS = ['userId', 'amount', 'status', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] /** * Get member financial data. @@ -19,9 +19,9 @@ const MEMBER_FINANCIAL_FIELDS = ['userId', 'amount', 'status', 'createdAt', 'upd */ async function getMemberFinancial (currentUser, handle, query) { // validate and parse query parameter - const fields = helper.parseCommaSeparatedString(query.fields, MEMBER_FINANCIAL_FIELDS) || MEMBER_FINANCIAL_FIELDS + // const fields = helper.parseCommaSeparatedString(query.fields, MEMBER_FINANCIAL_FIELDS) || MEMBER_FINANCIAL_FIELDS // get member by handle - const member = await helper.getMemberByHandle(handle) + // const member = await helper.getMemberByHandle(handle) // // only admin, M2M or user himself can get financial data // if (!helper.canManageMember(currentUser, member)) { // throw new errors.ForbiddenError('You are not allowed to get financial data of the user.') diff --git a/src/services/SearchService.js b/src/services/SearchService.js index 4cbdab1..f1cc130 100644 --- a/src/services/SearchService.js +++ b/src/services/SearchService.js @@ -28,7 +28,7 @@ const esClient = helper.getESClient() * @param {Object} query the query parameters * @returns {Object} the search result */ -async function searchMembers(currentUser, query) { +async function searchMembers (currentUser, query) { // validate and parse fields param let fields = helper.parseCommaSeparatedString(query.fields, MEMBER_FIELDS) || MEMBER_FIELDS // if current user is not admin and not M2M, then exclude the admin/M2M only fields @@ -67,7 +67,7 @@ async function searchMembers(currentUser, query) { if (!item.skills) { item.skills = {} } - return item; + return item }) // merge overall members and stats @@ -79,7 +79,7 @@ async function searchMembers(currentUser, query) { // add the maxrating item.maxRating = mbrsSkillsStatsKeys[item.userId].maxRating // set the rating color - if (item.maxRating.hasOwnProperty("rating")) { + if (item.maxRating.hasOwnProperty('rating')) { item.maxRating.ratingColor = helper.getRatingColor(item.maxRating.rating) } } @@ -88,11 +88,11 @@ async function searchMembers(currentUser, query) { } else { item.stats = [] } - return item; + return item }) // sort the data - results = _.orderBy(resultMbrsSkillsStats, ['handleLower'],[query.sort]) - // filter member based on fields + results = _.orderBy(resultMbrsSkillsStats, ['handleLower'], [query.sort]) + // filter member based on fields results = _.map(results, (item) => _.pick(item, fields)) } return { total: total, page: query.page, perPage: query.perPage, result: results } @@ -111,7 +111,7 @@ searchMembers.schema = { fields: Joi.string(), page: Joi.page(), perPage: Joi.perPage(), - sort: Joi.sort(), + sort: Joi.sort() }) } @@ -121,7 +121,7 @@ searchMembers.schema = { * @param {Object} query the query parameters * @returns {Object} the autocomplete result */ -async function autocomplete(currentUser, query) { +async function autocomplete (currentUser, query) { // validate and parse fields param let fields = helper.parseCommaSeparatedString(query.fields, MEMBER_AUTOCOMPLETE_FIELDS) || MEMBER_AUTOCOMPLETE_FIELDS // // if current user is not admin and not M2M, then exclude the admin/M2M only fields @@ -131,16 +131,16 @@ async function autocomplete(currentUser, query) { // } // get suggestion based on querys term const docsSuggestions = await eshelper.getSuggestion(query, esClient, currentUser) - if (docsSuggestions.hasOwnProperty("suggest")) { - const totalSuggest = docsSuggestions.suggest["handle-suggestion"][0].options.length - var results = docsSuggestions.suggest["handle-suggestion"][0].options + if (docsSuggestions.hasOwnProperty('suggest')) { + const totalSuggest = docsSuggestions.suggest['handle-suggestion'][0].options.length + var results = docsSuggestions.suggest['handle-suggestion'][0].options // custom filter & sort - let regex = new RegExp(`^${query.term}`, `i`); + let regex = new RegExp(`^${query.term}`, `i`) results = results - .filter(x => regex.test(x.payload.handle)) - .sort((a, b) => a.payload.handle.localeCompare(b.payload.handle)); - // filter member based on fields - results = _.map(results, (item) => _.pick(item.payload, fields)) + .filter(x => regex.test(x._source.handle)) + .sort((a, b) => a._source.handle.localeCompare(b._source.handle)) + // filter member based on fields + results = _.map(results, (item) => _.pick(item._source, fields)) // custom pagination results = helper.paginate(results, query.perPage, query.page - 1) return { total: totalSuggest, page: query.page, perPage: query.perPage, result: results } @@ -156,7 +156,7 @@ autocomplete.schema = { page: Joi.page(), perPage: Joi.perPage(), size: Joi.size(), - sort: Joi.sort(), + sort: Joi.sort() }) } diff --git a/src/services/StatisticsService.js b/src/services/StatisticsService.js index 9a271f2..fe36065 100644 --- a/src/services/StatisticsService.js +++ b/src/services/StatisticsService.js @@ -17,13 +17,13 @@ const HISTORY_STATS_FIELDS = ['userId', 'groupId', 'handle', 'handleLower', 'DEV 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] const MEMBER_STATS_FIELDS = ['userId', 'groupId', 'handle', 'handleLower', 'maxRating', - 'challenges', 'wins','DEVELOP', 'DESIGN', 'DATA_SCIENCE', 'copilot', 'createdAt', + 'challenges', 'wins', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE', 'copilot', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] const MEMBER_SKILL_FIELDS = ['userId', 'handle', 'handleLower', 'skills', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] -var allTags +// var allTags /** * Get distribution statistics. @@ -106,7 +106,7 @@ async function getHistoryStats (currentUser, handle, query) { if (!groupIds) { // get statistics by member user id from dynamodb let statsDb = await helper.getEntityByHashKey(handle, 'MemberHistoryStats', 'userId', member.userId, true) - if(!_.isEmpty(statsDb)) { + if (!_.isEmpty(statsDb)) { statsDb.originalItem().groupId = 10 overallStat.push(statsDb.originalItem()) } @@ -114,17 +114,17 @@ async function getHistoryStats (currentUser, handle, query) { if (groupIds) { for (const groupId of groupIds.split(',')) { let statsDb - if(groupId == "10") { + if (groupId === '10') { // get statistics by member user id from dynamodb statsDb = await helper.getEntityByHashKey(handle, 'MemberHistoryStats', 'userId', member.userId, false) - if(!_.isEmpty(statsDb)) { + if (!_.isEmpty(statsDb)) { statsDb.originalItem().groupId = 10 } } else { // get statistics private by member user id from dynamodb statsDb = await helper.getEntityByHashRangeKey(handle, 'MemberHistoryStatsPrivate', 'userId', member.userId, 'groupId', groupId, false) } - if(!_.isEmpty(statsDb)) { + if (!_.isEmpty(statsDb)) { overallStat.push(statsDb.originalItem()) } } @@ -166,18 +166,18 @@ async function getMemberStats (currentUser, handle, query, throwError) { stat = await esClient.get({ index: config.ES.MEMBER_STATS_ES_INDEX, type: config.ES.MEMBER_STATS_ES_TYPE, - id: member.userId + "_10" - }); - if (stat.hasOwnProperty("_source")) { + id: member.userId + '_10' + }) + if (stat.hasOwnProperty('_source')) { stat = stat._source } } catch (error) { - if (error.displayName == "NotFound") { + if (error.displayName === 'NotFound') { // get statistics by member user id from dynamodb stat = await helper.getEntityByHashKey(handle, 'MemberStats', 'userId', member.userId, throwError) if (!_.isEmpty(stat, true)) { stat.originalItem().groupId = 10 - stat = stat.originalItem + stat = stat.originalItem() } } } @@ -193,19 +193,19 @@ async function getMemberStats (currentUser, handle, query, throwError) { stat = await esClient.get({ index: config.ES.MEMBER_STATS_ES_INDEX, type: config.ES.MEMBER_STATS_ES_TYPE, - id: member.userId + "_" + groupId - }); - if (stat.hasOwnProperty("_source")) { + id: member.userId + '_' + groupId + }) + if (stat.hasOwnProperty('_source')) { stat = stat._source } } catch (error) { - if (error.displayName == "NotFound") { - if(groupId == "10") { + if (error.displayName === 'NotFound') { + if (groupId === '10') { // get statistics by member user id from dynamodb stat = await helper.getEntityByHashKey(handle, 'MemberStats', 'userId', member.userId, false) if (!_.isEmpty(stat, true)) { stat.originalItem().groupId = 10 - stat = stat.originalItem + stat = stat.originalItem() } } else { // get statistics private by member user id from dynamodb @@ -248,7 +248,7 @@ async function getMemberSkills (currentUser, handle, query, throwError) { // get member by handle const member = await helper.getMemberByHandle(handle) // fetch tags data - if(!this.allTags) { + if (!this.allTags) { this.allTags = await helper.getAllTags(config.TAGS.TAGS_BASE_URL + config.TAGS.TAGS_API_VERSION + config.TAGS.TAGS_FILTER) } // get member entered skill by member user id @@ -297,7 +297,7 @@ async function partiallyUpdateMemberSkills (currentUser, handle, data) { throw new errors.ForbiddenError('You are not allowed to update the member skills.') } // fetch tags data - if(!this.allTags) { + if (!this.allTags) { this.allTags = await helper.getAllTags(config.TAGS.TAGS_BASE_URL + config.TAGS.TAGS_API_VERSION + config.TAGS.TAGS_FILTER) } // get member entered skill by member user id @@ -312,12 +312,12 @@ async function partiallyUpdateMemberSkills (currentUser, handle, data) { var tempSkill = {} _.forIn(data, (value, key) => { var tag = helper.findTagById(this.allTags, Number(key)) - if(tag) { + if (tag) { value.tagName = tag.name - if (!value.hasOwnProperty("hidden")) { + if (!value.hasOwnProperty('hidden')) { value.hidden = false } - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = 1 } value.sources = [ 'USER_ENTERED' ] @@ -327,7 +327,7 @@ async function partiallyUpdateMemberSkills (currentUser, handle, data) { _.assignIn(memberEnteredSkill.skills, tempSkill) memberEnteredSkill.updatedAt = new Date().getTime() memberEnteredSkill.updatedBy = currentUser.handle || currentUser.sub - const result = await helper.update(memberEnteredSkill, {}) + await helper.update(memberEnteredSkill, {}) // get skills by member handle const memberSkill = await this.getMemberSkills(currentUser, handle, {}, true) return memberSkill diff --git a/test/e2e/member.api.test.js b/test/e2e/member.api.test.js deleted file mode 100644 index b651aea..0000000 --- a/test/e2e/member.api.test.js +++ /dev/null @@ -1,426 +0,0 @@ -/* - * E2E tests of member API - */ - -require('../../app-bootstrap') -const _ = require('lodash') -const config = require('config') -const fs = require('fs') -const path = require('path') -const chai = require('chai') -const chaiHttp = require('chai-http') -const app = require('../../app') -const testHelper = require('../testHelper') - -const should = chai.should() -chai.use(chaiHttp) - -const basePath = `/${config.API_VERSION}/members` - -const photoContent = fs.readFileSync(path.join(__dirname, '../photo.png')) - -describe('member API E2E tests', () => { - // test data - let member1 - let member2 - - before(async () => { - await testHelper.createData() - const data = testHelper.getData() - member1 = data.member1 - member2 = data.member2 - }) - - after(async () => { - await testHelper.clearData() - }) - - describe('get member API tests', () => { - it('get member successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - should.equal(response.status, 200) - const result = response.body - should.equal(_.isEqual(result.maxRating, member1.maxRating), true) - should.equal(result.userId, member1.userId) - should.equal(result.firstName, member1.firstName) - should.equal(result.lastName, member1.lastName) - should.equal(result.description, member1.description) - should.equal(result.otherLangName, member1.otherLangName) - should.equal(result.handle, member1.handle) - should.equal(result.handleLower, member1.handleLower) - should.equal(result.status, member1.status) - should.equal(result.email, member1.email) - should.equal(result.addresses.length, 1) - should.equal(result.addresses[0].streetAddr1, member1.addresses[0].streetAddr1) - should.equal(result.addresses[0].streetAddr2, member1.addresses[0].streetAddr2) - should.equal(result.addresses[0].city, member1.addresses[0].city) - should.equal(result.addresses[0].zip, member1.addresses[0].zip) - should.equal(result.addresses[0].stateCode, member1.addresses[0].stateCode) - should.equal(result.addresses[0].type, member1.addresses[0].type) - should.equal(testHelper.getDatesDiff(result.addresses[0].createdAt, member1.addresses[0].createdAt), 0) - should.equal(testHelper.getDatesDiff(result.addresses[0].updatedAt, member1.addresses[0].updatedAt), 0) - should.equal(result.addresses[0].createdBy, member1.addresses[0].createdBy) - should.equal(result.addresses[0].updatedBy, member1.addresses[0].updatedBy) - should.equal(result.homeCountryCode, member1.homeCountryCode) - should.equal(result.competitionCountryCode, member1.competitionCountryCode) - should.equal(result.photoURL, member1.photoURL) - should.equal(_.isEqual(result.tracks, member1.tracks), true) - should.equal(testHelper.getDatesDiff(result.createdAt, member1.createdAt), 0) - should.equal(testHelper.getDatesDiff(result.updatedAt, member1.updatedAt), 0) - should.equal(result.createdBy, member1.createdBy) - should.equal(result.updatedBy, member1.updatedBy) - }) - - it('get member successfully 2', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}`) - .query({ fields: 'userId,firstName,lastName,email,addresses' }) - should.equal(response.status, 200) - const result = response.body - should.equal(result.userId, member1.userId) - should.equal(result.firstName, member1.firstName) - should.equal(result.lastName, member1.lastName) - // identifiable fields should not be returned - should.not.exist(result.email) - should.not.exist(result.addresses) - }) - - it('get member - forbidden', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}`) - .set('Authorization', `Bearer ${config.M2M_UPDATE_ACCESS_TOKEN}`) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to perform this action!') - }) - - it('get member - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/other`) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('get member - invalid field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}`) - .query({ fields: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('get member - duplicate field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}`) - .query({ fields: 'email,email' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: email') - }) - - it('get member - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}`) - .query({ fields: 'email', other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('verify email API tests', () => { - it('verify email - wrong token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ token: 'wrong' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Wrong verification token.') - }) - - it('verify email successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.M2M_FULL_ACCESS_TOKEN}`) - .query({ token: member1.emailVerifyToken }) - should.equal(response.status, 200) - const result = response.body - should.equal(result.emailChangeCompleted, false) - should.equal(result.verifiedEmail, member1.email) - }) - - it('verify email successfully 2', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.M2M_UPDATE_ACCESS_TOKEN}`) - .query({ token: member1.newEmailVerifyToken }) - should.equal(response.status, 200) - const result = response.body - should.equal(result.emailChangeCompleted, true) - should.equal(result.verifiedEmail, member1.newEmail) - }) - - it('verify email - forbidden', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ token: member1.emailVerifyToken }) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to perform this action!') - }) - - it('verify email - missing auth token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .query({ token: member1.emailVerifyToken }) - should.equal(response.status, 401) - should.equal(response.body.message, 'No token provided.') - }) - - it('verify email - invalid bearer format', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', 'invalid format') - .query({ token: member1.emailVerifyToken }) - should.equal(response.status, 401) - should.equal(response.body.message, 'No token provided.') - }) - - it('verify email - invalid token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.INVALID_TOKEN}`) - .query({ token: member1.emailVerifyToken }) - should.equal(response.status, 401) - should.equal(response.body.message, 'Failed to authenticate token.') - }) - - it('verify email - expired token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.EXPIRED_TOKEN}`) - .query({ token: member1.emailVerifyToken }) - should.equal(response.status, 401) - should.equal(response.body.message, 'Failed to authenticate token.') - }) - - it('verify email - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/other/verify`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ token: member1.emailVerifyToken }) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('verify email - missing verify token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - should.equal(response.status, 400) - should.equal(response.body.message, '"token" is required') - }) - - it('verify email - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/verify`) - .set('Authorization', `Bearer ${config.M2M_FULL_ACCESS_TOKEN}`) - .query({ token: member1.emailVerifyToken, other: 123 }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('update member API tests', () => { - it('update member successfully', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ verifyUrl: 'http://test.com/verify' }) - .send({ - userId: 999, - firstName: 'fff', - lastName: 'lll', - description: 'updated desc', - email: 'new-email@test.com' - }) - should.equal(response.status, 200) - const result = response.body - should.equal(_.isEqual(result.maxRating, member2.maxRating), true) - should.equal(result.userId, 999) - should.equal(result.firstName, 'fff') - should.equal(result.lastName, 'lll') - should.equal(result.description, 'updated desc') - should.equal(result.otherLangName, member2.otherLangName) - should.equal(result.handle, member2.handle) - should.equal(result.handleLower, member2.handleLower) - should.equal(result.status, member2.status) - // email is not updated to new email, because it is not verified yet - should.equal(result.email, member2.email) - should.equal(result.addresses.length, 1) - should.equal(result.addresses[0].streetAddr1, member2.addresses[0].streetAddr1) - should.equal(result.addresses[0].streetAddr2, member2.addresses[0].streetAddr2) - should.equal(result.addresses[0].city, member2.addresses[0].city) - should.equal(result.addresses[0].zip, member2.addresses[0].zip) - should.equal(result.addresses[0].stateCode, member2.addresses[0].stateCode) - should.equal(result.addresses[0].type, member2.addresses[0].type) - should.equal(testHelper.getDatesDiff(result.addresses[0].createdAt, member2.addresses[0].createdAt), 0) - should.equal(testHelper.getDatesDiff(result.addresses[0].updatedAt, member2.addresses[0].updatedAt), 0) - should.equal(result.addresses[0].createdBy, member2.addresses[0].createdBy) - should.equal(result.addresses[0].updatedBy, member2.addresses[0].updatedBy) - should.equal(result.homeCountryCode, member2.homeCountryCode) - should.equal(result.competitionCountryCode, member2.competitionCountryCode) - should.equal(result.photoURL, member2.photoURL) - should.equal(_.isEqual(result.tracks, member2.tracks), true) - should.equal(testHelper.getDatesDiff(result.createdAt, member2.createdAt), 0) - should.exist(result.updatedAt) - should.equal(result.createdBy, member2.createdBy) - should.equal(result.updatedBy, 'TonyJ') - }) - - it('update member - forbidden', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}`) - .set('Authorization', `Bearer ${config.USER_TOKEN}`) - .query({ verifyUrl: 'http://test.com/verify' }) - .send({ - userId: 999, - firstName: 'fff', - lastName: 'lll', - description: 'updated desc', - email: 'new-email@test.com' - }) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to update the member.') - }) - - it('update member - not found', async () => { - const response = await chai.request(app) - .put(`${basePath}/other`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send({ - userId: 999 - }) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('update member - invalid verifyUrl', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ verifyUrl: 'abc' }) - .send({ - userId: 999, - firstName: 'fff', - lastName: 'lll', - description: 'updated desc', - email: 'new-email@test.com' - }) - should.equal(response.status, 400) - should.equal(response.body.message, '"verifyUrl" must be a valid uri') - }) - - it('update member - invalid userId', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ verifyUrl: 'http://test.com/verify' }) - .send({ - userId: 'abc' - }) - should.equal(response.status, 400) - should.equal(response.body.message, '"userId" must be a number') - }) - - it('update member - invalid photoURL', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ verifyUrl: 'http://test.com/verify' }) - .send({ - photoURL: 'abc' - }) - should.equal(response.status, 400) - should.equal(response.body.message, '"photoURL" must be a valid uri') - }) - - it('update member - invalid email', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ verifyUrl: 'http://test.com/verify' }) - .send({ - userId: 999, - firstName: 'fff', - lastName: 'lll', - description: 'updated desc', - email: 'invalid' - }) - should.equal(response.status, 400) - should.equal(response.body.message, '"email" must be a valid email') - }) - - it('update member - unexpected field', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ verifyUrl: 'http://test.com/verify' }) - .send({ - userId: 999, - firstName: 'fff', - lastName: 'lll', - description: 'updated desc', - other: 'abc' - }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('upload photo API tests', () => { - it('upload photo successfully', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member2.handle}/photo`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .attach('photo', photoContent, 'photo.png') - should.equal(response.status, 200) - const result = response.body - should.equal(result.photoURL.startsWith(config.PHOTO_URL_TEMPLATE.replace('', '')), true) - }) - - it('upload photo - forbidden', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member2.handle}/photo`) - .set('Authorization', `Bearer ${config.USER_TOKEN}`) - .attach('photo', photoContent, 'photo.png') - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to upload photo for the member.') - }) - - it('upload photo - not found', async () => { - const response = await chai.request(app) - .post(`${basePath}/other/photo`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .attach('photo', photoContent, 'photo.png') - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('upload photo - invalid file field name', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member2.handle}/photo`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .attach('invalid', photoContent, 'photo.png') - should.equal(response.status, 400) - should.equal(response.body.message, '"photo" is required') - }) - - it('upload photo - missing file', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member2.handle}/photo`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - should.equal(response.status, 400) - should.equal(response.body.message, '"files" is required') - }) - }) -}) diff --git a/test/e2e/member.trait.api.test.js b/test/e2e/member.trait.api.test.js deleted file mode 100644 index 90d394c..0000000 --- a/test/e2e/member.trait.api.test.js +++ /dev/null @@ -1,407 +0,0 @@ -/* - * E2E tests of member traits API - */ - -require('../../app-bootstrap') -const _ = require('lodash') -const config = require('config') -const chai = require('chai') -const chaiHttp = require('chai-http') -const app = require('../../app') -const testHelper = require('../testHelper') - -const should = chai.should() -chai.use(chaiHttp) - -const basePath = `/${config.API_VERSION}/members` - -describe('member traits API E2E tests', () => { - // test data - let member1 - let member2 - - before(async () => { - await testHelper.createData() - const data = testHelper.getData() - member1 = data.member1 - member2 = data.member2 - }) - - after(async () => { - await testHelper.clearData() - }) - - describe('get member traits API tests', () => { - it('get member traits successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ traitIds: 'basic_id,work', fields: 'traitId,categoryName,traits' }) - should.equal(response.status, 200) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].traitId, 'basic_id') - should.equal(result[0].categoryName, 'Subscription') - should.equal(result[0].traits.data.length, 1) - should.equal(result[0].traits.data[0].test, 'abc') - should.not.exist(result[0].createdAt) - should.not.exist(result[0].updatedAt) - should.not.exist(result[0].createdBy) - should.not.exist(result[0].updatedBy) - }) - - it('get member traits successfully 2', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - should.equal(response.status, 200) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].traitId, 'basic_id') - should.equal(result[0].categoryName, 'Subscription') - should.equal(result[0].traits.data.length, 1) - should.equal(result[0].traits.data[0].test, 'abc') - should.exist(result[0].createdAt) - should.exist(result[0].updatedAt) - should.exist(result[0].createdBy) - should.exist(result[0].updatedBy) - }) - - it('get member traits - missing auth token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - should.equal(response.status, 401) - should.equal(response.body.message, 'No token provided.') - }) - - it('get member traits - invalid bearer format', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', 'invalid format') - should.equal(response.status, 401) - should.equal(response.body.message, 'No token provided.') - }) - - it('get member traits - invalid token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.INVALID_TOKEN}`) - should.equal(response.status, 401) - should.equal(response.body.message, 'Failed to authenticate token.') - }) - - it('get member traits - expired token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.EXPIRED_TOKEN}`) - should.equal(response.status, 401) - should.equal(response.body.message, 'Failed to authenticate token.') - }) - - it('get member traits - forbidden', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.M2M_UPDATE_ACCESS_TOKEN}`) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to perform this action!') - }) - - it('get member traits - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/other/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('get member traits - invalid trait id', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ traitIds: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('get member traits - duplicate fields', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ fields: 'traitId,traitId' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: traitId') - }) - - it('get member traits - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('create member traits API tests', () => { - it('create member traits successfully', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 200) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].traitId, 'skill') - should.equal(result[0].categoryName, 'category') - should.equal(result[0].traits.data.length, 1) - should.equal(result[0].traits.data[0].test, 111) - should.exist(result[0].createdAt) - should.equal(result[0].createdBy, 'TonyJ') - should.not.exist(result[0].updatedAt) - should.not.exist(result[0].updatedBy) - }) - - it('create member traits - conflict', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 400) - should.equal(response.body.message, 'The trait id skill already exists for the member.') - }) - - it('create member traits - forbidden', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member2.handle}/traits`) - .set('Authorization', `Bearer ${config.USER_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to create traits of the member.') - }) - - it('create member traits - not found', async () => { - const response = await chai.request(app) - .post(`${basePath}/other/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('create member traits - invalid traitId', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'abc', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 400) - should.equal(response.body.message, '"traitId" must be one of [basic_id, work, skill, education, communities]') - }) - - it('create member traits - invalid traits', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: 123 - }]) - should.equal(response.status, 400) - should.equal(response.body.message, '"traits" must be an object') - }) - - it('create member traits - unexpected field', async () => { - const response = await chai.request(app) - .post(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] }, - other: 'abc' - }]) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('update member traits API tests', () => { - it('update member traits successfully', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category2', - traits: { data: [{ test: 222 }] } - }]) - should.equal(response.status, 200) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].traitId, 'skill') - should.equal(result[0].categoryName, 'category2') - should.equal(result[0].traits.data.length, 1) - should.equal(result[0].traits.data[0].test, 222) - should.exist(result[0].createdAt) - should.equal(result[0].createdBy, 'TonyJ') - should.exist(result[0].updatedAt) - should.equal(result[0].updatedBy, 'TonyJ') - }) - - it('update member traits - trait not found', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'education', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 404) - should.equal(response.body.message, 'The trait id education is not found for the member.') - }) - - it('update member traits - forbidden', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member2.handle}/traits`) - .set('Authorization', `Bearer ${config.USER_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to update traits of the member.') - }) - - it('update member traits - member not found', async () => { - const response = await chai.request(app) - .put(`${basePath}/other/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('update member traits - invalid traitId', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'abc', - categoryName: 'category', - traits: { data: [{ test: 111 }] } - }]) - should.equal(response.status, 400) - should.equal(response.body.message, '"traitId" must be one of [basic_id, work, skill, education, communities]') - }) - - it('update member traits - invalid traits', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: 123 - }]) - should.equal(response.status, 400) - should.equal(response.body.message, '"traits" must be an object') - }) - - it('update member traits - unexpected field', async () => { - const response = await chai.request(app) - .put(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send([{ - traitId: 'skill', - categoryName: 'category', - traits: { data: [{ test: 111 }] }, - other: 'abc' - }]) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('remove member traits API tests', () => { - it('remove member traits successfully', async () => { - const response = await chai.request(app) - .delete(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.M2M_FULL_ACCESS_TOKEN}`) - .query({ traitIds: 'skill' }) - should.equal(response.status, 200) - const result = response.body - should.equal(_.isEmpty(result), true) - }) - - it('remove member traits - forbidden', async () => { - const response = await chai.request(app) - .delete(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ traitIds: 'skill' }) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to perform this action!') - }) - - it('remove member traits - trait not found', async () => { - const response = await chai.request(app) - .delete(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ traitIds: 'skill' }) - should.equal(response.status, 404) - should.equal(response.body.message, 'The trait id skill is not found for the member.') - }) - - it('remove member traits - invalid trait id', async () => { - const response = await chai.request(app) - .delete(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ traitIds: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('remove member traits - duplicate trait ids', async () => { - const response = await chai.request(app) - .delete(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ traitIds: 'skill,skill' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: skill') - }) - - it('remove member traits - unexpected query parameter', async () => { - const response = await chai.request(app) - .delete(`${basePath}/${member1.handle}/traits`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) -}) diff --git a/test/e2e/misc.api.test.js b/test/e2e/misc.api.test.js deleted file mode 100644 index a88f2d2..0000000 --- a/test/e2e/misc.api.test.js +++ /dev/null @@ -1,143 +0,0 @@ -/* - * E2E tests of misc API - */ - -require('../../app-bootstrap') -const _ = require('lodash') -const config = require('config') -const chai = require('chai') -const chaiHttp = require('chai-http') -const app = require('../../app') -const testHelper = require('../testHelper') - -const should = chai.should() -chai.use(chaiHttp) - -const basePath = `/${config.API_VERSION}/members` - -describe('misc API E2E tests', () => { - // test data - let member1 - let member2 - let memberFinancial - - before(async () => { - await testHelper.createData() - const data = testHelper.getData() - member1 = data.member1 - member2 = data.member2 - memberFinancial = data.memberFinancial - }) - - after(async () => { - await testHelper.clearData() - }) - - describe('get member financial data API tests', () => { - it('get member financial data successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ - fields: 'userId,amount,status,createdBy,updatedBy' - }) - should.equal(response.status, 200) - const result = response.body - should.equal(_.isEqual(result, _.pick(memberFinancial, - ['userId', 'amount', 'status', 'createdBy', 'updatedBy'])), true) - should.not.exist(result.createdAt) - should.not.exist(result.updatedAt) - }) - - it('get member financial data - forbidden 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.M2M_UPDATE_ACCESS_TOKEN}`) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to perform this action!') - }) - - it('get member financial data - forbidden 2', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member2.handle}/financial`) - .set('Authorization', `Bearer ${config.USER_TOKEN}`) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to get financial data of the user.') - }) - - it('get member financial data - missing auth token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - should.equal(response.status, 401) - should.equal(response.body.message, 'No token provided.') - }) - - it('get member financial data - invalid bearer format', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', 'invalid format') - should.equal(response.status, 401) - should.equal(response.body.message, 'No token provided.') - }) - - it('get member financial data - invalid token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.INVALID_TOKEN}`) - should.equal(response.status, 401) - should.equal(response.body.message, 'Failed to authenticate token.') - }) - - it('get member financial data - expired token', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.EXPIRED_TOKEN}`) - should.equal(response.status, 401) - should.equal(response.body.message, 'Failed to authenticate token.') - }) - - it('get member financial data - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/other/financial`) - .set('Authorization', `Bearer ${config.M2M_FULL_ACCESS_TOKEN}`) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('get member financial data - invalid field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ fields: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('get member financial data - duplicate fields', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ fields: 'userId,amount,userId' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: userId') - }) - - it('get member financial data - empty field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ fields: 'userId, ,createdAt' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Empty value.') - }) - - it('get member financial data - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/financial`) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) -}) diff --git a/test/e2e/search.api.test.js b/test/e2e/search.api.test.js deleted file mode 100644 index cc563af..0000000 --- a/test/e2e/search.api.test.js +++ /dev/null @@ -1,239 +0,0 @@ -/* - * E2E tests of search API - */ - -require('../../app-bootstrap') -const _ = require('lodash') -const config = require('config') -const chai = require('chai') -const chaiHttp = require('chai-http') -const app = require('../../app') -const testHelper = require('../testHelper') - -const should = chai.should() -chai.use(chaiHttp) - -const basePath = `/${config.API_VERSION}/members` - -describe('search API E2E tests', () => { - // test data - let member1 - let member2 - - before(async () => { - await testHelper.createData() - const data = testHelper.getData() - member1 = data.member1 - member2 = data.member2 - }) - - after(async () => { - await testHelper.clearData() - }) - - describe('search members API tests', () => { - it('search members successfully 1', async () => { - const response = await chai.request(app) - .get(basePath) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .query({ - query: '"first name" | testing | denis', - handle: member1.handle, - status: member1.status, - fields: 'userId,maxRating,firstName,lastName,handle,status,skills,stats', - page: 1, - perPage: 10 - }) - should.equal(response.status, 200) - should.equal(response.headers['x-page'], '1') - should.equal(response.headers['x-per-page'], '10') - should.equal(response.headers['x-total'], '1') - should.equal(response.headers['x-total-pages'], '1') - should.exist(response.headers['link']) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].userId, member1.userId) - should.equal(_.isEqual(result[0].maxRating, member1.maxRating), true) - should.equal(result[0].firstName, member1.firstName) - should.equal(result[0].lastName, member1.lastName) - should.equal(result[0].handle, member1.handle) - should.equal(result[0].status, member1.status) - should.exist(result[0].skills) - should.equal(result[0].skills.length, 1) - should.equal(result[0].skills[0].name, 'Java') - should.equal(result[0].skills[0].score, 1888) - should.exist(result[0].stats) - should.not.exist(result[0].description) - should.not.exist(result[0].competitionCountryCode) - should.not.exist(result[0].photoURL) - should.not.exist(result[0].tracks) - should.not.exist(result[0].createdAt) - }) - - it('search members successfully 2', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ - query: '"first name" | testing | denis', - handle: member1.handle, - status: member1.status, - fields: 'userId,maxRating,firstName,lastName,handle,status', - page: 1, - perPage: 10 - }) - should.equal(response.status, 200) - should.equal(response.headers['x-page'], '1') - should.equal(response.headers['x-per-page'], '10') - should.equal(response.headers['x-total'], '1') - should.equal(response.headers['x-total-pages'], '1') - should.exist(response.headers['link']) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].userId, member1.userId) - should.equal(_.isEqual(result[0].maxRating, member1.maxRating), true) - should.equal(result[0].handle, member1.handle) - should.equal(result[0].status, member1.status) - should.not.exist(result[0].skills) - should.not.exist(result[0].stats) - should.not.exist(result[0].description) - should.not.exist(result[0].competitionCountryCode) - should.not.exist(result[0].photoURL) - should.not.exist(result[0].tracks) - should.not.exist(result[0].createdAt) - should.not.exist(result[0].firstName) - should.not.exist(result[0].lastName) - }) - - it('search members successfully 3', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ - fields: 'userId,handle', - page: 1, - perPage: 1 - }) - should.equal(response.status, 200) - should.equal(response.headers['x-page'], '1') - should.equal(response.headers['x-per-page'], '1') - should.equal(response.headers['x-total'], '2') - should.equal(response.headers['x-total-pages'], '2') - should.equal(response.headers['x-next-page'], '2') - should.exist(response.headers['link']) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].userId, member1.userId) - should.equal(result[0].handle, member1.handle) - }) - - it('search members successfully 4', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ - fields: 'userId,handle', - page: 2, - perPage: 1 - }) - should.equal(response.status, 200) - should.equal(response.headers['x-page'], '2') - should.equal(response.headers['x-per-page'], '1') - should.equal(response.headers['x-total'], '2') - should.equal(response.headers['x-total-pages'], '2') - should.equal(response.headers['x-prev-page'], '1') - should.exist(response.headers['link']) - const result = response.body - should.equal(result.length, 1) - should.equal(result[0].userId, member2.userId) - should.equal(result[0].handle, member2.handle) - }) - - it('search members successfully 5', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ - handle: 'abcdefg', - page: 1, - perPage: 10 - }) - should.equal(response.status, 200) - should.equal(response.headers['x-page'], '1') - should.equal(response.headers['x-per-page'], '10') - should.equal(response.headers['x-total'], '0') - should.equal(response.headers['x-total-pages'], '0') - const result = response.body - should.equal(result.length, 0) - }) - - it('search members successfully 6', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ - query: 'abcdefg -xyz' - }) - should.equal(response.status, 200) - should.equal(response.headers['x-page'], '1') - should.equal(response.headers['x-per-page'], '20') - should.equal(response.headers['x-total'], '0') - should.equal(response.headers['x-total-pages'], '0') - const result = response.body - should.equal(result.length, 0) - }) - - it('search members - forbidden', async () => { - const response = await chai.request(app) - .get(basePath) - .set('Authorization', `Bearer ${config.M2M_UPDATE_ACCESS_TOKEN}`) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to perform this action!') - }) - - it('search members - invalid field', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ fields: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('search members - duplicate fields', async () => { - const response = await chai.request(app) - .get(basePath) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ fields: 'userId,handle,userId' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: userId') - }) - - it('search members - empty field', async () => { - const response = await chai.request(app) - .get(basePath) - .set('Authorization', `Bearer ${config.M2M_READ_ACCESS_TOKEN}`) - .query({ fields: 'userId, ,handle' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Empty value.') - }) - - it('search members - invalid page', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ page: -1 }) - should.equal(response.status, 400) - should.equal(response.body.message, '"page" must be larger than or equal to 1') - }) - - it('search members - invalid perPage', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ perPage: -1 }) - should.equal(response.status, 400) - should.equal(response.body.message, '"perPage" must be larger than or equal to 1') - }) - - it('search members - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(basePath) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) -}) diff --git a/test/e2e/statistics.api.test.js b/test/e2e/statistics.api.test.js deleted file mode 100644 index fb27b39..0000000 --- a/test/e2e/statistics.api.test.js +++ /dev/null @@ -1,448 +0,0 @@ -/* - * E2E tests of statistics API - */ - -require('../../app-bootstrap') -const _ = require('lodash') -const config = require('config') -const chai = require('chai') -const chaiHttp = require('chai-http') -const app = require('../../app') -const testHelper = require('../testHelper') - -const should = chai.should() -chai.use(chaiHttp) - -const basePath = `/${config.API_VERSION}/members` - -describe('statistics API E2E tests', () => { - // test data - let member1 - let distribution1 - let distribution2 - let historyStats - let memberStats - let memberSkills - - before(async () => { - await testHelper.createData() - const data = testHelper.getData() - member1 = data.member1 - distribution1 = data.distribution1 - distribution2 = data.distribution2 - historyStats = data.historyStats - memberStats = data.memberStats - memberSkills = data.memberSkills - }) - - after(async () => { - await testHelper.clearData() - }) - - describe('get distribution statistics API tests', () => { - it('get distribution statistics successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/stats/distribution`) - .query({ - track: distribution1.track, - subTrack: distribution1.subTrack, - fields: 'track,subTrack,distribution,createdBy,updatedBy' - }) - should.equal(response.status, 200) - const result = response.body - should.equal(_.isEqual(result, _.omit(distribution1, ['createdAt', 'updatedAt'])), true) - }) - - it('get distribution statistics successfully 2', async () => { - const response = await chai.request(app) - .get(`${basePath}/stats/distribution`) - should.equal(response.status, 200) - const result = response.body - should.equal(result.distribution.ratingRange0To099, - distribution1.distribution.ratingRange0To099 + distribution2.distribution.ratingRange0To099) - should.equal(result.distribution.ratingRange100To199, - distribution1.distribution.ratingRange100To199 + distribution2.distribution.ratingRange100To199) - should.exist(result.createdAt) - should.exist(result.createdBy) - should.exist(result.updatedAt) - should.exist(result.updatedBy) - }) - - it('get distribution statistics - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/stats/distribution`) - .query({ track: 'develop', subTrack: 'other' }) - should.equal(response.status, 404) - should.equal(response.body.message, 'No member distribution statistics is found.') - }) - - it('get distribution statistics - invalid field', async () => { - const response = await chai.request(app) - .get(`${basePath}/stats/distribution`) - .query({ fields: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('get distribution statistics - duplicate fields', async () => { - const response = await chai.request(app) - .get(`${basePath}/stats/distribution`) - .query({ fields: 'track,track' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: track') - }) - - it('get distribution statistics - empty field', async () => { - const response = await chai.request(app) - .get(`${basePath}/stats/distribution`) - .query({ fields: 'track, ,subTrack' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Empty value.') - }) - - it('get distribution statistics - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/stats/distribution`) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('get member history statistics API tests', () => { - it('get member history statistics successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats/history`) - .query({ - fields: 'userId,handle,handleLower,createdBy,updatedBy' - }) - should.equal(response.status, 200) - const result = response.body - should.equal(_.isEqual(result, _.pick(historyStats, - ['userId', 'handle', 'handleLower', 'createdBy', 'updatedBy'])), true) - should.not.exist(result.createdAt) - should.not.exist(result.updatedAt) - should.not.exist(result.DEVELOP) - should.not.exist(result.DATA_SCIENCE) - }) - - it('get member history statistics - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/other/stats/history`) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('get member history statistics - invalid field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats/history`) - .query({ fields: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('get member history statistics - duplicate fields', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats/history`) - .query({ fields: 'userId,createdAt,userId' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: userId') - }) - - it('get member history statistics - empty field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats/history`) - .query({ fields: 'userId, ,createdAt' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Empty value.') - }) - - it('get member history statistics - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats/history`) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('get member statistics API tests', () => { - it('get member statistics successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats`) - .query({ - fields: 'userId,handle,handleLower,maxRating,challenges,wins,copilot,createdBy,updatedBy' - }) - should.equal(response.status, 200) - const result = response.body - should.equal(_.isEqual(result, _.pick(memberStats, - ['userId', 'handle', 'handleLower', 'maxRating', 'challenges', - 'wins', 'copilot', 'createdBy', 'updatedBy'])), true) - should.not.exist(result.createdAt) - should.not.exist(result.updatedAt) - should.not.exist(result.develop) - should.not.exist(result.design) - should.not.exist(result.dataScience) - }) - - it('get member statistics - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/other/stats`) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('get member statistics - invalid field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats`) - .query({ fields: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('get member statistics - duplicate fields', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats`) - .query({ fields: 'userId,createdAt,userId' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: userId') - }) - - it('get member statistics - empty field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats`) - .query({ fields: 'userId, ,createdAt' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Empty value.') - }) - - it('get member statistics - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/stats`) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('get member skills API tests', () => { - it('get member skills successfully 1', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/skills`) - .query({ - fields: 'userId,handle,handleLower,skills,createdBy,updatedBy' - }) - should.equal(response.status, 200) - const result = response.body - should.equal(result.userId, memberSkills.userId) - should.equal(result.handle, memberSkills.handle) - should.equal(result.handleLower, memberSkills.handleLower) - should.equal(result.createdBy, memberSkills.createdBy) - should.equal(result.updatedBy, memberSkills.updatedBy) - should.not.exist(result.createdAt) - should.not.exist(result.updatedAt) - should.exist(result.skills) - should.exist(result.skills.Java) - should.not.exist(result.skills.NodeJS) - should.equal(_.isEqual(result.skills.Java, memberSkills.skills.Java), true) - should.equal(result.userId, memberSkills.userId) - should.equal(result.userId, memberSkills.userId) - }) - - it('get member skills - not found', async () => { - const response = await chai.request(app) - .get(`${basePath}/other/skills`) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('get member skills - invalid field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/skills`) - .query({ fields: 'invalid' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Invalid value: invalid') - }) - - it('get member skills - duplicate fields', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/skills`) - .query({ fields: 'userId,createdAt,userId' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Duplicate values: userId') - }) - - it('get member skills - empty field', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/skills`) - .query({ fields: 'userId, ,createdAt' }) - should.equal(response.status, 400) - should.equal(response.body.message, 'Empty value.') - }) - - it('get member skills - unexpected query parameter', async () => { - const response = await chai.request(app) - .get(`${basePath}/${member1.handle}/skills`) - .query({ other: 'abc' }) - should.equal(response.status, 400) - should.equal(response.body.message, '"other" is not allowed') - }) - }) - - describe('partially update member skills API tests', () => { - it('partially update member skills successfully 1', async () => { - const skills = { - Java: { - tagName: 'code', - hidden: false, - score: 1888, - sources: ['source1', 'source2'] - }, - NodeJS: { - tagName: 'code', - hidden: false, - score: 1999, - sources: ['source3', 'source4'] - } - } - const response = await chai.request(app) - .patch(`${basePath}/${member1.handle}/skills`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send(skills) - should.equal(response.status, 200) - const result = response.body - should.equal(result.userId, memberSkills.userId) - should.equal(result.handle, memberSkills.handle) - should.equal(result.handleLower, memberSkills.handleLower) - should.equal(_.isEqual(result.skills, skills), true) - should.exist(result.createdAt) - should.exist(result.updatedAt) - should.equal(result.createdBy, memberSkills.createdBy) - should.equal(result.updatedBy, 'TonyJ') - }) - - it('partially update member skills successfully 2', async () => { - const response = await chai.request(app) - .patch(`${basePath}/${member1.handle}/skills`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send({ - DotNet: { - tagName: 'code', - hidden: true, - score: 1777, - sources: ['source1', 'source3'] - } - }) - should.equal(response.status, 200) - const result = response.body - should.equal(result.userId, memberSkills.userId) - should.equal(result.handle, memberSkills.handle) - should.equal(result.handleLower, memberSkills.handleLower) - should.equal(_.isEqual(result.skills, { - Java: { - tagName: 'code', - hidden: false, - score: 1888, - sources: ['source1', 'source2'] - }, - NodeJS: { - tagName: 'code', - hidden: false, - score: 1999, - sources: ['source3', 'source4'] - }, - DotNet: { - tagName: 'code', - hidden: true, - score: 1777, - sources: ['source1', 'source3'] - } - }), true) - should.exist(result.createdAt) - should.exist(result.updatedAt) - should.equal(result.createdBy, memberSkills.createdBy) - should.equal(result.updatedBy, 'TonyJ') - }) - - it('partially update member skills - forbidden', async () => { - const response = await chai.request(app) - .patch(`${basePath}/${member1.handle}/skills`) - .set('Authorization', `Bearer ${config.USER_TOKEN}`) - .send({ - DotNet: { - tagName: 'code', - hidden: false, - score: 1777, - sources: ['source1', 'source3'] - } - }) - should.equal(response.status, 403) - should.equal(response.body.message, 'You are not allowed to perform this action!') - }) - - it('partially update member skills - not found', async () => { - const response = await chai.request(app) - .patch(`${basePath}/other/skills`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send({ - DotNet: { - tagName: 'code', - hidden: false, - score: 1777, - sources: ['source1', 'source3'] - } - }) - should.equal(response.status, 404) - should.equal(response.body.message, 'Member with handle: "other" doesn\'t exist') - }) - - it('partially update member skills - invalid request body', async () => { - const response = await chai.request(app) - .patch(`${basePath}/${member1.handle}/skills`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send(['abc']) - should.equal(response.status, 400) - should.equal(response.body.message, '"data" must be an object') - }) - - it('partially update member skills - missing request body', async () => { - const response = await chai.request(app) - .patch(`${basePath}/${member1.handle}/skills`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - should.equal(response.status, 400) - should.equal(response.body.message, '"data" must have at least 1 children') - }) - - it('partially update member skills - invalid skills sources', async () => { - const response = await chai.request(app) - .patch(`${basePath}/${member1.handle}/skills`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send({ - DotNet: { - tagName: 'code', - hidden: false, - score: 1777, - sources: 123 - } - }) - should.equal(response.status, 400) - should.equal(response.body.message, '"sources" must be an array') - }) - - it('partially update member skills - invalid skills score', async () => { - const response = await chai.request(app) - .patch(`${basePath}/${member1.handle}/skills`) - .set('Authorization', `Bearer ${config.ADMIN_TOKEN}`) - .send({ - DotNet: { - tagName: 'code', - hidden: false, - score: -1898, - sources: ['source1', 'source3'] - } - }) - should.equal(response.status, 400) - should.equal(response.body.message, '"score" must be larger than or equal to 0') - }) - }) -}) diff --git a/test/postman/clearTestData.js b/test/postman/clearTestData.js new file mode 100644 index 0000000..6fb987b --- /dev/null +++ b/test/postman/clearTestData.js @@ -0,0 +1,23 @@ +/** + * Clear the postman test data. All data created by postman e2e tests will be cleared. + */ +const logger = require('../../src/common/logger') +const helper = require('./testHelper') +const config = require('config') + +/** + * Clear the postman test data. The main function of this class. + * @returns {Promise} + */ +async function clearTestData () { + logger.info('Clear the Postman test data.') + await helper.postRequest(`${config.API_BASE_URL}/${config.API_VERSION}/internal/jobs/clean`) + .then(() => { + logger.info('Completed!') + process.exit() + }).catch((e) => { + logger.logFullError(e) + process.exit(1) + }) +} +clearTestData() diff --git a/test/postman/member-api.postman_collection.json b/test/postman/member-api.postman_collection.json new file mode 100644 index 0000000..44c4c65 --- /dev/null +++ b/test/postman/member-api.postman_collection.json @@ -0,0 +1,4824 @@ +{ + "info": { + "_postman_id": "3a871023-a0f9-4e0e-93ab-a696811c5550", + "name": "member-api", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Health", + "item": [ + { + "name": "health check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{URL}}/members/health", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "health" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Member", + "item": [ + { + "name": "Get Member", + "item": [ + { + "name": "get member successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "get member with fields successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "get member by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "get member by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "get member by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Search Member", + "item": [ + { + "name": "search members successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members", + "host": [ + "{{URL}}" + ], + "path": [ + "members" + ] + } + }, + "response": [] + }, + { + "name": "search members by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members", + "host": [ + "{{URL}}" + ], + "path": [ + "members" + ] + } + }, + "response": [] + }, + { + "name": "search members by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members", + "host": [ + "{{URL}}" + ], + "path": [ + "members" + ] + } + }, + "response": [] + }, + { + "name": "search members autocomplete successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/autocomplete", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "autocomplete" + ] + } + }, + "response": [] + }, + { + "name": "search members autocomplete successfully 2", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/search/members/autocomplete", + "host": [ + "{{URL}}" + ], + "path": [ + "search", + "members", + "autocomplete" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Update Member", + "item": [ + { + "name": "update member successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "update member by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "update member by no admin", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "update member by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "update member by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "update member by existent email", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "update member email successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}" + ] + } + }, + "response": [] + }, + { + "name": "verify email by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/verify", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "verify" + ] + } + }, + "response": [] + }, + { + "name": "verify email by unauthorized user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/verify", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "verify" + ] + } + }, + "response": [] + }, + { + "name": "verify email by wrong verification token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/verify", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "verify" + ] + } + }, + "response": [] + }, + { + "name": "verify email by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/verify", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "verify" + ] + } + }, + "response": [] + }, + { + "name": "get verification code form mock api", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " if(pm.response.status === \"OK\"){", + " const response = pm.response.json()", + " pm.environment.set(iterationData.get('emailVerifyToken'), response.emailVerifyToken);", + " pm.environment.set(iterationData.get('newEmailVerifyToken'), response.newEmailVerifyToken);", + " }", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{MOCK_URL}}/members/{{memberHandle}}/verify", + "host": [ + "{{MOCK_URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "verify" + ] + } + }, + "response": [] + }, + { + "name": "verify old email successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/verify", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "verify" + ] + } + }, + "response": [] + }, + { + "name": "verify new email successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/verify", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "verify" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Upload Photo", + "item": [ + { + "name": "upload photo successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('photoURL'))", + " pm.expect(rg.test(response.photoURL)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "photo", + "type": "file", + "src": "{{photo_path}}" + } + ] + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/photo", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "photo" + ] + } + }, + "response": [] + }, + { + "name": "upload photo by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "photo", + "type": "file", + "src": "{{photo_path}}" + } + ] + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/photo", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "photo" + ] + } + }, + "response": [] + }, + { + "name": "upload photo by unauthorized user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "photo", + "type": "file", + "src": "{{photo_path}}" + } + ] + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/photo", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "photo" + ] + } + }, + "response": [] + }, + { + "name": "upload photo by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "photo", + "type": "file", + "src": "{{photo_path}}" + } + ] + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/photo", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "photo" + ] + } + }, + "response": [] + }, + { + "name": "upload photo by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [] + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/photo", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "photo" + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Trait", + "item": [ + { + "name": "Create Trait", + "item": [ + { + "name": "create trait successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " _.each(response, trait => delete trait.createdAt)", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "create trait by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "create trait by unauthorized user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "create trait by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "create trait by existent trait", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "create trait by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Get Trait", + "item": [ + { + "name": "get traits successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "get traits by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "get traits by unauthorized user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "get traits by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "get traits by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Update Trait", + "item": [ + { + "name": "update trait successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " _.each(response, trait => {", + " delete trait.createdAt;", + " delete trait.updatedAt;", + " })", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "update trait by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "update trait by unauthorized user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "update trait by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "update trait by nonexistent trait", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "update trait by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Delete Trait", + "item": [ + { + "name": "delete trait by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "delete trait by unauthorized user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "delete trait by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "delete trait by nonexistent trait", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "delete trait by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + }, + { + "name": "delete trait successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/traits", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "traits" + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Statistics", + "item": [ + { + "name": "Get Distribution", + "item": [ + { + "name": "get distribution successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/stats/distribution", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "stats", + "distribution" + ] + } + }, + "response": [] + }, + { + "name": "get distribution by nonexistent track", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/stats/distribution", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "stats", + "distribution" + ] + } + }, + "response": [] + }, + { + "name": "get distribution by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/stats/distribution", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "stats", + "distribution" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Get Stats", + "item": [ + { + "name": "get member stats successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/stats", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "stats" + ] + } + }, + "response": [] + }, + { + "name": "get member stats by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/stats", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "stats" + ] + } + }, + "response": [] + }, + { + "name": "get member stats by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/stats", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "stats" + ] + } + }, + "response": [] + }, + { + "name": "get member history stats successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/stats/history", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "stats", + "history" + ] + } + }, + "response": [] + }, + { + "name": "get member history stats by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/stats/history", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "stats", + "history" + ] + } + }, + "response": [] + }, + { + "name": "get member history stats by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/stats/history", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "stats", + "history" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Get Skills", + "item": [ + { + "name": "get member skills successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "get member skills by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "get member skills by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const iterationData = pm.iterationData;\r", + "const params = iterationData.get('requestParameters')\r", + "Object.keys(params).forEach(param => {\r", + " pm.request.addQueryParams(`${param}=${params[param]}`) \r", + "})" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "update member skills successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " delete response.createdAt", + " delete response.updatedAt", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "update member skills by invalid token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "update member skills by nonexistent memberHandle", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "update member skills by unauthorized user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "update member skills by invalid field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " const rg = new RegExp(iterationData.get('message'))", + " pm.expect(rg.test(response.message)).to.be.true", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{{{token}}}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{requestBody}}" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/skills", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "skills" + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Misc", + "item": [ + { + "name": "Get Financial", + "item": [ + { + "name": "get financial successfully", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const iterationData = pm.iterationData;", + "const httpStatus = iterationData.get('httpStatus');", + "pm.test(`Status code is ${httpStatus}`, function () {", + " pm.response.to.have.status(httpStatus);", + " const response = pm.response.json()", + " pm.expect(response).to.deep.eq(iterationData.get('responseBody'));", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{URL}}/members/{{memberHandle}}/financial", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "{{memberHandle}}", + "financial" + ] + } + }, + "response": [] + } + ] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] +} \ No newline at end of file diff --git a/test/postman/member-api.postman_environment.json b/test/postman/member-api.postman_environment.json new file mode 100644 index 0000000..d13703c --- /dev/null +++ b/test/postman/member-api.postman_environment.json @@ -0,0 +1,142 @@ +{ + "_": { + "postman_variable_scope": "environment", + "postman_exported_at": "2021-07-30T04:17:43.432Z", + "postman_exported_using": "Newman/5.2.4" + }, + "id": "3fac136a-b265-4936-8ec9-a51fbc53f830", + "name": "member-api", + "values": [ + { + "type": "any", + "value": "http://localhost:3000/v5", + "key": "URL" + }, + { + "type": "any", + "value": "http://localhost:4000", + "key": "MOCK_URL" + }, + { + "type": "any", + "value": "Bearer eyJ", + "key": "invalid_token_1" + }, + { + "type": "any", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJjb3BpbG90IiwiQ29ubmVjdCBTdXBwb3J0Il0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJHaG9zdGFyIiwiZXhwIjoxNTQ5ODAwMDc3LCJ1c2VySWQiOiIxNTE3NDMiLCJpYXQiOjE1NDk3OTk0NzcsImVtYWlsIjoiZW1haWxAZG9tYWluLmNvbS56IiwianRpIjoiMTJjMWMxMGItOTNlZi00NTMxLTgzMDUtYmE2NjVmYzRlMWI0In0.2n8k9pb16sE7LOLF_7mjAvEVKgggzS-wS3_8n2-R4RU", + "key": "invalid_token_2" + }, + { + "type": "any", + "value": "Bearer", + "key": "invalid_token_3" + }, + { + "type": "any", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJjb3BpbG90IiwiQ29ubmVjdCBTdXBwb3J0Il0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJHaG9zdGFyIiwiZXhwIjoxNTQ5ODAwMDc3LCJ1c2VySWQiOiIxNTE3NDMiLCJpYXQiOjE1NDk3OTk0NzcsImVtYWlsIjoiZW1haWxAZG9tYWluLmNvbS56IiwianRpIjoiMTJjMWMxMGItOTNlZi00NTMxLTgzMDUtYmE2NjVmYzRlMWI0In0.2n8k9pb16sE7LOLF_7mjAvEVKgggzS-wS3_8n2-R4RU", + "key": "invalid_token_4" + }, + { + "type": "any", + "value": "test/photo.png", + "key": "photo_path" + }, + { + "type": "any", + "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZ1o2anQ1MEhZSExCZjR2aHhqVWhYUFpPUjdRNWxrNGtAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNjI3NjE4MTcyLCJleHAiOjE2Mjc3MDQ1NzIsImF6cCI6ImdaNmp0NTBIWUhMQmY0dmh4alVoWFBaT1I3UTVsazRrIiwic2NvcGUiOiJyZWFkOnVzZXJfaWRwX3Rva2VucyByZWFkOmNoYWxsZW5nZXMgd3JpdGU6Y2hhbGxlbmdlcyBhbGw6Z3JvdXBzIHdyaXRlOmdyb3VwcyByZWFkOmdyb3VwcyByZWFkOnJldmlld190eXBlIHJlYWQ6cmV2aWV3IHdyaXRlOnByb2plY3QgcmVhZDpwcm9qZWN0IHJlYWQ6YnVzX3RvcGljcyB3cml0ZTpidXNfYXBpIHJlYWQ6ZW1haWxfdGVtcGxhdGVzIHJlYWQ6dXNlcl9wcm9maWxlcyByZWFkOnJvbGVzIHJlYWQ6cHJlZmVyZW5jZXMgcmVhZDpwcm9qZWN0LXVzZXIgcmVhZDpwcm9qZWN0LXBlcm1pc3Npb24gcmVhZDpsb29rdXBzIHJlYWQ6cmVzb3VyY2VzIHdyaXRlOnJlc291cmNlcyB1cGRhdGU6cmVzb3VyY2VzIHJlYWQ6dGVybXMgcmVhZDpwcm9qZWN0cyB3cml0ZTpwcm9qZWN0cyByZWFkOnByb2plY3QtbWVtYmVycyB3cml0ZTpwcm9qZWN0LW1lbWJlcnMgcmVhZDphY2hpZXZlbWVudCByZWFkOmFjaGlldmVtZW50c1Byb3ZpZGVyIHJlYWQ6YXR0cmlidXRlIHJlYWQ6YXR0cmlidXRlR3JvdXAgYWxsOmF0dHJpYnV0ZUdyb3VwIGNyZWF0ZTphdHRyaWJ1dGVHcm91cCB1cGRhdGU6YXR0cmlidXRlR3JvdXAgcmVhZDpvcmdhbml6YXRpb24gcmVhZDpyb2xlIHJlYWQ6c2tpbGwgcmVhZDpza2lsbHNQcm92aWRlciByZWFkOnVzZXJBdHRyaWJ1dGUgcmVhZDp1c2Vyc1JvbGUgcmVhZDp1c2Vyc1NraWxsIHJlYWQ6Z3JvdXAgYWxsOmdyb3VwIHVwZGF0ZTpncm91cCBjcmVhdGU6Z3JvdXAgY3JlYXRlOmNoYWxsZW5nZXMgY3JlYXRlOnJlc291cmNlcyByZWFkOnVzZXIgcmVhZDp0YWFzLXRlYW1zIHJlYWQ6dGFhcy1qb2JzIGFsbDp0YWFzLWpvYnMgcmVhZDp0YWFzLWpvYkNhbmRpZGF0ZXMgYWxsOnRhYXMtam9iQ2FuZGlkYXRlcyBhbGw6dGFhcy1yZXNvdXJjZUJvb2tpbmdzIHJlYWQ6cHJvamVjdC1pbnZpdGVzIHdyaXRlOnByb2plY3QtaW52aXRlcyB1cGRhdGU6Y2hhbGxlbmdlcyByZWFkOnRhYXMtd29ya1BlcmlvZFBheW1lbnRzIGNyZWF0ZTp0YWFzLXdvcmtQZXJpb2RQYXltZW50cyB1cGRhdGU6dGFhcy13b3JrUGVyaW9kUGF5bWVudHMgYWxsOnRhYXMtd29ya1BlcmlvZFBheW1lbnRzIHJlYWQ6dGFhcy1pbnRlcnZpZXdzIGNyZWF0ZTp0YWFzLWludGVydmlld3MgdXBkYXRlOnRhYXMtaW50ZXJ2aWV3cyBhbGw6dGFhcy1pbnRlcnZpZXdzIHJlYWQ6dGFhcy13b3JrUGVyaW9kcyBjcmVhdGU6dGFhcy13b3JrUGVyaW9kcyB1cGRhdGU6dGFhcy13b3JrUGVyaW9kcyBhbGw6dGFhcy13b3JrUGVyaW9kcyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.JNHlJf583ZHRzy9yVXyDXvxDva5npr_Wl-MAHr3FSXGjtVXuxXzdVY1YVQRKiun5NiWXxwlRTH_MQY3jIHw6D4mJLa0Z0OdJSiNFIJ0h5mY347EqtRXt_NOKrIIYCGORk5OE5Z0dm6iXpqZtZPQXWsVi239ldm5fv6968f2M93H3Cy4fkVKX4J9T7lfMFbeYgXxxcgny4Z_bbJxqWRxqj-x3EMiPBddLxwV5uFbLnlIhy_4DhQD2CkXG223pX0BjgZSpHAdDmzpzNIKpm73H9zrLT145ZVyZ_t91BA4h3LgCP1RhV-jXCM2uOx0C2vc-GBIdOUILdZpQ1924WuJaUA", + "key": "m2m_token" + }, + { + "type": "any", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJjb3BpbG90IiwiYWRtaW5pc3RyYXRvciIsIkNvbm5lY3QgQWRtaW4iLCJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJqY29yaSIsImV4cCI6MTYyNzYxODc3OSwidXNlcklkIjoiNDAwMjk0ODQiLCJpYXQiOjE2Mjc2MTgxNzksImVtYWlsIjoiamNvcmkrZGV2QHRvcGNvZGVyLmNvbSIsImp0aSI6IjM4MGE3MzljLTBhNmMtNGJlNC1iODkxLWU4ZTc0YTBjZGZhOSJ9.xjr4LeS3DRkaFMCG8Me_GxAGJjWtzG_FFZUaFdzAPL8", + "key": "admin_token" + }, + { + "type": "any", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJDb25uZWN0IENvcGlsb3QiLCJjb3BpbG90IiwiVG9wY29kZXIgVXNlciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoiVENDb25uQ29waWxvdCIsImV4cCI6MTYyNzYxODc4OSwidXNlcklkIjoiNDAxNTg5OTQiLCJpYXQiOjE2Mjc2MTgxODksImVtYWlsIjoidG9wY29kZXJjb25uZWN0K0NvcGlsb3RAZ21haWwuY29tIiwianRpIjoiMzc2Y2JjODctZjFhYy00OTY2LTkwOTMtMDFmNGE0MTU5NTk3In0.cy_yR3hiPXaNRPGXXHcC4sRIune1Qu_KiwENIPt6_SY", + "key": "copilot_token" + }, + { + "type": "any", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJpc2JpbGlyIiwiZXhwIjoxNjI3NjE4Nzk0LCJ1c2VySWQiOiI4ODc3NDYzNCIsImlhdCI6MTYyNzYxODE5NCwiZW1haWwiOiJlbXJlLmlzYmlsaXJAZ21haWwuY29tIiwianRpIjoiY2VmOTU3NzQtMzQ2Yi00ODQwLWIyYjItM2I1MThlNWQxOWY2In0.Vblh8FF5Foq80tWQ9dL-CL2Mlatr3JhjyCTQtCeaFfY", + "key": "user_token" + }, + { + "type": "any", + "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZ1o2anQ1MEhZSExCZjR2aHhqVWhYUFpPUjdRNWxrNGtAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNjI3NjE4MTcyLCJleHAiOjE2Mjc3MDQ1NzIsImF6cCI6ImdaNmp0NTBIWUhMQmY0dmh4alVoWFBaT1I3UTVsazRrIiwic2NvcGUiOiJyZWFkOnVzZXJfaWRwX3Rva2VucyByZWFkOmNoYWxsZW5nZXMgd3JpdGU6Y2hhbGxlbmdlcyBhbGw6Z3JvdXBzIHdyaXRlOmdyb3VwcyByZWFkOmdyb3VwcyByZWFkOnJldmlld190eXBlIHJlYWQ6cmV2aWV3IHdyaXRlOnByb2plY3QgcmVhZDpwcm9qZWN0IHJlYWQ6YnVzX3RvcGljcyB3cml0ZTpidXNfYXBpIHJlYWQ6ZW1haWxfdGVtcGxhdGVzIHJlYWQ6dXNlcl9wcm9maWxlcyByZWFkOnJvbGVzIHJlYWQ6cHJlZmVyZW5jZXMgcmVhZDpwcm9qZWN0LXVzZXIgcmVhZDpwcm9qZWN0LXBlcm1pc3Npb24gcmVhZDpsb29rdXBzIHJlYWQ6cmVzb3VyY2VzIHdyaXRlOnJlc291cmNlcyB1cGRhdGU6cmVzb3VyY2VzIHJlYWQ6dGVybXMgcmVhZDpwcm9qZWN0cyB3cml0ZTpwcm9qZWN0cyByZWFkOnByb2plY3QtbWVtYmVycyB3cml0ZTpwcm9qZWN0LW1lbWJlcnMgcmVhZDphY2hpZXZlbWVudCByZWFkOmFjaGlldmVtZW50c1Byb3ZpZGVyIHJlYWQ6YXR0cmlidXRlIHJlYWQ6YXR0cmlidXRlR3JvdXAgYWxsOmF0dHJpYnV0ZUdyb3VwIGNyZWF0ZTphdHRyaWJ1dGVHcm91cCB1cGRhdGU6YXR0cmlidXRlR3JvdXAgcmVhZDpvcmdhbml6YXRpb24gcmVhZDpyb2xlIHJlYWQ6c2tpbGwgcmVhZDpza2lsbHNQcm92aWRlciByZWFkOnVzZXJBdHRyaWJ1dGUgcmVhZDp1c2Vyc1JvbGUgcmVhZDp1c2Vyc1NraWxsIHJlYWQ6Z3JvdXAgYWxsOmdyb3VwIHVwZGF0ZTpncm91cCBjcmVhdGU6Z3JvdXAgY3JlYXRlOmNoYWxsZW5nZXMgY3JlYXRlOnJlc291cmNlcyByZWFkOnVzZXIgcmVhZDp0YWFzLXRlYW1zIHJlYWQ6dGFhcy1qb2JzIGFsbDp0YWFzLWpvYnMgcmVhZDp0YWFzLWpvYkNhbmRpZGF0ZXMgYWxsOnRhYXMtam9iQ2FuZGlkYXRlcyBhbGw6dGFhcy1yZXNvdXJjZUJvb2tpbmdzIHJlYWQ6cHJvamVjdC1pbnZpdGVzIHdyaXRlOnByb2plY3QtaW52aXRlcyB1cGRhdGU6Y2hhbGxlbmdlcyByZWFkOnRhYXMtd29ya1BlcmlvZFBheW1lbnRzIGNyZWF0ZTp0YWFzLXdvcmtQZXJpb2RQYXltZW50cyB1cGRhdGU6dGFhcy13b3JrUGVyaW9kUGF5bWVudHMgYWxsOnRhYXMtd29ya1BlcmlvZFBheW1lbnRzIHJlYWQ6dGFhcy1pbnRlcnZpZXdzIGNyZWF0ZTp0YWFzLWludGVydmlld3MgdXBkYXRlOnRhYXMtaW50ZXJ2aWV3cyBhbGw6dGFhcy1pbnRlcnZpZXdzIHJlYWQ6dGFhcy13b3JrUGVyaW9kcyBjcmVhdGU6dGFhcy13b3JrUGVyaW9kcyB1cGRhdGU6dGFhcy13b3JrUGVyaW9kcyBhbGw6dGFhcy13b3JrUGVyaW9kcyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.JNHlJf583ZHRzy9yVXyDXvxDva5npr_Wl-MAHr3FSXGjtVXuxXzdVY1YVQRKiun5NiWXxwlRTH_MQY3jIHw6D4mJLa0Z0OdJSiNFIJ0h5mY347EqtRXt_NOKrIIYCGORk5OE5Z0dm6iXpqZtZPQXWsVi239ldm5fv6968f2M93H3Cy4fkVKX4J9T7lfMFbeYgXxxcgny4Z_bbJxqWRxqj-x3EMiPBddLxwV5uFbLnlIhy_4DhQD2CkXG223pX0BjgZSpHAdDmzpzNIKpm73H9zrLT145ZVyZ_t91BA4h3LgCP1RhV-jXCM2uOx0C2vc-GBIdOUILdZpQ1924WuJaUA", + "key": "M2M_TOKEN" + }, + { + "type": "any", + "value": { + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 200, + "responseBody": { + "message": "No Data" + } + }, + "key": "0" + }, + { + "type": "any", + "value": { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": "{\"tagName\":\"311\"}", + "httpStatus": 400, + "message": "\"tagName\" must be an object" + }, + "key": "1" + }, + { + "type": "any", + "value": { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": "{\"212\":{\"max\":1455}}", + "httpStatus": 400, + "message": "\"max\" is not allowed" + }, + "key": "2" + }, + { + "type": "any", + "value": { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + }, + "key": "3" + }, + { + "type": "any", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJDb25uZWN0IFN1cHBvcnQiLCJ0ZXN0Um9sZSIsImFhYSIsInRvbnlfdGVzdF8xIiwiQ29ubmVjdCBNYW5hZ2VyIiwiQ29ubmVjdCBBZG1pbiIsImNvcGlsb3QiLCJDb25uZWN0IENvcGlsb3QgTWFuYWdlciIsIlRvcGNvZGVyIFVzZXIiLCJhZG1pbmlzdHJhdG9yIiwidS1iYWhuIiwiYm9va2luZ21hbmFnZXIiXSwiaXNzIjoiaHR0cHM6Ly9hcGkudG9wY29kZXItZGV2LmNvbSIsImhhbmRsZSI6IlRvbnlKIiwiZXhwIjoxNjI3NjE4Nzg0LCJ1c2VySWQiOiI4NTQ3ODk5IiwiaWF0IjoxNjI3NjE4MTg0LCJlbWFpbCI6ImFqZWZ0c0B0b3Bjb2Rlci5jb20iLCJqdGkiOiIxZDAyZGMwYy03NjJkLTRjZjItOTU3NC02NmUyZWEwMmRkYjcifQ.0ruRxtsyOKsKLOOElW8sfTaBGkMKwdq3bD57uKYg5-E", + "key": "manager_token" + }, + { + "type": "any", + "value": "58ac95c6-cf95-4917-9c46-791f340817c4", + "key": "emailVerifyToken-1" + }, + { + "type": "any", + "value": "97330706-afd9-4436-a6a0-153740afe007", + "key": "newEmailVerifyToken-1" + }, + { + "type": "any", + "value": "618ebe4e-7af8-4c0a-8140-7df79add1e36", + "key": "emailVerifyToken-2" + }, + { + "type": "any", + "value": "da93aa16-78e6-4ab3-86a0-94d4f43ba54b", + "key": "newEmailVerifyToken-2" + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2021-07-30T04:17:49.796Z", + "_postman_exported_using": "Newman/5.2.4" +} \ No newline at end of file diff --git a/test/postman/newman.js b/test/postman/newman.js new file mode 100644 index 0000000..e79b4b9 --- /dev/null +++ b/test/postman/newman.js @@ -0,0 +1,332 @@ +const config = require('config') +const apiTestLib = require('tc-api-testing-lib') +const helper = require('./testHelper') +const logger = require('../../src/common/logger') + +const healthCheckRequests = [ + { + folder: 'health check', + iterationData: require('./testData/health/health-check.json') + } +] + +const memberRequests = [ + { + folder: 'get member successfully', + iterationData: require('./testData/member/get-member-successfully.json') + }, + { + folder: 'get member with fields successfully', + iterationData: require('./testData/member/get-member-with-fields-successfully.json') + }, + { + folder: 'get member by invalid token', + iterationData: require('./testData/member/get-member-by-invalid-token.json') + }, + { + folder: 'get member by invalid field', + iterationData: require('./testData/member/get-member-by-invalid-field.json') + }, + { + folder: 'get member by nonexistent memberHandle', + iterationData: require('./testData/member/get-member-by-nonexistent-memberHandle.json') + }, + { + folder: 'search members successfully', + iterationData: require('./testData/member/search-members-successfully.json') + }, + { + folder: 'search members by invalid token', + iterationData: require('./testData/member/search-members-by-invalid-token.json') + }, + { + folder: 'search members autocomplete successfully', + iterationData: require('./testData/member/search-members-autocomplete-successfully.json') + }, + { + folder: 'search members autocomplete successfully 2', + iterationData: require('./testData/member/search-members-autocomplete-successfully-2.json') + }, + { + folder: 'update member successfully', + iterationData: require('./testData/member/update-member-successfully.json') + }, + { + folder: 'update member by invalid token', + iterationData: require('./testData/member/update-member-by-invalid-token.json') + }, + { + folder: 'update member by no admin', + iterationData: require('./testData/member/update-member-by-no-admin.json') + }, + { + folder: 'update member by nonexistent memberHandle', + iterationData: require('./testData/member/update-member-by-nonexistent-memberHandle.json') + }, + { + folder: 'update member by invalid field', + iterationData: require('./testData/member/update-member-by-invalid-field.json') + }, + { + folder: 'update member by existent email', + iterationData: require('./testData/member/update-member-by-existent-email.json') + }, + { + folder: 'update member email successfully', + iterationData: require('./testData/member/update-member-email-successfully.json') + }, + { + folder: 'verify email by invalid token', + iterationData: require('./testData/member/verify-email-by-invalid-token.json') + }, + { + folder: 'verify email by unauthorized user', + iterationData: require('./testData/member/verify-email-by-unauthorized-user.json') + }, + { + folder: 'verify email by wrong verification token', + iterationData: require('./testData/member/verify-email-by-wrong-verification-token.json') + }, + { + folder: 'verify email by nonexistent memberHandle', + iterationData: require('./testData/member/verify-email-by-nonexistent-memberHandle.json') + }, + { + folder: 'get verification code form mock api', + iterationData: require('./testData/member/get-verification-code-from-mock-api.json') + }, + { + folder: 'verify old email successfully', + iterationData: require('./testData/member/verify-old-email-successfully.json') + }, + { + folder: 'verify new email successfully', + iterationData: require('./testData/member/verify-new-email-successfully.json') + }, + { + folder: 'upload photo successfully', + iterationData: require('./testData/member/upload-photo-successfully.json') + }, + { + folder: 'upload photo by invalid token', + iterationData: require('./testData/member/upload-photo-by-invalid-token.json') + }, + { + folder: 'upload photo by unauthorized user', + iterationData: require('./testData/member/upload-photo-by-unauthorized-user.json') + }, + { + folder: 'upload photo by nonexistent memberHandle', + iterationData: require('./testData/member/upload-photo-by-nonexistent-memberHandle.json') + }, + { + folder: 'upload photo by invalid field', + iterationData: require('./testData/member/upload-photo-by-invalid-field.json') + } +] + +const traitRequests = [ + { + folder: 'create trait successfully', + iterationData: require('./testData/trait/create-trait-successfully.json') + }, + { + folder: 'create trait by invalid token', + iterationData: require('./testData/trait/create-trait-by-invalid-token.json') + }, + { + folder: 'create trait by unauthorized user', + iterationData: require('./testData/trait/create-trait-by-unauthorized-user.json') + }, + { + folder: 'create trait by nonexistent memberHandle', + iterationData: require('./testData/trait/create-trait-by-nonexistent-memberHandle.json') + }, + { + folder: 'create trait by existent trait', + iterationData: require('./testData/trait/create-trait-by-existent-trait.json') + }, + { + folder: 'create trait by invalid field', + iterationData: require('./testData/trait/create-trait-by-invalid-field.json') + }, + { + folder: 'get traits successfully', + iterationData: require('./testData/trait/get-traits-successfully.json') + }, + { + folder: 'get traits by invalid token', + iterationData: require('./testData/trait/get-traits-by-invalid-token.json') + }, + { + folder: 'get traits by unauthorized user', + iterationData: require('./testData/trait/get-traits-by-unauthorized-user.json') + }, + { + folder: 'get traits by nonexistent memberHandle', + iterationData: require('./testData/trait/get-traits-by-nonexistent-memberHandle.json') + }, + { + folder: 'get traits by invalid field', + iterationData: require('./testData/trait/get-traits-by-invalid-field.json') + }, + { + folder: 'update trait successfully', + iterationData: require('./testData/trait/update-trait-successfully.json') + }, + { + folder: 'update trait by invalid token', + iterationData: require('./testData/trait/update-trait-by-invalid-token.json') + }, + { + folder: 'update trait by unauthorized user', + iterationData: require('./testData/trait/update-trait-by-unauthorized-user.json') + }, + { + folder: 'update trait by nonexistent memberHandle', + iterationData: require('./testData/trait/update-trait-by-nonexistent-memberHandle.json') + }, + { + folder: 'update trait by nonexistent trait', + iterationData: require('./testData/trait/update-trait-by-nonexistent-trait.json') + }, + { + folder: 'update trait by invalid field', + iterationData: require('./testData/trait/update-trait-by-invalid-field.json') + }, + { + folder: 'delete trait by invalid token', + iterationData: require('./testData/trait/delete-trait-by-invalid-token.json') + }, + { + folder: 'delete trait by unauthorized user', + iterationData: require('./testData/trait/delete-trait-by-unauthorized-user.json') + }, + { + folder: 'delete trait by nonexistent memberHandle', + iterationData: require('./testData/trait/delete-trait-by-nonexistent-memberHandle.json') + }, + { + folder: 'delete trait by nonexistent trait', + iterationData: require('./testData/trait/delete-trait-by-nonexistent-trait.json') + }, + { + folder: 'delete trait by invalid field', + iterationData: require('./testData/trait/delete-trait-by-invalid-field.json') + }, + { + folder: 'delete trait successfully', + iterationData: require('./testData/trait/delete-trait-successfully.json') + } +] + +const statisticsRequests = [ + { + folder: 'get distribution successfully', + iterationData: require('./testData/statistics/get-distribution-successfully.json') + }, + { + folder: 'get distribution by nonexistent track', + iterationData: require('./testData/statistics/get-distribution-by-nonexistent-track.json') + }, + { + folder: 'get distribution by invalid field', + iterationData: require('./testData/statistics/get-distribution-by-invalid-field.json') + }, + { + folder: 'get member stats successfully', + iterationData: require('./testData/statistics/get-member-stats-successfully.json') + }, + { + folder: 'get member stats by nonexistent memberHandle', + iterationData: require('./testData/statistics/get-member-stats-by-nonexistent-memberHandle.json') + }, + { + folder: 'get member stats by invalid field', + iterationData: require('./testData/statistics/get-member-stats-by-invalid field.json') + }, + { + folder: 'get member history stats successfully', + iterationData: require('./testData/statistics/get-member-history-stats-successfully.json') + }, + { + folder: 'get member history stats by nonexistent memberHandle', + iterationData: require('./testData/statistics/get-member-history-stats-by-nonexistent-memberHandle.json') + }, + { + folder: 'get member history stats by invalid field', + iterationData: require('./testData/statistics/get-member-history-stats-by-invalid field.json') + }, + { + folder: 'get member skills successfully', + iterationData: require('./testData/statistics/get-member-skills-successfully.json') + }, + { + folder: 'get member skills by nonexistent memberHandle', + iterationData: require('./testData/statistics/get-member-skills-by-nonexistent-memberHandle.json') + }, + { + folder: 'get member skills by invalid field', + iterationData: require('./testData/statistics/get-member-skills-by-invalid field.json') + }, + { + folder: 'update member skills successfully', + iterationData: require('./testData/statistics/update-member-skills-successfully.json') + }, + { + folder: 'update member skills by invalid token', + iterationData: require('./testData/statistics/update-member-skills-by-invalid-token.json') + }, + { + folder: 'update member skills by nonexistent memberHandle', + iterationData: require('./testData/statistics/update-member-skills-by-nonexistent-memberHandle.json') + }, + { + folder: 'update member skills by unauthorized user', + iterationData: require('./testData/statistics/update-member-skills-by-unauthorized-user.json') + }, + { + folder: 'update member skills by invalid field', + iterationData: require('./testData/statistics/update-member-skills-by-invalid-field.json') + } +] + +const miscRequests = [ + { + folder: 'get financial successfully', + iterationData: require('./testData/misc/get-financial-successfully.json') + } +] + +const requests = [ + ...healthCheckRequests, + ...memberRequests, + ...traitRequests, + ...statisticsRequests, + ...miscRequests +] + +/** + * Clear the test data. + * @return {Promise} + */ +async function clearTestData () { + logger.info('Clear the Postman test data.') + await helper.postRequest(`${config.API_BASE_URL}/${config.API_VERSION}/internal/jobs/clean`) + logger.info('Finished clear the Postman test data.') +} + +/** + * Run the postman tests. + */ +apiTestLib.runTests(requests, require.resolve('./member-api.postman_collection.json'), + require.resolve('./member-api.postman_environment.json')).then(async () => { + logger.info('newman test completed!') + await clearTestData() +}).catch(async (err) => { + logger.logFullError(err) + // Only calling the clean up function when it is not validation error. + if (err.name !== 'ValidationError') { + await clearTestData() + } +}) diff --git a/test/postman/testData/health/health-check.json b/test/postman/testData/health/health-check.json new file mode 100644 index 0000000..007405d --- /dev/null +++ b/test/postman/testData/health/health-check.json @@ -0,0 +1,5 @@ +[ + { + "httpStatus": 200 + } +] \ No newline at end of file diff --git a/test/postman/testData/member/get-member-by-invalid-field.json b/test/postman/testData/member/get-member-by-invalid-field.json new file mode 100644 index 0000000..58b136e --- /dev/null +++ b/test/postman/testData/member/get-member-by-invalid-field.json @@ -0,0 +1,29 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "photoURL,maxRating,," + }, + "httpStatus": 400, + "message": "Empty value." + }, + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "photoURL,maxRating,maxRating" + }, + "httpStatus": 400, + "message": "Duplicate values: maxRating" + }, + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "photoURL,maxRating,password" + }, + "httpStatus": 400, + "message": "Invalid value: password" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/get-member-by-invalid-token.json b/test/postman/testData/member/get-member-by-invalid-token.json new file mode 100644 index 0000000..6a1ed1b --- /dev/null +++ b/test/postman/testData/member/get-member-by-invalid-token.json @@ -0,0 +1,26 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/get-member-by-nonexistent-memberHandle.json b/test/postman/testData/member/get-member-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..192cad8 --- /dev/null +++ b/test/postman/testData/member/get-member-by-nonexistent-memberHandle.json @@ -0,0 +1,8 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-nothere", + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/get-member-successfully.json b/test/postman/testData/member/get-member-successfully.json new file mode 100644 index 0000000..5162114 --- /dev/null +++ b/test/postman/testData/member/get-member-successfully.json @@ -0,0 +1,142 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 200, + "responseBody": { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "tracks": [ + "code" + ], + "status": "ACTIVE", + "addresses": [ + { + "streetAddr1": "addr1", + "streetAddr2": "addr2", + "city": "NY", + "zip": "123123", + "stateCode": "A", + "type": "type", + "updatedAt": "2020-02-06T07:38:50.088Z", + "createdAt": "2020-02-06T07:38:50.088Z", + "createdBy": "test", + "updatedBy": "test" + } + ], + "description": "desc", + "email": "denis@topcoder.com", + "homeCountryCode": "US", + "competitionCountryCode": "US", + "photoURL": "http://test.com/abc.png", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "createdAt": "2020-02-06T07:38:50.088Z", + "createdBy": "test1", + "updatedAt": "2020-02-06T07:38:50.088Z", + "updatedBy": "test2" + } + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 200, + "responseBody": { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "tracks": [ + "code" + ], + "status": "ACTIVE", + "description": "desc", + "homeCountryCode": "US", + "competitionCountryCode": "US", + "photoURL": "http://test.com/abc.png", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-testing", + "httpStatus": 200, + "responseBody": { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "tracks": [ + "code" + ], + "status": "ACTIVE", + "description": "desc 2", + "homeCountryCode": "US", + "competitionCountryCode": "US", + "photoURL": "http://test.com/def.png", + "maxRating": { + "rating": 1500, + "track": "dev", + "subTrack": "code" + }, + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + }, + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-testing", + "httpStatus": 200, + "responseBody": { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "tracks": [ + "code" + ], + "status": "ACTIVE", + "addresses": [ + { + "streetAddr1": "addr1", + "streetAddr2": "addr2", + "city": "NY", + "zip": "123123", + "stateCode": "A", + "type": "type", + "updatedAt": "2020-02-06T07:38:50.088Z", + "createdAt": "2020-02-06T07:38:50.088Z", + "createdBy": "test", + "updatedBy": "test" + } + ], + "description": "desc 2", + "email": "testing@topcoder.com", + "homeCountryCode": "US", + "competitionCountryCode": "US", + "photoURL": "http://test.com/def.png", + "maxRating": { + "rating": 1500, + "track": "dev", + "subTrack": "code" + }, + "createdAt": "2020-02-06T07:38:50.088Z", + "createdBy": "test1", + "updatedAt": "2020-02-06T07:38:50.088Z", + "updatedBy": "test2" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/member/get-member-with-fields-successfully.json b/test/postman/testData/member/get-member-with-fields-successfully.json new file mode 100644 index 0000000..7267802 --- /dev/null +++ b/test/postman/testData/member/get-member-with-fields-successfully.json @@ -0,0 +1,87 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "fields": "userId,handle,handleLower,firstName,lastName" + }, + "httpStatus": 200, + "responseBody": { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2" + } + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "fields": "tracks,status,description,homeCountryCode,competitionCountryCode" + }, + "httpStatus": 200, + "responseBody": { + "tracks": [ + "code" + ], + "status": "ACTIVE", + "description": "desc 2", + "homeCountryCode": "US", + "competitionCountryCode": "US" + } + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "photoURL,maxRating,handle,handleLower" + }, + "httpStatus": 200, + "responseBody": { + "photoURL": "http://test.com/abc.png", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis" + } + }, + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "photoURL,maxRating,handle,handleLower,addresses,email" + }, + "httpStatus": 200, + "responseBody": { + "photoURL": "http://test.com/abc.png", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "addresses": [ + { + "streetAddr1": "addr1", + "streetAddr2": "addr2", + "city": "NY", + "zip": "123123", + "stateCode": "A", + "type": "type", + "updatedAt": "2020-02-06T07:38:50.088Z", + "createdAt": "2020-02-06T07:38:50.088Z", + "createdBy": "test", + "updatedBy": "test" + } + ], + "email": "denis@topcoder.com" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/member/get-verification-code-from-mock-api.json b/test/postman/testData/member/get-verification-code-from-mock-api.json new file mode 100644 index 0000000..5270e88 --- /dev/null +++ b/test/postman/testData/member/get-verification-code-from-mock-api.json @@ -0,0 +1,16 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 200, + "emailVerifyToken": "emailVerifyToken-1", + "newEmailVerifyToken": "newEmailVerifyToken-1" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "httpStatus": 200, + "emailVerifyToken": "emailVerifyToken-2", + "newEmailVerifyToken": "newEmailVerifyToken-2" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/search-members-autocomplete-successfully-2.json b/test/postman/testData/member/search-members-autocomplete-successfully-2.json new file mode 100644 index 0000000..bd6b67f --- /dev/null +++ b/test/postman/testData/member/search-members-autocomplete-successfully-2.json @@ -0,0 +1,138 @@ +[ + { + "token": "admin_token", + "requestParameters": { + "term": "post", + "page": 1, + "perPage": 5, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + }, + { + "token": "m2m_token", + "requestParameters": { + "term": "post", + "page": 1, + "perPage": 10, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + }, + { + "token": "copilot_token", + "requestParameters": { + "term": "postman", + "page": 1, + "perPage": 10, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + }, + { + "token": "user_token", + "requestParameters": { + "term": "postman", + "page": 1, + "perPage": 10, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testData/member/search-members-autocomplete-successfully.json b/test/postman/testData/member/search-members-autocomplete-successfully.json new file mode 100644 index 0000000..bd6b67f --- /dev/null +++ b/test/postman/testData/member/search-members-autocomplete-successfully.json @@ -0,0 +1,138 @@ +[ + { + "token": "admin_token", + "requestParameters": { + "term": "post", + "page": 1, + "perPage": 5, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + }, + { + "token": "m2m_token", + "requestParameters": { + "term": "post", + "page": 1, + "perPage": 10, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + }, + { + "token": "copilot_token", + "requestParameters": { + "term": "postman", + "page": 1, + "perPage": 10, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + }, + { + "token": "user_token", + "requestParameters": { + "term": "postman", + "page": 1, + "perPage": 10, + "fields": "userId,handle,handleLower,firstName,lastName,status,email,createdAt,updatedAt" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "status": "ACTIVE", + "email": "denis@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + }, + { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name 2", + "lastName": "last name 2", + "status": "ACTIVE", + "email": "testing@topcoder.com", + "createdAt": "2020-02-06T07:38:50.088Z", + "updatedAt": "2020-02-06T07:38:50.088Z" + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testData/member/search-members-by-invalid-field.json b/test/postman/testData/member/search-members-by-invalid-field.json new file mode 100644 index 0000000..d7a54aa --- /dev/null +++ b/test/postman/testData/member/search-members-by-invalid-field.json @@ -0,0 +1,26 @@ +[ + { + "token": "m2m_token", + "requestParameters": { + "fields": "photoURL,maxRating,," + }, + "httpStatus": 400, + "message": "Empty value." + }, + { + "token": "m2m_token", + "requestParameters": { + "fields": "photoURL,maxRating,maxRating" + }, + "httpStatus": 400, + "message": "Duplicate values: maxRating" + }, + { + "token": "m2m_token", + "requestParameters": { + "fields": "photoURL,maxRating,password" + }, + "httpStatus": 400, + "message": "Invalid value: password" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/search-members-by-invalid-token.json b/test/postman/testData/member/search-members-by-invalid-token.json new file mode 100644 index 0000000..b7b1a28 --- /dev/null +++ b/test/postman/testData/member/search-members-by-invalid-token.json @@ -0,0 +1,22 @@ +[ + { + "token": "invalid_token_1", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/search-members-successfully.json b/test/postman/testData/member/search-members-successfully.json new file mode 100644 index 0000000..3c78125 --- /dev/null +++ b/test/postman/testData/member/search-members-successfully.json @@ -0,0 +1,87 @@ +[ + { + "token": "admin_token", + "requestParameters": { + "handle": "POSTMANE2E-denis", + "fields": "userId,status,handle,handleLower,firstName,lastName,homeCountryCode,skills,stats,email" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "status": "ACTIVE", + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name", + "lastName": "last name", + "homeCountryCode": "US", + "skills": {}, + "stats": [], + "email": "denis@topcoder.com" + } + ] + }, + { + "token": "m2m_token", + "requestParameters": { + "handleLower": "postmane2e-denis", + "fields": "competitionCountryCode,status,handle,tracks,firstName,lastName,homeCountryCode,skills,stats,email" + }, + "httpStatus": 200, + "responseBody": [ + { + "competitionCountryCode": "US", + "status": "ACTIVE", + "handle": "POSTMANE2E-denis", + "tracks": [ + "code" + ], + "firstName": "first name", + "lastName": "last name", + "homeCountryCode": "US", + "skills": {}, + "stats": [], + "email": "denis@topcoder.com" + } + ] + }, + { + "token": "copilot_token", + "requestParameters": { + "userId": 123, + "fields": "userId,status,handle,handleLower,description,lastName,homeCountryCode,skills,stats,email" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "status": "ACTIVE", + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "description": "desc", + "homeCountryCode": "US", + "skills": {}, + "stats": [] + } + ] + }, + { + "token": "user_token", + "requestParameters": { + "userId": 456, + "fields": "photoURL,status,handle,handleLower,firstName,lastName,competitionCountryCode,skills,stats,email" + }, + "httpStatus": 200, + "responseBody": [ + { + "photoURL": "http://test.com/def.png", + "status": "ACTIVE", + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "competitionCountryCode": "US", + "skills": {}, + "stats": [] + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testData/member/update-member-by-existent-email.json b/test/postman/testData/member/update-member-by-existent-email.json new file mode 100644 index 0000000..9f2e5ee --- /dev/null +++ b/test/postman/testData/member/update-member-by-existent-email.json @@ -0,0 +1,20 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "email": "testing@topcoder.com" + }, + "httpStatus": 409, + "message": "Email \"testing@topcoder.com\" is already registered" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": { + "email": "denis@topcoder.com" + }, + "httpStatus": 409, + "message": "Email \"denis@topcoder.com\" is already registered" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/update-member-by-invalid-field.json b/test/postman/testData/member/update-member-by-invalid-field.json new file mode 100644 index 0000000..5989380 --- /dev/null +++ b/test/postman/testData/member/update-member-by-invalid-field.json @@ -0,0 +1,36 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId" + }, + "requestBody": { + "email": "asd" + }, + "httpStatus": 400, + "message": "\"email\" must be a valid email" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId" + }, + "requestBody": { + "photoURL": "asd" + }, + "httpStatus": 400, + "message": "\"photoURL\" must be a valid uri" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "asd" + }, + "requestBody": {}, + "httpStatus": 400, + "message": "Invalid value: asd" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/update-member-by-invalid-token.json b/test/postman/testData/member/update-member-by-invalid-token.json new file mode 100644 index 0000000..6c7ecb2 --- /dev/null +++ b/test/postman/testData/member/update-member-by-invalid-token.json @@ -0,0 +1,114 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/update-member-by-no-admin.json b/test/postman/testData/member/update-member-by-no-admin.json new file mode 100644 index 0000000..4afd90c --- /dev/null +++ b/test/postman/testData/member/update-member-by-no-admin.json @@ -0,0 +1,86 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 403, + "message": "You are not allowed to perform this action!" + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 403, + "message": "You are not allowed to update the member." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 403, + "message": "You are not allowed to update the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/update-member-by-nonexistent-memberHandle.json b/test/postman/testData/member/update-member-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..94354a8 --- /dev/null +++ b/test/postman/testData/member/update-member-by-nonexistent-memberHandle.json @@ -0,0 +1,12 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestParameters": { + "fields": "userId" + }, + "requestBody": {}, + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/update-member-email-successfully.json b/test/postman/testData/member/update-member-email-successfully.json new file mode 100644 index 0000000..9156745 --- /dev/null +++ b/test/postman/testData/member/update-member-email-successfully.json @@ -0,0 +1,70 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANe2e-denis", + "requestParameters": { + "fields": "userId,handle,handleLower,firstName,lastName,tracks,status,addresses,description,email,homeCountryCode,competitionCountryCode,photoURL,maxRating,createdAt,createdBy,updatedBy" + }, + "requestBody": { + "email": "denis_new@topcoder.com" + }, + "httpStatus": 200, + "responseBody": { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name edit", + "lastName": "last name edit", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "status": "ACTIVE", + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "description": "What goes around comes around", + "email": "denis@topcoder.com", + "homeCountryCode": "IRL", + "competitionCountryCode": "IRL", + "photoURL": "http://test.com/abc.png", + "maxRating": { + "rating": 1000, + "track": "dev", + "subTrack": "code" + }, + "createdAt": "2020-02-06T07:38:50.088Z", + "createdBy": "test1", + "updatedBy": "40029484" + } + }, + { + "token": "admin_token", + "memberHandle": "POSTMANe2e-testing", + "requestParameters": { + "fields": "userId,handle,handleLower,firstName,lastName,tracks,status" + }, + "requestBody": { + "email": "testing_new@topcoder.com" + }, + "httpStatus": 200, + "responseBody": { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name edit", + "lastName": "last name edit", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "status": "ACTIVE" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/member/update-member-successfully.json b/test/postman/testData/member/update-member-successfully.json new file mode 100644 index 0000000..e8e6ec5 --- /dev/null +++ b/test/postman/testData/member/update-member-successfully.json @@ -0,0 +1,108 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,handle,handleLower,firstName,lastName,tracks,status,addresses,description,email,homeCountryCode,competitionCountryCode,photoURL,maxRating,createdAt,createdBy,updatedBy" + }, + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 200, + "responseBody": { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "firstName": "first name edit", + "lastName": "last name edit", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "status": "ACTIVE", + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "description": "What goes around comes around", + "email": "denis@topcoder.com", + "homeCountryCode": "IRL", + "competitionCountryCode": "IRL", + "photoURL": "http://test.com/abc.png", + "maxRating": { + "rating": 1000, + "track": "dev", + "subTrack": "code" + }, + "createdAt": "2020-02-06T07:38:50.088Z", + "createdBy": "test1", + "updatedBy": "40029484" + } + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "fields": "userId,handle,handleLower,firstName,lastName,tracks,status" + }, + "requestBody": { + "firstName": "first name edit", + "lastName": "last name edit", + "description": "What goes around comes around", + "otherLangName": "tr", + "status": "ACTIVE", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "addresses": [ + { + "zip": "560110", + "streetAddr1": "GM INFINITE ECITY TOWN", + "city": "Bangalore", + "stateCode": "Karnataka", + "type": "HOME" + } + ], + "competitionCountryCode": "IRL", + "homeCountryCode": "IRL" + }, + "httpStatus": 200, + "responseBody": { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "firstName": "first name edit", + "lastName": "last name edit", + "tracks": [ + "DESIGN", + "DEVELOP" + ], + "status": "ACTIVE" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/member/upload-photo-by-invalid-field.json b/test/postman/testData/member/upload-photo-by-invalid-field.json new file mode 100644 index 0000000..84c03d1 --- /dev/null +++ b/test/postman/testData/member/upload-photo-by-invalid-field.json @@ -0,0 +1,8 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 400, + "message": "\"files\" is required" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/upload-photo-by-invalid-token.json b/test/postman/testData/member/upload-photo-by-invalid-token.json new file mode 100644 index 0000000..6a1ed1b --- /dev/null +++ b/test/postman/testData/member/upload-photo-by-invalid-token.json @@ -0,0 +1,26 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/upload-photo-by-nonexistent-memberHandle.json b/test/postman/testData/member/upload-photo-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..06ce5c7 --- /dev/null +++ b/test/postman/testData/member/upload-photo-by-nonexistent-memberHandle.json @@ -0,0 +1,8 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/upload-photo-by-unauthorized-user.json b/test/postman/testData/member/upload-photo-by-unauthorized-user.json new file mode 100644 index 0000000..dce897a --- /dev/null +++ b/test/postman/testData/member/upload-photo-by-unauthorized-user.json @@ -0,0 +1,20 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to perform this action!" + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to upload photo for the member." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to upload photo for the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/upload-photo-successfully.json b/test/postman/testData/member/upload-photo-successfully.json new file mode 100644 index 0000000..d7bdd83 --- /dev/null +++ b/test/postman/testData/member/upload-photo-successfully.json @@ -0,0 +1,14 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 200, + "photoURL": "https://topcoder-dev-media.s3.us-east-1.amazonaws.com/member/profile/POSTMANE2E-denis-[\\d]+.png" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "httpStatus": 200, + "photoURL": "https://topcoder-dev-media.s3.us-east-1.amazonaws.com/member/profile/POSTMANE2E-testing-[\\d]+.png" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/verify-email-by-invalid-token.json b/test/postman/testData/member/verify-email-by-invalid-token.json new file mode 100644 index 0000000..24d59f2 --- /dev/null +++ b/test/postman/testData/member/verify-email-by-invalid-token.json @@ -0,0 +1,38 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/verify-email-by-nonexistent-memberHandle.json b/test/postman/testData/member/verify-email-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..34222fb --- /dev/null +++ b/test/postman/testData/member/verify-email-by-nonexistent-memberHandle.json @@ -0,0 +1,11 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestParameters": { + "token": "token" + }, + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/member/verify-email-by-unauthorized-user.json b/test/postman/testData/member/verify-email-by-unauthorized-user.json new file mode 100644 index 0000000..dd331c1 --- /dev/null +++ b/test/postman/testData/member/verify-email-by-unauthorized-user.json @@ -0,0 +1,29 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 403, + "message": "You are not allowed to perform this action!" + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 403, + "message": "You are not allowed to update the member." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 403, + "message": "You are not allowed to update the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/verify-email-by-wrong-verification-token.json b/test/postman/testData/member/verify-email-by-wrong-verification-token.json new file mode 100644 index 0000000..4de64b2 --- /dev/null +++ b/test/postman/testData/member/verify-email-by-wrong-verification-token.json @@ -0,0 +1,20 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "token" + }, + "httpStatus": 400, + "message": "Wrong verification token." + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "token": "token" + }, + "httpStatus": 400, + "message": "Wrong verification token." + } +] \ No newline at end of file diff --git a/test/postman/testData/member/verify-new-email-successfully.json b/test/postman/testData/member/verify-new-email-successfully.json new file mode 100644 index 0000000..b1f7a81 --- /dev/null +++ b/test/postman/testData/member/verify-new-email-successfully.json @@ -0,0 +1,26 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "{{newEmailVerifyToken-1}}" + }, + "httpStatus": 200, + "responseBody": { + "emailChangeCompleted": true, + "verifiedEmail": "denis_new@topcoder.com" + } + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "token": "{{newEmailVerifyToken-2}}" + }, + "httpStatus": 200, + "responseBody": { + "emailChangeCompleted": true, + "verifiedEmail": "testing_new@topcoder.com" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/member/verify-old-email-successfully.json b/test/postman/testData/member/verify-old-email-successfully.json new file mode 100644 index 0000000..6da7f40 --- /dev/null +++ b/test/postman/testData/member/verify-old-email-successfully.json @@ -0,0 +1,26 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "token": "{{emailVerifyToken-1}}" + }, + "httpStatus": 200, + "responseBody": { + "emailChangeCompleted": false, + "verifiedEmail": "denis@topcoder.com" + } + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "token": "{{emailVerifyToken-2}}" + }, + "httpStatus": 200, + "responseBody": { + "emailChangeCompleted": false, + "verifiedEmail": "testing@topcoder.com" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/misc/get-financial-successfully.json b/test/postman/testData/misc/get-financial-successfully.json new file mode 100644 index 0000000..10d7d6c --- /dev/null +++ b/test/postman/testData/misc/get-financial-successfully.json @@ -0,0 +1,9 @@ +[ + { + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 200, + "responseBody": { + "message": "No Data" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-distribution-by-invalid-field.json b/test/postman/testData/statistics/get-distribution-by-invalid-field.json new file mode 100644 index 0000000..15a581d --- /dev/null +++ b/test/postman/testData/statistics/get-distribution-by-invalid-field.json @@ -0,0 +1,11 @@ +[ + { + "requestParameters": { + "track": "POSTMANE2E-develop", + "subTrack": "f2f", + "fields": "track,subTrack,distribution,name" + }, + "httpStatus": 400, + "message": "Invalid value: name" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-distribution-by-nonexistent-track.json b/test/postman/testData/statistics/get-distribution-by-nonexistent-track.json new file mode 100644 index 0000000..0a9ea16 --- /dev/null +++ b/test/postman/testData/statistics/get-distribution-by-nonexistent-track.json @@ -0,0 +1,11 @@ +[ + { + "requestParameters": { + "track": "POSTMANE2E-develop", + "subTrack": "task", + "fields": "track,subTrack,distribution" + }, + "httpStatus": 404, + "message": "No member distribution statistics is found." + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-distribution-successfully.json b/test/postman/testData/statistics/get-distribution-successfully.json new file mode 100644 index 0000000..2a092f1 --- /dev/null +++ b/test/postman/testData/statistics/get-distribution-successfully.json @@ -0,0 +1,56 @@ +[ + { + "requestParameters": {}, + "httpStatus": 200, + "responseBody": { + "distribution": { + "ratingRange0To099": 11, + "ratingRange100To199": 14 + } + } + }, + { + "requestParameters": { + "fields": "track,subTrack,distribution" + }, + "httpStatus": 200, + "responseBody": { + "distribution": { + "ratingRange0To099": 11, + "ratingRange100To199": 14 + } + } + }, + { + "requestParameters": { + "track": "POSTMANE2E-develop", + "subTrack": "code", + "fields": "track,subTrack,distribution" + }, + "httpStatus": 200, + "responseBody": { + "track": "POSTMANE2E-DEVELOP", + "subTrack": "CODE", + "distribution": { + "ratingRange0To099": 3, + "ratingRange100To199": 5 + } + } + }, + { + "requestParameters": { + "track": "POSTMANE2E-develop", + "subTrack": "f2f", + "fields": "track,subTrack,distribution" + }, + "httpStatus": 200, + "responseBody": { + "track": "POSTMANE2E-DEVELOP", + "subTrack": "F2F", + "distribution": { + "ratingRange0To099": 8, + "ratingRange100To199": 9 + } + } + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-history-stats-by-invalid field.json b/test/postman/testData/statistics/get-member-history-stats-by-invalid field.json new file mode 100644 index 0000000..d57fbfa --- /dev/null +++ b/test/postman/testData/statistics/get-member-history-stats-by-invalid field.json @@ -0,0 +1,26 @@ +[ + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,," + }, + "httpStatus": 400, + "message": "Empty value." + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,address" + }, + "httpStatus": 400, + "message": "Invalid value: address" + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,userId" + }, + "httpStatus": 400, + "message": "Duplicate values: userId" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-history-stats-by-nonexistent-memberHandle.json b/test/postman/testData/statistics/get-member-history-stats-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..01bc719 --- /dev/null +++ b/test/postman/testData/statistics/get-member-history-stats-by-nonexistent-memberHandle.json @@ -0,0 +1,7 @@ +[ + { + "memberHandle": "POSTMANE2E-nothere", + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-history-stats-successfully.json b/test/postman/testData/statistics/get-member-history-stats-successfully.json new file mode 100644 index 0000000..117a6cb --- /dev/null +++ b/test/postman/testData/statistics/get-member-history-stats-successfully.json @@ -0,0 +1,172 @@ +[ + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": {}, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 10, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "DEVELOP": { + "subTracks": [ + { + "id": 1111, + "name": "name", + "history": [ + { + "challengeId": 789789, + "challengeName": "test", + "ratingDate": "2020-02-15T14:04:22.544Z", + "newRating": 1888 + } + ] + } + ] + }, + "DATA_SCIENCE": { + "SRM": { + "history": [ + { + "challengeId": 754545, + "challengeName": "test2", + "date": "2020-02-15T14:04:22.544Z", + "rating": 1565, + "placement": 1, + "percentile": 100 + } + ] + }, + "MARATHON_MATCH": { + "history": [ + { + "challengeId": 121212, + "challengeName": "test3", + "date": "2020-02-15T14:04:22.544Z", + "rating": 1232, + "placement": 2, + "percentile": 80 + } + ] + } + }, + "createdAt": "2020-02-09T07:38:50.088Z", + "updatedAt": "2020-02-08T07:38:50.088Z" + } + ] + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "groupIds": 10, + "fields": "userId,groupId,handle,DEVELOP" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 10, + "handle": "POSTMANE2E-denis", + "DEVELOP": { + "subTracks": [ + { + "id": 1111, + "name": "name", + "history": [ + { + "challengeId": 789789, + "challengeName": "test", + "ratingDate": "2020-02-15T14:04:22.544Z", + "newRating": 1888 + } + ] + } + ] + } + } + ] + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "groupIds": 10, + "fields": "userId,groupId,handle,DATA_SCIENCE" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 10, + "handle": "POSTMANE2E-denis", + "DATA_SCIENCE": { + "SRM": { + "history": [ + { + "challengeId": 754545, + "challengeName": "test2", + "date": "2020-02-15T14:04:22.544Z", + "rating": 1565, + "placement": 1, + "percentile": 100 + } + ] + }, + "MARATHON_MATCH": { + "history": [ + { + "challengeId": 121212, + "challengeName": "test3", + "date": "2020-02-15T14:04:22.544Z", + "rating": 1232, + "placement": 2, + "percentile": 80 + } + ] + } + } + } + ] + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "groupIds": 20, + "fields": "userId,groupId,handle,DATA_SCIENCE" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 20, + "handle": "POSTMANE2E-denis", + "DATA_SCIENCE": { + "SRM": { + "history": [ + { + "challengeId": 754545, + "challengeName": "test2", + "date": "2020-02-15T14:04:22.544Z", + "rating": 1565, + "placement": 1, + "percentile": 100 + } + ] + }, + "MARATHON_MATCH": { + "history": [ + { + "challengeId": 121212, + "challengeName": "test3", + "date": "2020-02-15T14:04:22.544Z", + "rating": 1232, + "placement": 2, + "percentile": 80 + } + ] + } + } + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-skills-by-invalid field.json b/test/postman/testData/statistics/get-member-skills-by-invalid field.json new file mode 100644 index 0000000..d57fbfa --- /dev/null +++ b/test/postman/testData/statistics/get-member-skills-by-invalid field.json @@ -0,0 +1,26 @@ +[ + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,," + }, + "httpStatus": 400, + "message": "Empty value." + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,address" + }, + "httpStatus": 400, + "message": "Invalid value: address" + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,userId" + }, + "httpStatus": 400, + "message": "Duplicate values: userId" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-skills-by-nonexistent-memberHandle.json b/test/postman/testData/statistics/get-member-skills-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..01bc719 --- /dev/null +++ b/test/postman/testData/statistics/get-member-skills-by-nonexistent-memberHandle.json @@ -0,0 +1,7 @@ +[ + { + "memberHandle": "POSTMANE2E-nothere", + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-skills-successfully.json b/test/postman/testData/statistics/get-member-skills-successfully.json new file mode 100644 index 0000000..e8bddf9 --- /dev/null +++ b/test/postman/testData/statistics/get-member-skills-successfully.json @@ -0,0 +1,46 @@ +[ + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,handle,handleLower,skills" + }, + "httpStatus": 200, + "responseBody": { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "skills": { + "286": { + "hidden": false, + "score": 1888, + "sources": [ + "source1", + "source2" + ], + "tagName": "Node.js" + }, + "311": { + "hidden": false, + "tagName": "Python", + "sources": [ + "USER_ENTERED" + ], + "score": 0 + } + } + } + }, + { + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "fields": "userId,handle,handleLower,skills" + }, + "httpStatus": 200, + "responseBody": { + "userId": 456, + "handle": "POSTMANE2E-testing", + "handleLower": "postmane2e-testing", + "skills": {} + } + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-stats-by-invalid field.json b/test/postman/testData/statistics/get-member-stats-by-invalid field.json new file mode 100644 index 0000000..d57fbfa --- /dev/null +++ b/test/postman/testData/statistics/get-member-stats-by-invalid field.json @@ -0,0 +1,26 @@ +[ + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,," + }, + "httpStatus": 400, + "message": "Empty value." + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,address" + }, + "httpStatus": 400, + "message": "Invalid value: address" + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "fields": "userId,userId" + }, + "httpStatus": 400, + "message": "Duplicate values: userId" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-stats-by-nonexistent-memberHandle.json b/test/postman/testData/statistics/get-member-stats-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..01bc719 --- /dev/null +++ b/test/postman/testData/statistics/get-member-stats-by-nonexistent-memberHandle.json @@ -0,0 +1,7 @@ +[ + { + "memberHandle": "POSTMANE2E-nothere", + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/get-member-stats-successfully.json b/test/postman/testData/statistics/get-member-stats-successfully.json new file mode 100644 index 0000000..b3cf0a2 --- /dev/null +++ b/test/postman/testData/statistics/get-member-stats-successfully.json @@ -0,0 +1,439 @@ +[ + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": {}, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 10, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "challenges": 10, + "wins": 8, + "DEVELOP": { + "challenges": 3, + "wins": 2, + "subTracks": [ + { + "id": 11111, + "name": "test1", + "challenges": 20, + "wins": 3, + "rank": { + "rating": 1212, + "activePercentile": 80, + "activeRank": 1, + "activeCountryRank": 2, + "activeSchoolRank": 1, + "overallPercentile": 10, + "overallRank": 2, + "overallCountryRank": 1, + "overallSchoolRank": 1, + "volatility": 60, + "reliability": 80, + "maxRating": 1999, + "minRating": 1200 + }, + "submissions": { + "numInquiries": 1, + "submissions": 2, + "submissionRate": 3, + "passedScreening": 1, + "screeningSuccessRate": 2, + "passedReview": 3, + "reviewSuccessRate": 1, + "appeals": 2, + "appealSuccessRate": 3, + "maxScore": 1, + "minScore": 2, + "avgScore": 3, + "avgPlacement": 1, + "winPercent": 2 + }, + "mostRecentEventDate": "2020-02-15T14:05:16.275Z", + "mostRecentSubmission": "2020-02-15T14:05:16.275Z" + } + ], + "mostRecentEventDate": "2020-02-15T14:05:16.275Z", + "mostRecentSubmission": "2020-02-15T14:05:16.275Z" + }, + "DESIGN": { + "challenges": 1, + "wins": 2, + "subTracks": [ + { + "id": 1, + "name": "test", + "numInquiries": 1, + "challenges": 2, + "wins": 3, + "winPercent": 1, + "avgPlacement": 2, + "submissions": 3, + "submissionRate": 1, + "passedScreening": 2, + "screeningSuccessRate": 3, + "mostRecentEventDate": "2020-02-15T14:05:16.275Z", + "mostRecentSubmission": "2020-02-15T14:05:16.275Z" + } + ], + "mostRecentEventDate": "2020-02-15T14:05:16.275Z", + "mostRecentSubmission": "2020-02-15T14:05:16.275Z" + }, + "DATA_SCIENCE": { + "challenges": 10, + "wins": 0, + "srm": { + "challenges": 1, + "wins": 2, + "rank": { + "rating": 3, + "percentile": 0, + "rank": 1, + "countryRank": 2, + "schoolRank": 1, + "volatility": 20, + "maximumRating": 10, + "minimumRating": 20, + "defaultLanguage": "EN", + "competitions": 1, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z" + }, + "challengeDetails": [ + { + "levelName": "test", + "challenges": 10, + "failedChallenges": 20 + } + ], + "division1": [ + { + "levelName": "level 1", + "problemsSubmitted": 1, + "problemsFailed": 2, + "problemsSysByTest": 0 + } + ], + "division2": [ + { + "levelName": "level 2", + "problemsSubmitted": 1, + "problemsFailed": 2, + "problemsSysByTest": 0 + } + ], + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "marathonMatch": { + "challenges": 1, + "wins": 2, + "rank": { + "rating": 1, + "competitions": 2, + "avgRank": 1, + "avgNumSubmissions": 0, + "bestRank": 0, + "topFiveFinishes": 0, + "topTenFinishes": 0, + "rank": 10, + "percentile": 20, + "volatility": 10, + "minimumRating": 20, + "maximumRating": 10, + "countryRank": 20, + "schoolRank": 10, + "defaultLanguage": "test", + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z" + }, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "createdAt": "2020-02-09T07:38:50.088Z", + "updatedAt": "2020-02-08T07:38:50.088Z" + } + ] + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "groupIds": 20, + "fields": "userId,groupId,handle,maxRating,DEVELOP,wins" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 20, + "handle": "POSTMANE2E-denis", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "DEVELOP": { + "challenges": 3, + "wins": 2, + "subTracks": [ + { + "id": 11111, + "name": "test1", + "challenges": 20, + "wins": 3, + "rank": { + "rating": 1212, + "activePercentile": 80, + "activeRank": 1, + "activeCountryRank": 2, + "activeSchoolRank": 1, + "overallPercentile": 10, + "overallRank": 2, + "overallCountryRank": 1, + "overallSchoolRank": 1, + "volatility": 60, + "reliability": 80, + "maxRating": 1999, + "minRating": 1200 + }, + "submissions": { + "numInquiries": 1, + "submissions": 2, + "submissionRate": 3, + "passedScreening": 1, + "screeningSuccessRate": 2, + "passedReview": 3, + "reviewSuccessRate": 1, + "appeals": 2, + "appealSuccessRate": 3, + "maxScore": 1, + "minScore": 2, + "avgScore": 3, + "avgPlacement": 1, + "winPercent": 2 + }, + "mostRecentEventDate": "2020-02-15T14:05:16.275Z", + "mostRecentSubmission": "2020-02-15T14:05:16.275Z" + } + ], + "mostRecentEventDate": "2020-02-15T14:05:16.275Z", + "mostRecentSubmission": "2020-02-15T14:05:16.275Z" + }, + "wins": 8 + } + ] + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "groupIds": 10, + "fields": "userId,groupId,handle,maxRating,DATA_SCIENCE,wins" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 10, + "handle": "POSTMANE2E-denis", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "DATA_SCIENCE": { + "challenges": 10, + "wins": 0, + "srm": { + "challenges": 1, + "wins": 2, + "rank": { + "rating": 3, + "percentile": 0, + "rank": 1, + "countryRank": 2, + "schoolRank": 1, + "volatility": 20, + "maximumRating": 10, + "minimumRating": 20, + "defaultLanguage": "EN", + "competitions": 1, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z" + }, + "challengeDetails": [ + { + "levelName": "test", + "challenges": 10, + "failedChallenges": 20 + } + ], + "division1": [ + { + "levelName": "level 1", + "problemsSubmitted": 1, + "problemsFailed": 2, + "problemsSysByTest": 0 + } + ], + "division2": [ + { + "levelName": "level 2", + "problemsSubmitted": 1, + "problemsFailed": 2, + "problemsSysByTest": 0 + } + ], + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "marathonMatch": { + "challenges": 1, + "wins": 2, + "rank": { + "rating": 1, + "competitions": 2, + "avgRank": 1, + "avgNumSubmissions": 0, + "bestRank": 0, + "topFiveFinishes": 0, + "topTenFinishes": 0, + "rank": 10, + "percentile": 20, + "volatility": 10, + "minimumRating": 20, + "maximumRating": 10, + "countryRank": 20, + "schoolRank": 10, + "defaultLanguage": "test", + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z" + }, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "wins": 8 + } + ] + }, + { + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "groupIds": 10, + "fields": "userId,groupId,handle,maxRating,DATA_SCIENCE,wins" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "groupId": 10, + "handle": "POSTMANE2E-denis", + "maxRating": { + "rating": 1565, + "track": "develop", + "subTrack": "code", + "ratingColor": "#FCD617" + }, + "DATA_SCIENCE": { + "challenges": 10, + "wins": 0, + "srm": { + "challenges": 1, + "wins": 2, + "rank": { + "rating": 3, + "percentile": 0, + "rank": 1, + "countryRank": 2, + "schoolRank": 1, + "volatility": 20, + "maximumRating": 10, + "minimumRating": 20, + "defaultLanguage": "EN", + "competitions": 1, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z" + }, + "challengeDetails": [ + { + "levelName": "test", + "challenges": 10, + "failedChallenges": 20 + } + ], + "division1": [ + { + "levelName": "level 1", + "problemsSubmitted": 1, + "problemsFailed": 2, + "problemsSysByTest": 0 + } + ], + "division2": [ + { + "levelName": "level 2", + "problemsSubmitted": 1, + "problemsFailed": 2, + "problemsSysByTest": 0 + } + ], + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "marathonMatch": { + "challenges": 1, + "wins": 2, + "rank": { + "rating": 1, + "competitions": 2, + "avgRank": 1, + "avgNumSubmissions": 0, + "bestRank": 0, + "topFiveFinishes": 0, + "topTenFinishes": 0, + "rank": 10, + "percentile": 20, + "volatility": 10, + "minimumRating": 20, + "maximumRating": 10, + "countryRank": 20, + "schoolRank": 10, + "defaultLanguage": "test", + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z" + }, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "mostRecentEventName": "test", + "mostRecentEventDate": "2020-02-15T14:05:16.276Z", + "mostRecentSubmission": "2020-02-15T14:05:16.276Z" + }, + "wins": 8 + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/update-member-skills-by-invalid-field.json b/test/postman/testData/statistics/update-member-skills-by-invalid-field.json new file mode 100644 index 0000000..b1c5e62 --- /dev/null +++ b/test/postman/testData/statistics/update-member-skills-by-invalid-field.json @@ -0,0 +1,29 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": {}, + "httpStatus": 400, + "message": "\"data\" must have at least 1 children" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": { + "tagName": "311" + }, + "httpStatus": 400, + "message": "\"tagName\" must be an object" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": { + "212": { + "max": 1455 + } + }, + "httpStatus": 400, + "message": "\"max\" is not allowed" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/update-member-skills-by-invalid-token.json b/test/postman/testData/statistics/update-member-skills-by-invalid-token.json new file mode 100644 index 0000000..6a1ed1b --- /dev/null +++ b/test/postman/testData/statistics/update-member-skills-by-invalid-token.json @@ -0,0 +1,26 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/update-member-skills-by-nonexistent-memberHandle.json b/test/postman/testData/statistics/update-member-skills-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..33ccab1 --- /dev/null +++ b/test/postman/testData/statistics/update-member-skills-by-nonexistent-memberHandle.json @@ -0,0 +1,13 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": { + "380": { + "score": 1455 + } + }, + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/update-member-skills-by-unauthorized-user.json b/test/postman/testData/statistics/update-member-skills-by-unauthorized-user.json new file mode 100644 index 0000000..f8c697e --- /dev/null +++ b/test/postman/testData/statistics/update-member-skills-by-unauthorized-user.json @@ -0,0 +1,35 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "212": { + "score": 1455 + } + }, + "httpStatus": 403, + "message": "You are not allowed to perform this action!" + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "212": { + "score": 1455 + } + }, + "httpStatus": 403, + "message": "You are not allowed to update the member skills." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "212": { + "score": 1455 + } + }, + "httpStatus": 403, + "message": "You are not allowed to update the member skills." + } +] \ No newline at end of file diff --git a/test/postman/testData/statistics/update-member-skills-successfully.json b/test/postman/testData/statistics/update-member-skills-successfully.json new file mode 100644 index 0000000..0aff9e2 --- /dev/null +++ b/test/postman/testData/statistics/update-member-skills-successfully.json @@ -0,0 +1,98 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "380": { + "score": 1455 + } + }, + "httpStatus": 200, + "responseBody": { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "skills": { + "286": { + "hidden": false, + "score": 1888, + "sources": [ + "source1", + "source2" + ], + "tagName": "Node.js" + }, + "311": { + "hidden": false, + "tagName": "Python", + "sources": [ + "USER_ENTERED" + ], + "score": 0 + }, + "380": { + "score": 1455, + "tagName": "VB.NET", + "hidden": false, + "sources": [ + "USER_ENTERED" + ] + } + }, + "createdBy": "test1", + "updatedBy": "jcori" + } + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": { + "212": { + "score": 1455 + } + }, + "httpStatus": 200, + "responseBody": { + "userId": 123, + "handle": "POSTMANE2E-denis", + "handleLower": "postmane2e-denis", + "skills": { + "212": { + "score": 1455, + "tagName": "HTML", + "hidden": false, + "sources": [ + "USER_ENTERED" + ] + }, + "286": { + "hidden": false, + "score": 1888, + "sources": [ + "source1", + "source2" + ], + "tagName": "Node.js" + }, + "311": { + "hidden": false, + "tagName": "Python", + "sources": [ + "USER_ENTERED" + ], + "score": 0 + }, + "380": { + "score": 1455, + "tagName": "VB.NET", + "hidden": false, + "sources": [ + "USER_ENTERED" + ] + } + }, + "createdBy": "test1", + "updatedBy": "jcori" + } + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/create-trait-by-existent-trait.json b/test/postman/testData/trait/create-trait-by-existent-trait.json new file mode 100644 index 0000000..cb312af --- /dev/null +++ b/test/postman/testData/trait/create-trait-by-existent-trait.json @@ -0,0 +1,44 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "categoryName": "Software", + "traitId": "software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ], + "httpStatus": 400, + "message": "The trait id software already exists for the member." + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "device", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 400, + "message": "The trait id device already exists for the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/create-trait-by-invalid-field.json b/test/postman/testData/trait/create-trait-by-invalid-field.json new file mode 100644 index 0000000..e04ed6e --- /dev/null +++ b/test/postman/testData/trait/create-trait-by-invalid-field.json @@ -0,0 +1,66 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "categoryName": "Software", + "traitId": "big-software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ], + "httpStatus": 400, + "message": "\"traitId\" must be one of \\[basic_info, education, work, communities, languages, hobby, organization, device, software, service_provider, subscription, personalization, connect_info\\]" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "device", + "traits": { + "traitId": "devices", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 400, + "message": "\"traitId\" must be one of \\[basic_info, education, work, communities, languages, hobby, organization, device, software, service_provider, subscription, personalization, connect_info\\]" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "device", + "name": "android", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 400, + "message": "\"name\" is not allowed" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/create-trait-by-invalid-token.json b/test/postman/testData/trait/create-trait-by-invalid-token.json new file mode 100644 index 0000000..6a1ed1b --- /dev/null +++ b/test/postman/testData/trait/create-trait-by-invalid-token.json @@ -0,0 +1,26 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/create-trait-by-nonexistent-memberHandle.json b/test/postman/testData/trait/create-trait-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..a8a599a --- /dev/null +++ b/test/postman/testData/trait/create-trait-by-nonexistent-memberHandle.json @@ -0,0 +1,13 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/create-trait-by-unauthorized-user.json b/test/postman/testData/trait/create-trait-by-unauthorized-user.json new file mode 100644 index 0000000..59483ad --- /dev/null +++ b/test/postman/testData/trait/create-trait-by-unauthorized-user.json @@ -0,0 +1,35 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 403, + "message": "You are not allowed to perform this action!" + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 403, + "message": "You are not allowed to create traits of the member." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 403, + "message": "You are not allowed to create traits of the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/create-trait-successfully.json b/test/postman/testData/trait/create-trait-successfully.json new file mode 100644 index 0000000..9a6f956 --- /dev/null +++ b/test/postman/testData/trait/create-trait-successfully.json @@ -0,0 +1,74 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "categoryName": "Software", + "traitId": "software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ], + "httpStatus": 200, + "responseBody": [ + { + "categoryName": "Software", + "traitId": "software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + }, + "createdBy": 40029484 + } + ] + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "device", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 200, + "responseBody": [ + { + "categoryName": "Device", + "traitId": "device", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + }, + "createdBy": 40029484 + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/delete-trait-by-invalid-field.json b/test/postman/testData/trait/delete-trait-by-invalid-field.json new file mode 100644 index 0000000..c642c8a --- /dev/null +++ b/test/postman/testData/trait/delete-trait-by-invalid-field.json @@ -0,0 +1,29 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "work,," + }, + "httpStatus": 400, + "message": "Empty value." + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "education,work,home" + }, + "httpStatus": 400, + "message": "Invalid value: home" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "education,work,education" + }, + "httpStatus": 400, + "message": "Duplicate values: education" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/delete-trait-by-invalid-token.json b/test/postman/testData/trait/delete-trait-by-invalid-token.json new file mode 100644 index 0000000..6a1ed1b --- /dev/null +++ b/test/postman/testData/trait/delete-trait-by-invalid-token.json @@ -0,0 +1,26 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/delete-trait-by-nonexistent-memberHandle.json b/test/postman/testData/trait/delete-trait-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..06ce5c7 --- /dev/null +++ b/test/postman/testData/trait/delete-trait-by-nonexistent-memberHandle.json @@ -0,0 +1,8 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/delete-trait-by-nonexistent-trait.json b/test/postman/testData/trait/delete-trait-by-nonexistent-trait.json new file mode 100644 index 0000000..da59b81 --- /dev/null +++ b/test/postman/testData/trait/delete-trait-by-nonexistent-trait.json @@ -0,0 +1,20 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "education" + }, + "httpStatus": 404, + "message": "The trait id education is not found for the member." + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "traitIds": "work" + }, + "httpStatus": 404, + "message": "The trait id work is not found for the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/delete-trait-by-unauthorized-user.json b/test/postman/testData/trait/delete-trait-by-unauthorized-user.json new file mode 100644 index 0000000..864801e --- /dev/null +++ b/test/postman/testData/trait/delete-trait-by-unauthorized-user.json @@ -0,0 +1,20 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to perform this action!" + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to remove traits of the member." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to remove traits of the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/delete-trait-successfully.json b/test/postman/testData/trait/delete-trait-successfully.json new file mode 100644 index 0000000..b5322fb --- /dev/null +++ b/test/postman/testData/trait/delete-trait-successfully.json @@ -0,0 +1,18 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "software" + }, + "httpStatus": 200 + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "traitIds": "device" + }, + "httpStatus": 200 + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/get-traits-by-invalid-field.json b/test/postman/testData/trait/get-traits-by-invalid-field.json new file mode 100644 index 0000000..bcc5ce2 --- /dev/null +++ b/test/postman/testData/trait/get-traits-by-invalid-field.json @@ -0,0 +1,22 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "basic_info,software,machine", + "fields": "userId,traitId,categoryName,traits" + }, + "httpStatus": 400, + "message": "Invalid value: machine" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "basic_info,software", + "fields": "userId,traitId,categoryName,traits,machine" + }, + "httpStatus": 400, + "message": "Invalid value: machine" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/get-traits-by-invalid-token.json b/test/postman/testData/trait/get-traits-by-invalid-token.json new file mode 100644 index 0000000..6a1ed1b --- /dev/null +++ b/test/postman/testData/trait/get-traits-by-invalid-token.json @@ -0,0 +1,26 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/get-traits-by-nonexistent-memberHandle.json b/test/postman/testData/trait/get-traits-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..06ce5c7 --- /dev/null +++ b/test/postman/testData/trait/get-traits-by-nonexistent-memberHandle.json @@ -0,0 +1,8 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/get-traits-by-unauthorized-user.json b/test/postman/testData/trait/get-traits-by-unauthorized-user.json new file mode 100644 index 0000000..0946e91 --- /dev/null +++ b/test/postman/testData/trait/get-traits-by-unauthorized-user.json @@ -0,0 +1,14 @@ +[ + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to view traits of the member." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 403, + "message": "You are not allowed to view traits of the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/get-traits-successfully.json b/test/postman/testData/trait/get-traits-successfully.json new file mode 100644 index 0000000..6653b0e --- /dev/null +++ b/test/postman/testData/trait/get-traits-successfully.json @@ -0,0 +1,124 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "basic_info,software", + "fields": "userId,traitId,categoryName,traits" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "traitId": "basic_info", + "categoryName": "Subscription", + "traits": { + "data": [ + { + "test": "abc" + } + ] + } + }, + { + "userId": 123, + "traitId": "software", + "categoryName": "Software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ] + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "fields": "userId,traitId,categoryName,traits" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 456, + "traitId": "device", + "categoryName": "Device", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ] + }, + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestParameters": { + "traitIds": "basic_info,software", + "fields": "userId,traitId,categoryName,traits" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 123, + "traitId": "basic_info", + "categoryName": "Subscription", + "traits": { + "data": [ + { + "test": "abc" + } + ] + } + }, + { + "userId": 123, + "traitId": "software", + "categoryName": "Software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ] + }, + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-testing", + "requestParameters": { + "fields": "userId,traitId,categoryName,traits" + }, + "httpStatus": 200, + "responseBody": [ + { + "userId": 456, + "traitId": "device", + "categoryName": "Device", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/update-trait-by-invalid-field.json b/test/postman/testData/trait/update-trait-by-invalid-field.json new file mode 100644 index 0000000..e04ed6e --- /dev/null +++ b/test/postman/testData/trait/update-trait-by-invalid-field.json @@ -0,0 +1,66 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "categoryName": "Software", + "traitId": "big-software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ], + "httpStatus": 400, + "message": "\"traitId\" must be one of \\[basic_info, education, work, communities, languages, hobby, organization, device, software, service_provider, subscription, personalization, connect_info\\]" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "device", + "traits": { + "traitId": "devices", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 400, + "message": "\"traitId\" must be one of \\[basic_info, education, work, communities, languages, hobby, organization, device, software, service_provider, subscription, personalization, connect_info\\]" + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "device", + "name": "android", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 400, + "message": "\"name\" is not allowed" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/update-trait-by-invalid-token.json b/test/postman/testData/trait/update-trait-by-invalid-token.json new file mode 100644 index 0000000..6a1ed1b --- /dev/null +++ b/test/postman/testData/trait/update-trait-by-invalid-token.json @@ -0,0 +1,26 @@ +[ + { + "token": "invalid_token_1", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Invalid Token." + }, + { + "token": "invalid_token_2", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_3", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "No token provided." + }, + { + "token": "invalid_token_4", + "memberHandle": "POSTMANE2E-denis", + "httpStatus": 401, + "message": "Failed to authenticate token." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/update-trait-by-nonexistent-memberHandle.json b/test/postman/testData/trait/update-trait-by-nonexistent-memberHandle.json new file mode 100644 index 0000000..a8a599a --- /dev/null +++ b/test/postman/testData/trait/update-trait-by-nonexistent-memberHandle.json @@ -0,0 +1,13 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-nothere", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 404, + "message": "Member with handle: \"POSTMANE2E-nothere\" doesn't exist" + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/update-trait-by-nonexistent-trait.json b/test/postman/testData/trait/update-trait-by-nonexistent-trait.json new file mode 100644 index 0000000..34ceebc --- /dev/null +++ b/test/postman/testData/trait/update-trait-by-nonexistent-trait.json @@ -0,0 +1,44 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "categoryName": "Software", + "traitId": "education", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ], + "httpStatus": 404, + "message": "The trait id education is not found for the member." + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "work", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 404, + "message": "The trait id work is not found for the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/update-trait-by-unauthorized-user.json b/test/postman/testData/trait/update-trait-by-unauthorized-user.json new file mode 100644 index 0000000..3e6057e --- /dev/null +++ b/test/postman/testData/trait/update-trait-by-unauthorized-user.json @@ -0,0 +1,35 @@ +[ + { + "token": "m2m_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 403, + "message": "You are not allowed to perform this action!" + }, + { + "token": "copilot_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 403, + "message": "You are not allowed to update traits of the member." + }, + { + "token": "user_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "traitId": "software" + } + ], + "httpStatus": 403, + "message": "You are not allowed to update traits of the member." + } +] \ No newline at end of file diff --git a/test/postman/testData/trait/update-trait-successfully.json b/test/postman/testData/trait/update-trait-successfully.json new file mode 100644 index 0000000..fc4b494 --- /dev/null +++ b/test/postman/testData/trait/update-trait-successfully.json @@ -0,0 +1,76 @@ +[ + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-denis", + "requestBody": [ + { + "categoryName": "Software", + "traitId": "software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + } + } + ], + "httpStatus": 200, + "responseBody": [ + { + "categoryName": "Software", + "traitId": "software", + "traits": { + "traitId": "software", + "data": [ + { + "softwareType": "Developer Tools", + "name": "VSCode" + } + ] + }, + "createdBy": 40029484, + "updatedBy": 40029484 + } + ] + }, + { + "token": "admin_token", + "memberHandle": "POSTMANE2E-testing", + "requestBody": [ + { + "categoryName": "Device", + "traitId": "device", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + } + } + ], + "httpStatus": 200, + "responseBody": [ + { + "categoryName": "Device", + "traitId": "device", + "traits": { + "traitId": "device", + "data": [ + { + "deviceType": "phone", + "name": "android" + } + ] + }, + "createdBy": 40029484, + "updatedBy": 40029484 + } + ] + } +] \ No newline at end of file diff --git a/test/postman/testHelper.js b/test/postman/testHelper.js new file mode 100644 index 0000000..68ecd30 --- /dev/null +++ b/test/postman/testHelper.js @@ -0,0 +1,130 @@ +/** + * This file defines methods for getting access tokens + */ + +const _ = require('lodash') +const axios = require('axios') +const config = require('config') +const m2mAuth = require('tc-core-library-js').auth.m2m +const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_PROXY_SERVER_URL'])) + +/** + * Get m2mToken + * @returns {String} the M2MToken + */ +async function getM2MToken () { + return m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) +} + +/** + * Get user tokens from V2 API + * @param {String} userName the user name + * @param {String} userPassword the user password + * @returns {Object} the user tokens + */ +async function getUserTokenV2 (userName, userPassword) { + const { data } = await axios({ + method: 'post', + url: config.AUTH_V2_URL, + data: { + username: userName, + password: userPassword, + client_id: config.AUTH_V2_CLIENT_ID, + sso: false, + scope: 'openid profile offline_access', + response_type: 'token', + connection: 'TC-User-Database', + grant_type: 'password', + device: 'Browser' + }, + headers: { + 'cache-control': 'no-cache', + 'content-type': 'application/json' + } + }) + return data +} + +/** + * Get user token from V3 API + * @param {String} idToken the id_token + * @param {String} refreshToken the refresh_token + * @returns {String} the user token + */ +async function getUserTokenV3 (idToken, refreshToken) { + const { data } = await axios({ + method: 'post', + url: config.AUTH_V3_URL, + data: { + param: { + externalToken: idToken, + refreshToken: refreshToken + } + }, + headers: { + 'cache-control': 'no-cache', + authorization: `Bearer ${this.v2_token}`, + 'content-type': 'application/json;charset=UTF-8' + } + }) + return data +} + +/** + * Get admin token from V3 API + * @returns {String} The admin token + */ +async function getAdminToken () { + const v2 = await getUserTokenV2(config.ADMIN_CREDENTIALS_USERNAME, config.ADMIN_CREDENTIALS_PASSWORD) + const v3 = await getUserTokenV3(v2.id_token, v2.refresh_token) + return _.get(v3, 'result.content.token') +} + +/** + * Get copilot token from V3 API + * @returns {String} The copilot token + */ +async function getCopilotToken () { + const v2 = await getUserTokenV2(config.COPILOT_CREDENTIALS_USERNAME, config.COPILOT_CREDENTIALS_PASSWORD) + const v3 = await getUserTokenV3(v2.id_token, v2.refresh_token) + return _.get(v3, 'result.content.token') +} + +/** + * Get regular user token from V3 API + * @returns {String} The user token + */ +async function getUserToken () { + const v2 = await getUserTokenV2(config.USER_CREDENTIALS_USERNAME, config.USER_CREDENTIALS_PASSWORD) + const v3 = await getUserTokenV3(v2.id_token, v2.refresh_token) + return _.get(v3, 'result.content.token') +} + +/** + * Uses superagent to proxy post request + * @param {String} url the url + * @param {Object} body the request body, optional + * @returns {Object} the response + */ +async function postRequest (url, data = {}) { + const adminToken = await getAdminToken() + const { data: response } = await axios({ + method: 'post', + url: url, + data, + headers: { + Accept: 'application/json', + Authorization: `Bearer ${adminToken}`, + 'Content-Type': 'application/json' + } + }) + return response +} + +module.exports = { + getM2MToken, + getAdminToken, + getCopilotToken, + getUserToken, + postRequest +}